Skip to content
fdb-layer-engineering logo

FoundationDB Layer Engineering

fdb-layer-engineering

Guide for building FoundationDB layers in Rust. Use when designing

SKILL.md

Full skill instructions

FoundationDB Layer Engineering

Patterns for building robust FDB layers with the foundationdb crate.

Transaction Limits

ResourceTargetHard Limit
Duration<1 sec5 sec (MVCC window)
Transaction size<8 MB10 MB
Key size—10 KB
Value size—100 KB
Batch size100-1000 records—

Keep transactions short—autothrottle can't rate-limit large transactions effectively.

Transaction Rules

RuleDetails
Always use db.run()Handles retry loop with exponential backoff
Design for OCCConflicts detected at commit, not during reads
Automatic idempotencyFDB 7.3+: use TransactionOption::AutomaticIdempotency
Use Batch prioritySet TransactionOption::PriorityBatch for background jobs
Idempotent writesGenerate unique IDs OUTSIDE retry loop

Critical Error Codes

CodeNameAction
1007transaction_too_oldRetry—exceeded 5s limit
1020not_committedRetry—conflict detected
1021commit_unknown_resultMay have committed—check maybe_committed
2101transaction_too_largeReduce size (non-retryable)

Key Design Patterns

  • Tuple layer: Always use pack()/unpack() for order-preserving keys
  • Subspaces: Isolate tenant data in contiguous key ranges
  • Sharded counters: Split hot keys across multiple shards
  • Snapshot reads: trx.get(key, true) to skip conflict range

Hotspot Prevention

TechniqueWhen to Use
SaltingPrepend hash prefix to distribute sequential writes
Reversed numbersMAX_VALUE - ts for latest-first ordering
Avoid monotonic keysSequential IDs/​timestamps hotspot single storage nodes

Atomic Operations

OperationUse Case
AddCounters (conflict-free increments)
Max/MinHigh/​low watermarks
SetVersionstampedKeyOrdered logs, event sourcing
CompareAndClearConditional deletion

Secondary Indexes

  • Index key format: (index_prefix, indexed_value, primary_key)
  • Always update indexes in same transaction as primary data
  • Covering indexes store full value to avoid second lookup

Decision Matrix

ScenarioStrategy
Single record CRUDOne txn, <100ms
Bulk import (100s)Batched, 100-200 records/​txn
Bulk import (millions)Batched + continuation + Batch priority
Read then external APISeparate transactions
Hot keyAtomic ops or sharding
Background jobBatched + Batch priority

Anti-patterns

  1. Long transactions - Break large ops into continuations
  2. Holding txn during external calls - Separate read and write transactions
  3. Fixed batch sizes - Use size-aware batching (~1MB per batch)
  4. Ignoring maybe_committed - Causes duplicate side effects on retry
  5. Hot keys without sharding - Shard frequently updated keys
  6. Atomic ops + reads on same key - Loses conflict-free benefit
  7. Low-level get_range - Use get_ranges/get_ranges_keyvalues instead

Full Reference