or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

blob-granules.mdc-api.mdgo-api.mdindex.mdjava-api.mdkey-encoding.mdmulti-tenancy.mdpython-api.mdruby-api.md

python-api.mddocs/

0

# Python API Reference

1

2

The FoundationDB Python API provides a high-level, Pythonic interface to the database with automatic transaction retry, iterator support for ranges, and comprehensive error handling. The API is built on the C client library and offers both functional and object-oriented patterns.

3

4

## Installation

5

6

```bash

7

pip install foundationdb

8

```

9

10

## Core Imports

11

12

```python

13

import fdb

14

import fdb.tuple

15

import fdb.directory

16

import fdb.subspace

17

```

18

19

## Capabilities

20

21

### Initialization and Database Connection

22

23

API version selection and database connection management.

24

25

```python { .api }

26

def api_version(version: int) -> None:

27

"""

28

Set the API version (must be called before any other operations).

29

30

Args:

31

version: API version to use (typically 740 for v7.4.0+)

32

"""

33

34

def open(cluster_file: Optional[str] = None) -> Database:

35

"""

36

Open database connection.

37

38

Args:

39

cluster_file: Path to cluster file (None for default location)

40

41

Returns:

42

Database handle for creating transactions

43

"""

44

```

45

46

**Usage Example:**

47

48

```python

49

import fdb

50

51

# Initialize FoundationDB (required first step)

52

fdb.api_version(740)

53

54

# Open database connection

55

db = fdb.open() # Uses default cluster file

56

# or specify cluster file path

57

db = fdb.open('/etc/foundationdb/fdb.cluster')

58

```

59

60

### Database Operations

61

62

Database handle for creating transactions and managing tenants.

63

64

```python { .api }

65

class Database:

66

"""Database handle for transaction creation and tenant management."""

67

68

def create_transaction(self) -> Transaction:

69

"""

70

Create a new transaction.

71

72

Returns:

73

Transaction handle for database operations

74

"""

75

76

def open_tenant(self, tenant_name: bytes) -> Tenant:

77

"""

78

Open tenant handle for multi-tenancy.

79

80

Args:

81

tenant_name: Name of the tenant to open

82

83

Returns:

84

Tenant handle with isolated keyspace

85

"""

86

```

87

88

### Transaction Decorator and Functional Interface

89

90

High-level transaction handling with automatic retry logic.

91

92

```python { .api }

93

@transactional

94

def transaction_function(tr: Transaction, *args, **kwargs) -> Any:

95

"""

96

Decorator for automatic transaction retry.

97

Functions decorated with @fdb.transactional automatically retry

98

on retryable errors and conflicts.

99

100

Args:

101

tr: Transaction handle (automatically provided)

102

*args, **kwargs: Function arguments

103

104

Returns:

105

Function return value

106

"""

107

108

def transactional(func: Callable) -> Callable:

109

"""

110

Decorator that automatically retries functions on database conflicts.

111

112

Args:

113

func: Function to wrap with retry logic

114

115

Returns:

116

Wrapped function with automatic retry

117

"""

118

```

119

120

**Usage Example:**

121

122

```python

123

import fdb

124

125

fdb.api_version(740)

126

db = fdb.open()

127

128

@fdb.transactional

129

def update_counter(tr, counter_key, increment=1):

130

"""Atomically increment a counter."""

131

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

132

if current_value is None:

133

new_value = increment

134

else:

135

new_value = int(current_value) + increment

136

137

tr.set(counter_key, str(new_value).encode())

138

return new_value

139

140

# Function automatically retries on conflicts

141

result = update_counter(db, b'my_counter', 5)

142

print(f"New counter value: {result}")

143

```

144

145

### Transaction Operations

146

147

Core transaction operations for reading and writing data.

148

149

