or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

atomic-operations.mdconfiguration.mdcore-operations.mddirectory-layer.mdindex.mdkey-value-ops.mdsubspaces.mdtransactions.md

key-value-ops.mddocs/

0

# Key-Value Operations

1

2

Reading and writing key-value pairs with support for atomic operations, range queries, and watches for change notifications. FoundationDB stores data as ordered key-value pairs with binary keys and values.

3

4

## Capabilities

5

6

### Single Key Operations

7

8

Basic operations for reading and writing individual key-value pairs.

9

10

```c { .api }

11

/**

12

* Read a value from the database by key

13

* @param tr Transaction handle

14

* @param key_name Key to read

15

* @param key_name_length Length of key in bytes

16

* @param snapshot Whether to use snapshot read (non-conflicting)

17

* @return Future containing the value (or NULL if key doesn't exist)

18

*/

19

FDBFuture* fdb_transaction_get(FDBTransaction* tr, uint8_t const* key_name,

20

int key_name_length, fdb_bool_t snapshot);

21

22

/**

23

* Set a key-value pair in the database

24

* @param tr Transaction handle

25

* @param key_name Key to set

26

* @param key_name_length Length of key in bytes

27

* @param value Value to store

28

* @param value_length Length of value in bytes

29

*/

30

void fdb_transaction_set(FDBTransaction* tr, uint8_t const* key_name, int key_name_length,

31

uint8_t const* value, int value_length);

32

33

/**

34

* Remove a key from the database

35

* @param tr Transaction handle

36

* @param key_name Key to remove

37

* @param key_name_length Length of key in bytes

38

*/

39

void fdb_transaction_clear(FDBTransaction* tr, uint8_t const* key_name, int key_name_length);

40

```

41

42

```python { .api }

43

class Transaction:

44

def get(self, key: bytes, snapshot: bool = False) -> Future:

45

"""

46

Read a value from the database

47

48

Args:

49

key: Key to read

50

snapshot: Whether to use snapshot read (default False)

51

52

Returns:

53

Future that resolves to bytes value or None if key doesn't exist

54

"""

55

56

def set(self, key: bytes, value: bytes) -> None:

57

"""

58

Set a key-value pair

59

60

Args:

61

key: Key to set

62

value: Value to store

63

"""

64

65

def clear(self, key: bytes) -> None:

66

"""

67

Remove a key from the database

68

69

Args:

70

key: Key to remove

71

"""

72

73

def __getitem__(self, key: bytes) -> bytes:

74

"""Dictionary-style read (blocking)"""

75

76

def __setitem__(self, key: bytes, value: bytes) -> None:

77

"""Dictionary-style write"""

78

79

def __delitem__(self, key: bytes) -> None:

80

"""Dictionary-style delete"""

81

```

82

83

```java { .api }

84

// Java API

85

public interface ReadTransaction {

86

public CompletableFuture<byte[]> get(byte[] key):

87

/**

88

* Read a value from the database

89

* @param key Key to read

90

* @return CompletableFuture with value bytes (null if not found)

91

*/

92

}

93

94

public interface Transaction extends ReadTransaction {

95

public void set(byte[] key, byte[] value):

96

/**

97

* Set a key-value pair

98

* @param key Key to set

99

* @param value Value to store

100

*/

101

102

public void clear(byte[] key):

103

/**

104

* Remove a key from the database

105

* @param key Key to remove

106

*/

107

}

108

```

109

110

```go { .api }

111

// Go API

112

type ReadTransaction interface {

113

Get(key Key) FutureByteSlice:

114

/**

115

* Read a value from the database

116

* @param key Key to read

117

* @return Future with value bytes (nil if not found)

118

*/

119

120

Snapshot() ReadTransaction:

121

/**

122

* Get snapshot view for non-conflicting reads

123

* @return ReadTransaction for snapshot reads

124

*/

125

}

126

127

type Transaction interface {

128

ReadTransaction

129

130

Set(key Key, value []byte):

131

/**

132

* Set a key-value pair

133

* @param key Key to set

134

* @param value Value to store

135

*/

136

137

Clear(key Key):

138

/**

139

* Remove a key from the database

140

* @param key Key to remove

141

*/

142

}

143

```

144

145

### Range Operations

146

147

Operations for reading and clearing ranges of key-value pairs.

148

149