```python { .api }

150

class Transaction:

151

"""Transaction context for ACID database operations."""

152

153

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

154

"""

155

Read a single key.

156

157

Args:

158

key: Key to read (bytes, str, or tuple)

159

snapshot: Whether to use snapshot read (no conflicts)

160

161

Returns:

162

Future containing the value (None if key doesn't exist)

163

"""

164

165

def get_range(self, begin: KeyConvertible, end: KeyConvertible,

166

limit: int = 0, reverse: bool = False,

167

streaming_mode: int = fdb.StreamingMode.ITERATOR) -> Iterable[KeyValue]:

168

"""

169

Read a range of key-value pairs.

170

171

Args:

172

begin: Start key (inclusive)

173

end: End key (exclusive)

174

limit: Maximum number of pairs to return (0 for no limit)

175

reverse: Whether to read in reverse order

176

streaming_mode: How to stream results

177

178

Returns:

179

Iterable of KeyValue objects

180

"""

181

182

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

183

"""

184

Write a key-value pair.

185

186

Args:

187

key: Key to write (bytes, str, or tuple)

188

value: Value to write (bytes or str)

189

"""

190

191

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

192

"""

193

Delete a single key.

194

195

Args:

196

key: Key to delete (bytes, str, or tuple)

197

"""

198

199

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

200

"""

201

Delete a range of keys.

202

203

Args:

204

begin: Start key (inclusive)

205

end: End key (exclusive)

206

"""

207

208

def commit(self) -> Future:

209

"""

210

Commit the transaction.

211

212

Returns:

213

Future that completes when commit finishes

214

"""

215

216

def on_error(self, error: Exception) -> Future:

217

"""

218

Handle transaction errors and determine retry logic.

219

220

Args:

221

error: Exception that occurred

222

223

Returns:

224

Future that completes when ready to retry

225

"""

226

227

def get_read_version(self) -> Future:

228

"""

229

Get the read version for this transaction.

230

231

Returns:

232

Future containing the read version

233

"""

234

235

def set_read_version(self, version: int) -> None:

236

"""

237

Set the read version for this transaction.

238

239

Args:

240

version: Read version to use

241

"""

242

243

def get_committed_version(self) -> int:

244

"""

245

Get the version at which this transaction was committed.

246

Only valid after successful commit.

247

248

Returns:

249

Committed version number

250

"""

251

252

def get_versionstamp(self) -> Future:

253

"""

254

Get the versionstamp for this transaction.

255

Only valid after successful commit.

256

257

Returns:

258

Future containing the 12-byte versionstamp

259

"""

260

261

def add_read_conflict_range(self, begin: KeyConvertible, end: KeyConvertible) -> None:

262

"""

263

Add a range to the transaction's read conflict ranges.

264

265

Args:

266

begin: Start key (inclusive)

267

end: End key (exclusive)

268

"""

269

270

def add_write_conflict_range(self, begin: KeyConvertible, end: KeyConvertible) -> None:

271

"""

272

Add a range to the transaction's write conflict ranges.

273

274

Args:

275

begin: Start key (inclusive)

276

end: End key (exclusive)

277

"""

278

```

279

280

### Future Operations

281

282

Asynchronous result handling and synchronization.

283

284

```python { .api }

285

class Future:

286

"""Asynchronous result wrapper for database operations."""

287

288

def wait(self) -> Any:

289

"""

290

Block until the result is available and return it.

291

292

Returns:

293

The result value

294

295

Raises:

296

FDBError: If the operation failed

297

"""

298

299

def is_ready(self) -> bool:

300

"""

301

Check if the result is available without blocking.

302

303

Returns:

304

True if result is ready, False otherwise

305

"""

306

307

def block_until_ready(self) -> None:

308

"""

309

Block until the result is available (without returning it).

310

"""

311

312

def on_ready(self, callback: Callable[[Future], None]) -> None:

313

"""

314

Set a callback to be called when the result is ready.

315

316

Args:

317

callback: Function to call with this Future as argument

318

"""

319

```

320

321

### Key Selectors and Range Operations

322

323

Advanced key selection and range query operations.

324

325

```python { .api }

326

class KeySelector:

327

"""Key selector for advanced range operations."""

328

329

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

330

"""

331

Create a key selector.

332

333

Args:

334

key: Reference key

335

or_equal: Whether to include the reference key

336

offset: Offset from the reference key

337

"""

338

339

@staticmethod

340

def last_less_than(key: KeyConvertible) -> KeySelector:

341

"""Key selector for last key less than given key."""

342

343

@staticmethod

344

def last_less_or_equal(key: KeyConvertible) -> KeySelector:

345

"""Key selector for last key less than or equal to given key."""

346

347

@staticmethod

348

def first_greater_than(key: KeyConvertible) -> KeySelector:

349

"""Key selector for first key greater than given key."""

350

351

@staticmethod

352

def first_greater_or_equal(key: KeyConvertible) -> KeySelector:

353

"""Key selector for first key greater than or equal to given key."""

354

355

class KeyValue:

356

"""Key-value pair from range operations."""

357

key: bytes

358

value: bytes

359

```

360

361

### Tenant Operations

362

363

Multi-tenancy support for isolated keyspaces.

364

365

```python { .api }

366

class Tenant:

367

"""Tenant handle for multi-tenancy operations."""

368

369

def create_transaction(self) -> Transaction:

370

"""

371

Create a new transaction within this tenant's keyspace.

372

373

Returns:

374

Transaction handle scoped to this tenant

375

"""

376

377

def __enter__(self) -> Tenant:

378

"""Context manager entry."""

379

380

def __exit__(self, exc_type, exc_val, exc_tb) -> None:

381

"""Context manager exit."""

382

```

383

384

### Error Handling

385

386

Exception classes and error predicate functions.

387

388

```python { .api }

389

class FDBError(Exception):

390

"""Base exception class for FoundationDB errors."""

391

392

def __init__(self, code: int):

393

"""

394

Create FDBError with error code.

395

396

Args:

397

code: FoundationDB error code

398

"""

399

self.code = code

400

super().__init__(fdb.strerror(code))

401

402

# Error predicate functions

403

def predicates.retryable(error: Exception) -> bool:

404

"""

405

Check if error indicates transaction should be retried.

406

407

Args:

408

error: Exception to check

409

410

Returns:

411

True if error is retryable

412

"""

413

414

def predicates.maybe_committed(error: Exception) -> bool:

415

"""

416

Check if transaction may have been committed despite error.

417

418

Args:

419

error: Exception to check

420

421

Returns:

422

True if transaction may have committed

423

"""

424

425

def predicates.retryable_not_committed(error: Exception) -> bool:

426

"""

427

Check if error is retryable and transaction was not committed.

428

429

Args:

430

error: Exception to check

431

432

Returns:

433

True if safe to retry (not committed)

434

"""

435

```

436

437

### Tuple Encoding

438

439

Structured key encoding for hierarchical data organization.

440

441

```python { .api }

442

# fdb.tuple module

443

def pack(items: Tuple[Any, ...]) -> bytes:

444

"""

445

Encode tuple to bytes for use as key.

446

447

Args:

448

items: Tuple of values to encode

449

450

Returns:

451

Encoded bytes suitable for use as FoundationDB key

452

"""

453

454

def unpack(data: bytes) -> Tuple[Any, ...]:

455

"""

456

Decode bytes back to tuple.

457

458

Args:

459

data: Encoded tuple bytes

460

461

Returns:

462

Decoded tuple values

463

"""

464

465

def range(prefix: Tuple[Any, ...]) -> Tuple[bytes, bytes]:

466

"""

467

Create key range for all tuples with given prefix.

468

469

Args:

470

prefix: Tuple prefix

471

472

Returns:

473

(begin_key, end_key) tuple for range operations

474

"""

475

```

476

477

**Usage Example:**

478

479

```python

480

import fdb.tuple as tuple

481

482

# Encode structured keys

483

user_key = tuple.pack(("users", user_id, "profile"))

484

score_key = tuple.pack(("scores", game_id, timestamp, player_id))

485

486

# Create ranges for prefix queries

487

user_range = tuple.range(("users", user_id)) # All keys for this user

488

score_range = tuple.range(("scores", game_id)) # All scores for this game

489

490

# Use in transactions

491

@fdb.transactional

492

def get_user_data(tr, user_id):

493

begin, end = tuple.range(("users", user_id))

494

return list(tr.get_range(begin, end))

495

```