```c { .api }

150

/**

151

* Read a range of key-value pairs

152

* @param tr Transaction handle

153

* @param begin_key_name Start key

154

* @param begin_key_name_length Length of start key

155

* @param begin_or_equal Whether start is inclusive (1) or exclusive (0)

156

* @param begin_offset Offset from start key

157

* @param end_key_name End key

158

* @param end_key_name_length Length of end key

159

* @param end_or_equal Whether end is inclusive (1) or exclusive (0)

160

* @param end_offset Offset from end key

161

* @param limit Maximum number of results (0 for no limit)

162

* @param target_bytes Target bytes to read (0 for no target)

163

* @param mode Streaming mode for result delivery

164

* @param iteration Iteration number for continuation

165

* @param snapshot Whether to use snapshot read

166

* @param reverse Whether to read in reverse order

167

* @return Future containing array of key-value pairs

168

*/

169

FDBFuture* fdb_transaction_get_range(FDBTransaction* tr,

170

uint8_t const* begin_key_name, int begin_key_name_length,

171

fdb_bool_t begin_or_equal, int begin_offset,

172

uint8_t const* end_key_name, int end_key_name_length,

173

fdb_bool_t end_or_equal, int end_offset,

174

int limit, int target_bytes, FDBStreamingMode mode,

175

int iteration, fdb_bool_t snapshot, fdb_bool_t reverse);

176

177

/**

178

* Clear a range of keys

179

* @param tr Transaction handle

180

* @param begin_key_name Start of range (inclusive)

181

* @param begin_key_name_length Length of start key

182

* @param end_key_name End of range (exclusive)

183

* @param end_key_name_length Length of end key

184

*/

185

void fdb_transaction_clear_range(FDBTransaction* tr,

186

uint8_t const* begin_key_name, int begin_key_name_length,

187

uint8_t const* end_key_name, int end_key_name_length);

188

```

189

190

```python { .api }

191

class Transaction:

192

def get_range(self, begin: KeySelector, end: KeySelector, limit: int = 0,

193

reverse: bool = False, streaming_mode: int = StreamingMode.ITERATOR,

194

snapshot: bool = False) -> FDBRange:

195

"""

196

Read a range of key-value pairs

197

198

Args:

199

begin: Start key selector

200

end: End key selector

201

limit: Maximum number of results (0 for no limit)

202

reverse: Whether to read in reverse order

203

streaming_mode: How results are delivered

204

snapshot: Whether to use snapshot read

205

206

Returns:

207

FDBRange iterable of KeyValue pairs

208

"""

209

210

def get_range_startswith(self, prefix: bytes, **kwargs) -> FDBRange:

211

"""

212

Read all keys starting with a prefix

213

214

Args:

215

prefix: Key prefix to match

216

**kwargs: Additional arguments passed to get_range

217

218

Returns:

219

FDBRange iterable of KeyValue pairs

220

"""

221

222

def clear_range(self, begin: bytes, end: bytes) -> None:

223

"""

224

Clear a range of keys

225

226

Args:

227

begin: Start of range (inclusive)

228

end: End of range (exclusive)

229

"""

230

231

def clear_range_startswith(self, prefix: bytes) -> None:

232

"""

233

Clear all keys starting with a prefix

234

235

Args:

236

prefix: Key prefix to match

237

"""

238

239

class KeySelector:

240

@staticmethod

241

def first_greater_or_equal(key: bytes) -> 'KeySelector': ...

242

243

@staticmethod

244

def first_greater_than(key: bytes) -> 'KeySelector': ...

245

246

@staticmethod

247

def last_less_or_equal(key: bytes) -> 'KeySelector': ...

248

249

@staticmethod

250

def last_less_than(key: bytes) -> 'KeySelector': ...

251

252

class KeyValue:

253

key: bytes

254

value: bytes

255

256

class FDBRange:

257

"""Iterator over range query results"""

258

259

def __init__(self, tr: Transaction, begin: KeySelector, end: KeySelector,

260

limit: int, reverse: bool, streaming_mode: int): ...

261

262

def __iter__(self) -> Iterator[KeyValue]:

263

"""Iterate over KeyValue pairs in the range"""

264

265

def to_list(self) -> List[KeyValue]:

266

"""Convert range to list (forces evaluation)"""

267

```

268

269