496

497

### Locality Operations

498

499

Functions for querying database topology and key distribution.

500

501

```python { .api }

502

# fdb.locality module

503

def get_boundary_keys(db_or_tr, begin: KeyConvertible, end: KeyConvertible) -> Iterator[bytes]:

504

"""

505

Get boundary keys for shards in the specified range.

506

507

Args:

508

db_or_tr: Database or Transaction handle

509

begin: Start of key range

510

end: End of key range

511

512

Returns:

513

Iterator of boundary keys between shards

514

"""

515

516

@transactional

517

def get_addresses_for_key(tr: Transaction, key: KeyConvertible) -> Future:

518

"""

519

Get network addresses of servers storing the specified key.

520

521

Args:

522

tr: Transaction handle

523

key: Key to query addresses for

524

525

Returns:

526

Future containing list of server addresses

527

"""

528

```

529

530

**Usage Example:**

531

532

```python

533

import fdb.locality

534

535

# Get shard boundaries for a key range

536

boundaries = list(fdb.locality.get_boundary_keys(db, b"user/", b"user0"))

537

print(f"Found {len(boundaries)} shard boundaries")

538

539

# Get addresses for a specific key

540

@fdb.transactional

541

def find_key_location(tr, key):

542

addresses = fdb.locality.get_addresses_for_key(tr, key).wait()

543

return addresses

544

545

servers = find_key_location(db, b"user:12345")

546

```

547

548

### Administrative Tenant Management

549

550

Functions for creating, deleting, and listing tenants in the cluster.

551

552

```python { .api }

553

# fdb.tenant_management module (available in API version 630+)

554

def create_tenant(db_or_tr, tenant_name: bytes) -> None:

555

"""

556

Create a new tenant in the cluster.

557

558

Args:

559

db_or_tr: Database or Transaction handle

560

tenant_name: Unique name for the tenant

561

562

Raises:

563

FDBError(2132): tenant_already_exists if tenant exists

564

"""

565

566

def delete_tenant(db_or_tr, tenant_name: bytes) -> None:

567

"""

568

Delete a tenant from the cluster.

569

570

Args:

571

db_or_tr: Database or Transaction handle

572

tenant_name: Name of tenant to delete

573

574

Raises:

575

FDBError(2131): tenant_not_found if tenant doesn't exist

576

"""

577

578

def list_tenants(db_or_tr, begin: bytes, end: bytes, limit: int) -> FDBTenantList:

579

"""

580

List tenants in the cluster within the specified range.

581

582

Args:

583

db_or_tr: Database or Transaction handle

584

begin: Start of tenant name range (inclusive)

585

end: End of tenant name range (exclusive)

586

limit: Maximum number of tenants to return

587

588

Returns:

589

Iterable of KeyValue objects with tenant names and metadata

590

"""

591

592

class FDBTenantList:

593

"""Iterator for tenant listing results."""

594

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

595

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

596

```

597

598

**Usage Example:**

599

600

```python

601

import fdb.tenant_management

602

603

# Create tenants for different applications

604

fdb.tenant_management.create_tenant(db, b"app1")

605

fdb.tenant_management.create_tenant(db, b"app2")

606

607

# List all tenants

608

tenants = fdb.tenant_management.list_tenants(db, b"", b"\xFF", 100)

609

for tenant in tenants:

610

tenant_name = tenant.key.decode()

611

print(f"Tenant: {tenant_name}")

612

613

# Delete a tenant

614

fdb.tenant_management.delete_tenant(db, b"old_app")

615

```

616

617

### Subspace Operations

618

619

Key prefix management for organizing data hierarchies.

620

621

```python { .api }

622

class Subspace:

623

"""Key prefix management for hierarchical data organization."""

624

625

def __init__(self, prefixTuple: Tuple[Any, ...] = tuple(), rawPrefix: bytes = b''):

626

"""

627

Create subspace with tuple prefix.

628

629

Args:

630

prefixTuple: Tuple to use as prefix

631

rawPrefix: Raw bytes to use as prefix

632

"""

633

634

def pack(self, t: Tuple[Any, ...] = tuple()) -> bytes:

635

"""

636

Pack tuple with subspace prefix.

637

638

Args:

639

t: Tuple to pack with prefix

640

641

Returns:

642

Packed key with subspace prefix

643

"""

644

645

def unpack(self, key: bytes) -> Tuple[Any, ...]:

646

"""

647

Unpack key and remove subspace prefix.

648

649

Args:

650

key: Key to unpack

651

652

Returns:

653

Tuple without subspace prefix

654

"""

655

656

def range(self, t: Tuple[Any, ...] = tuple()) -> Tuple[bytes, bytes]:

657

"""

658

Create range within this subspace.

659

660

Args:

661

t: Tuple suffix for range

662

663

Returns:

664

(begin_key, end_key) for range operations

665

"""

666

667

def contains(self, key: bytes) -> bool:

668

"""

669

Check if key belongs to this subspace.

670

671

Args:

672

key: Key to check

673

674

Returns:

675

True if key is in this subspace

676

"""

677

678

def subspace(self, t: Tuple[Any, ...]) -> Subspace:

679

"""

680

Create child subspace.

681

682

Args:

683

t: Additional tuple elements for child

684

685

Returns:

686

New subspace with extended prefix

687

"""

688

```

689

690

### Directory Layer

691

692

Hierarchical directory management system built on subspaces.

693

694

```python { .api }

695

# fdb.directory module

696

class DirectoryLayer:

697

"""Hierarchical directory management for organizing keyspaces."""

698

699

def create_or_open(self, tr: Transaction, path: List[str],

700

layer: bytes = b'') -> Directory:

701

"""

702

Create or open directory at path.

703

704

Args:

705

tr: Transaction handle

706

path: Directory path as list of strings

707

layer: Layer identifier for directory type

708

709

Returns:

710

Directory handle

711

"""

712

713

def open(self, tr: Transaction, path: List[str],

714

layer: bytes = b'') -> Directory:

715

"""

716

Open existing directory at path.

717

718

Args:

719

tr: Transaction handle

720

path: Directory path as list of strings

721

layer: Expected layer identifier

722

723

Returns:

724

Directory handle

725

726

Raises:

727

DirectoryError: If directory doesn't exist

728

"""

729

730

def create(self, tr: Transaction, path: List[str],

731

layer: bytes = b'', prefix: bytes = None) -> Directory:

732

"""

733

Create new directory at path.

734

735

Args:

736

tr: Transaction handle

737

path: Directory path as list of strings

738

layer: Layer identifier for directory type

739

prefix: Specific prefix to use (None for automatic)

740

741

Returns:

742

Directory handle

743

744

Raises:

745

DirectoryError: If directory already exists

746

"""

747

748

def list(self, tr: Transaction, path: List[str] = []) -> List[str]:

749

"""

750

List subdirectories at path.

751

752

Args:

753

tr: Transaction handle

754

path: Directory path to list

755

756

Returns:

757

List of subdirectory names

758

"""

759

760

def remove(self, tr: Transaction, path: List[str]) -> bool:

761

"""

762

Remove directory and all its data.

763

764

Args:

765

tr: Transaction handle

766

path: Directory path to remove

767

768

Returns:

769

True if directory was removed

770

"""

771

772

def move(self, tr: Transaction, old_path: List[str],

773

new_path: List[str]) -> Directory:

774

"""

775

Move directory to new path.

776

777

Args:

778

tr: Transaction handle

779

old_path: Current directory path

780

new_path: New directory path

781

782

Returns:

783

Directory handle at new location

784

"""

785

786

def exists(self, tr: Transaction, path: List[str]) -> bool:

787

"""

788

Check if directory exists at path.

789

790

Args:

791

tr: Transaction handle

792

path: Directory path to check

793

794

Returns:

795

True if directory exists

796

"""

797

798

class Directory(Subspace):

799

"""Directory handle extending Subspace functionality."""

800

801

def get_layer(self) -> bytes:

802

"""Get the layer identifier for this directory."""

803

804

def get_path(self) -> List[str]:

805

"""Get the path for this directory."""

806

807

# Default directory layer instance

808

directory = DirectoryLayer()

809

```