```java { .api }

270

// Java API

271

public interface ReadTransaction {

272

public AsyncIterable<KeyValue> getRange(KeySelector begin, KeySelector end):

273

/**

274

* Read a range of key-value pairs

275

* @param begin Start key selector

276

* @param end End key selector

277

* @return AsyncIterable of KeyValue pairs

278

*/

279

280

public AsyncIterable<KeyValue> getRange(KeySelector begin, KeySelector end, int limit):

281

/**

282

* Read a range with limit

283

* @param begin Start key selector

284

* @param end End key selector

285

* @param limit Maximum number of results

286

* @return AsyncIterable of KeyValue pairs

287

*/

288

289

public AsyncIterable<KeyValue> getRange(Range range):

290

/**

291

* Read a range using Range object

292

* @param range Range specification

293

* @return AsyncIterable of KeyValue pairs

294

*/

295

}

296

297

public interface Transaction extends ReadTransaction {

298

public void clear(Range range):

299

/**

300

* Clear a range of keys

301

* @param range Range to clear

302

*/

303

}

304

305

public class KeySelector {

306

public static KeySelector firstGreaterOrEqual(byte[] key): ...

307

public static KeySelector firstGreaterThan(byte[] key): ...

308

public static KeySelector lastLessOrEqual(byte[] key): ...

309

public static KeySelector lastLessThan(byte[] key): ...

310

}

311

312

public class KeyValue {

313

public byte[] getKey(): ...

314

public byte[] getValue(): ...

315

}

316

317

public class Range {

318

public Range(byte[] begin, byte[] end): ...

319

public static Range startsWith(byte[] prefix): ...

320

}

321

```

322

323

```go { .api }

324

// Go API

325

type ReadTransaction interface {

326

GetRange(begin, end KeySelector, options RangeOptions) RangeResult:

327

/**

328

* Read a range of key-value pairs

329

* @param begin Start key selector

330

* @param end End key selector

331

* @param options Range query options

332

* @return RangeResult with key-value pairs

333

*/

334

}

335

336

type Transaction interface {

337

ReadTransaction

338

339

ClearRange(begin, end Key):

340

/**

341

* Clear a range of keys

342

* @param begin Start of range (inclusive)

343

* @param end End of range (exclusive)

344

*/

345

}

346

347

type KeySelector struct {

348

Key Key

349

OrEqual bool

350

Offset int

351

}

352

353

func FirstGreaterOrEqual(key Key) KeySelector: ...

354

func FirstGreaterThan(key Key) KeySelector: ...

355

func LastLessOrEqual(key Key) KeySelector: ...

356

func LastLessThan(key Key) KeySelector: ...

357

358

type KeyValue struct {

359

Key Key

360

Value []byte

361

}

362

363

type RangeOptions struct {

364

Limit int

365

Reverse bool

366

Mode StreamingMode

367

}

368

```

369

370

### Key Selection and Navigation

371

372

Advanced key selection for precise range boundaries and key navigation.

373

374

```c { .api }

375

/**

376

* Get a key based on a key selector

377

* @param tr Transaction handle

378

* @param key_name Base key for selector

379

* @param key_name_length Length of base key

380

* @param or_equal Whether selector includes the base key

381

* @param offset Offset from base key

382

* @param snapshot Whether to use snapshot read

383

* @return Future containing the selected key

384

*/

385

FDBFuture* fdb_transaction_get_key(FDBTransaction* tr, uint8_t const* key_name,

386

int key_name_length, fdb_bool_t or_equal,

387

int offset, fdb_bool_t snapshot);

388

389

// Key selector convenience macros

390

#define FDB_KEYSEL_LAST_LESS_THAN(k, l) k, l, 0, 0

391

#define FDB_KEYSEL_LAST_LESS_OR_EQUAL(k, l) k, l, 1, 0

392

#define FDB_KEYSEL_FIRST_GREATER_THAN(k, l) k, l, 1, 1

393

#define FDB_KEYSEL_FIRST_GREATER_OR_EQUAL(k, l) k, l, 0, 1

394

```

395

396

```python { .api }

397

class Transaction:

398

def get_key(self, selector: KeySelector, snapshot: bool = False) -> Future:

399

"""

400

Get a key based on a key selector

401

402

Args:

403

selector: Key selector specification

404

snapshot: Whether to use snapshot read

405

406

Returns:

407

Future containing the selected key bytes

408

"""

409

410

class KeySelector:

411

def __init__(self, key: bytes, or_equal: bool, offset: int): ...

412

413

def __add__(self, offset: int) -> 'KeySelector':

414

"""Add offset to selector"""

415

416

def __sub__(self, offset: int) -> 'KeySelector':

417

"""Subtract offset from selector"""

418

```

419

420

```java { .api }

421

// Java API

422

public interface ReadTransaction {

423

public CompletableFuture<byte[]> getKey(KeySelector selector):

424

/**

425

* Get a key based on a key selector

426

* @param selector Key selector specification

427

* @return CompletableFuture with selected key

428

*/

429

}

430

431

public class KeySelector {

432

public KeySelector add(int offset):

433

/**

434

* Create new selector with added offset

435

* @param offset Offset to add

436

* @return New KeySelector

437

*/

438

439

public KeySelector subtract(int offset):

440

/**

441

* Create new selector with subtracted offset

442

* @param offset Offset to subtract

443

* @return New KeySelector

444

*/

445

}

446

```

447

448

```go { .api }

449

// Go API

450

type ReadTransaction interface {

451

GetKey(selector KeySelector) FutureKey:

452

/**

453

* Get a key based on a key selector

454

* @param selector Key selector specification

455

* @return Future with selected key

456

*/

457

}

458

459

func (ks KeySelector) Add(offset int) KeySelector: ...

460

func (ks KeySelector) Sub(offset int) KeySelector: ...

461

```

462

463

### Watch Operations

464

465

Monitor keys for changes with efficient push notifications.

466

467

```c { .api }

468

/**

469

* Watch a key for changes

470

* @param tr Transaction handle

471

* @param key_name Key to watch

472

* @param key_name_length Length of key

473

* @return Future that completes when key changes

474

*/

475

FDBFuture* fdb_transaction_watch(FDBTransaction* tr, uint8_t const* key_name, int key_name_length);

476

```

477

478

```python { .api }

479

class Transaction:

480

def watch(self, key: bytes) -> Future:

481

"""

482

Watch a key for changes

483

484

Args:

485

key: Key to monitor for changes

486

487

Returns:

488

Future that completes when key changes from its current value

489

"""

490

```

491

492

```java { .api }

493

// Java API

494

public interface Transaction {

495

public CompletableFuture<Void> watch(byte[] key):

496

/**

497

* Watch a key for changes

498

* @param key Key to monitor

499

* @return CompletableFuture that completes when key changes

500

*/

501

}

502

```

503

504

```go { .api }

505

// Go API

506

type Transaction interface {

507

Watch(key Key) FutureNil:

508

/**

509

* Watch a key for changes

510

* @param key Key to monitor

511

* @return Future that completes when key changes

512

*/

513

}

514

```

515

516

### Size Estimation

517

518

Estimate the size of key ranges for performance planning.

519

520

```c { .api }

521

/**

522

* Get estimated size of a key range in bytes

523

* @param tr Transaction handle

524

* @param begin_key_name Start of range

525

* @param begin_key_name_length Length of start key

526

* @param end_key_name End of range

527

* @param end_key_name_length Length of end key

528

* @return Future containing estimated size in bytes

529

*/

530

FDBFuture* fdb_transaction_get_estimated_range_size_bytes(FDBTransaction* tr,

531

uint8_t const* begin_key_name, int begin_key_name_length,

532

uint8_t const* end_key_name, int end_key_name_length);

533

```

534

535

```python { .api }

536

class Transaction:

537

def get_estimated_range_size_bytes(self, begin: bytes, end: bytes) -> Future:

538

"""

539

Get estimated size of a key range

540

541

Args:

542

begin: Start of range (inclusive)

543

end: End of range (exclusive)

544

545

Returns:

546

Future containing estimated size in bytes

547

"""

548

```

549

550

```java { .api }

551

// Java API

552

public interface ReadTransaction {

553

public CompletableFuture<Long> getEstimatedRangeSizeBytes(byte[] begin, byte[] end):

554

/**

555

* Get estimated size of a key range

556

* @param begin Start of range

557

* @param end End of range

558

* @return CompletableFuture with estimated size in bytes

559

*/

560

}

561

```

562

563

```go { .api }

564

// Go API

565

type ReadTransaction interface {

566

GetEstimatedRangeSizeBytes(begin, end Key) FutureInt64:

567

/**

568

* Get estimated size of a key range

569

* @param begin Start of range

570

* @param end End of range

571

* @return Future with estimated size in bytes

572

*/

573

}

574

```

575

576

**Usage Examples:**

577

578

**Basic Key-Value Operations (Python):**

579

```python

580

import fdb

581

582

fdb.api_version(630)

583

db = fdb.open()

584

585

@fdb.transactional

586

def basic_operations(tr):

587

# Set some values

588

tr.set(b"user:123:name", b"Alice")

589

tr.set(b"user:123:email", b"alice@example.com")

590

tr.set(b"user:124:name", b"Bob")

591

tr.set(b"user:124:email", b"bob@example.com")

592

593

# Read a value

594

name = tr.get(b"user:123:name").wait()

595

print(f"Name: {name.decode()}")

596

597

# Delete a key

598

tr.clear(b"user:124:email")

599

600

return name

601

602

result = basic_operations(db)

603

```