810

811

## Configuration Options

812

813

### Transaction Options

814

815

```python { .api }

816

# Transaction option constants

817

class TransactionOptions:

818

CAUSAL_READ_RISKY = 0

819

CAUSAL_WRITE_RISKY = 1

820

READ_YOUR_WRITES_DISABLE = 2

821

READ_AHEAD_DISABLE = 3

822

DURABILITY_DATACENTER = 4

823

DURABILITY_RISKY = 5

824

PRIORITY_SYSTEM_IMMEDIATE = 6

825

PRIORITY_BATCH = 7

826

INITIALIZE_NEW_DATABASE = 8

827

ACCESS_SYSTEM_KEYS = 9

828

READ_SYSTEM_KEYS = 10

829

TIMEOUT = 11

830

RETRY_LIMIT = 12

831

MAX_RETRY_DELAY = 13

832

SIZE_LIMIT = 14

833

SNAPSHOT_RYW_ENABLE = 15

834

SNAPSHOT_RYW_DISABLE = 16

835

LOCK_AWARE = 17

836

USED_DURING_COMMIT_PROTECTION_DISABLE = 18

837

READ_LOCK_AWARE = 19

838

839

# Usage

840

tr.options.set_timeout(5000) # 5 second timeout

841

tr.options.set_retry_limit(100)

842

tr.options.set_size_limit(10000000) # 10MB limit

843

```

844

845

### Streaming Modes

846

847

```python { .api }

848

class StreamingMode:

849

"""Streaming modes for range operations."""

850

WANT_ALL = -2

851

ITERATOR = -1 # Default for iteration

852

EXACT = 0

853

SMALL = 1

854

MEDIUM = 2

855

LARGE = 3

856

SERIAL = 4

857

```

858

859

## Complete Usage Example

860

861

```python

862

import fdb

863

import fdb.tuple as tuple

864

import fdb.directory

865

866

# Initialize FoundationDB

867

fdb.api_version(740)

868

db = fdb.open()

869

870

# Directory-based organization

871

@fdb.transactional

872

def setup_application(tr):

873

# Create application directories

874

root = fdb.directory.create_or_open(tr, ['myapp'])

875

users = root.create_or_open(tr, ['users'])

876

sessions = root.create_or_open(tr, ['sessions'])

877

return users, sessions

878

879

users_dir, sessions_dir = setup_application(db)

880

881

@fdb.transactional

882

def create_user(tr, user_id, email, name):

883

"""Create a new user with profile data."""

884

user_space = users_dir.subspace((user_id,))

885

886

# Store user profile

887

tr.set(user_space.pack(('email',)), email.encode())

888

tr.set(user_space.pack(('name',)), name.encode())

889

tr.set(user_space.pack(('created',)), str(time.time()).encode())

890

891

# Add to email index

892

email_key = tuple.pack(('email_index', email, user_id))

893

tr.set(email_key, b'')

894

895

return user_id

896

897

@fdb.transactional

898

def get_user_profile(tr, user_id):

899

"""Get complete user profile."""

900

user_space = users_dir.subspace((user_id,))

901

902

profile = {}

903

for kv in tr.get_range(user_space.range()):

904

field = user_space.unpack(kv.key)[0]

905

profile[field] = kv.value.decode()

906

907

return profile

908

909

@fdb.transactional

910

def find_user_by_email(tr, email):

911

"""Find user ID by email address."""

912

begin, end = tuple.range(('email_index', email))

913

914

for kv in tr.get_range(begin, end, limit=1):

915

# Extract user_id from key

916

_, _, user_id = tuple.unpack(kv.key)

917

return user_id

918

919

return None

920

921

# Usage

922

user_id = create_user(db, 'user123', 'alice@example.com', 'Alice Smith')

923

profile = get_user_profile(db, user_id)

924

found_user = find_user_by_email(db, 'alice@example.com')

925

926

print(f"Created user: {user_id}")

927

print(f"Profile: {profile}")

928

print(f"Found user by email: {found_user}")

929

```