604

605

**Range Queries (Java):**

606

```java

607

import com.apple.foundationdb.*;

608

import com.apple.foundationdb.tuple.Tuple;

609

610

FDB fdb = FDB.selectAPIVersion(630);

611

612

try (Database db = fdb.open()) {

613

db.run(tr -> {

614

// Set some data

615

tr.set(Tuple.from("user", 123, "name").pack(), "Alice".getBytes());

616

tr.set(Tuple.from("user", 123, "email").pack(), "alice@example.com".getBytes());

617

tr.set(Tuple.from("user", 124, "name").pack(), "Bob".getBytes());

618

tr.set(Tuple.from("user", 124, "email").pack(), "bob@example.com".getBytes());

619

620

// Range query for all user data

621

Range userRange = Range.startsWith(Tuple.from("user").pack());

622

623

for (KeyValue kv : tr.getRange(userRange)) {

624

Tuple key = Tuple.fromBytes(kv.getKey());

625

String value = new String(kv.getValue());

626

System.out.println(key + " = " + value);

627

}

628

629

return null;

630

});

631

}

632

```

633

634

**Key Selectors and Navigation (Go):**

635

```go

636

package main

637

638

import (

639

"fmt"

640

"github.com/apple/foundationdb/bindings/go/src/fdb"

641

"github.com/apple/foundationdb/bindings/go/src/fdb/tuple"

642

)

643

644

func main() {

645

fdb.MustAPIVersion(630)

646

db := fdb.MustOpenDefault()

647

648

db.Transact(func(tr fdb.Transaction) (interface{}, error) {

649

// Set some ordered data

650

for i := 0; i < 10; i++ {

651

key := tuple.Tuple{"item", i}.Pack()

652

value := fmt.Sprintf("value-%d", i)

653

tr.Set(fdb.Key(key), []byte(value))

654

}

655

656

// Find the first key >= "item", 5

657

targetKey := tuple.Tuple{"item", 5}.Pack()

658

selector := fdb.FirstGreaterOrEqual(fdb.Key(targetKey))

659

foundKey := tr.GetKey(selector).MustGet()

660

661

foundTuple, _ := tuple.Unpack(foundKey)

662

fmt.Printf("Found key: %v\n", foundTuple)

663

664

// Get range from item 3 to item 7

665

beginKey := tuple.Tuple{"item", 3}.Pack()

666

endKey := tuple.Tuple{"item", 8}.Pack() // End is exclusive

667

668

beginSel := fdb.FirstGreaterOrEqual(fdb.Key(beginKey))

669

endSel := fdb.FirstGreaterOrEqual(fdb.Key(endKey))

670

671

rangeResult := tr.GetRange(beginSel, endSel, fdb.RangeOptions{})

672

kvs := rangeResult.GetSliceWithError()

673

674

for _, kv := range kvs {

675

keyTuple, _ := tuple.Unpack(kv.Key)

676

fmt.Printf("%v = %s\n", keyTuple, string(kv.Value))

677

}

678

679

return nil, nil

680

})

681

}

682

```

683

684

**Watch Operations (Python):**

685

```python

686

import fdb

687

import threading

688

import time

689

690

fdb.api_version(630)

691

db = fdb.open()

692

693

def watch_key():

694

@fdb.transactional

695

def setup_watch(tr, key):

696

# Get current value and set up watch

697

current_value = tr.get(key).wait()

698

watch_future = tr.watch(key)

699

return current_value, watch_future

700

701

key = b"watched_key"

702

current_value, watch_future = setup_watch(db, key)

703

print(f"Initial value: {current_value}")

704

705

# Wait for change (this will block until key changes)

706

watch_future.wait()

707

print("Key changed!")

708

709

# Get new value

710

@fdb.transactional

711

def get_new_value(tr, key):

712

return tr.get(key).wait()

713

714

new_value = get_new_value(db, key)

715

print(f"New value: {new_value}")

716

717

# Start watching in background

718

watch_thread = threading.Thread(target=watch_key)

719

watch_thread.start()

720

721

# Change the value after a delay

722

time.sleep(2)

723

724

@fdb.transactional

725

def change_value(tr):

726

tr.set(b"watched_key", b"new_value")

727

728

change_value(db)

729

watch_thread.join()

730

```