name: fdb-layer-engineering
description: Guide for building FoundationDB layers in Rust. Use when designing
key schemas, handling transactions, implementing indexes, or troubleshooting
FDB errors. Covers tuple layer, atomic ops, retries, and performance patterns.
allowed-tools: Read, Grep, Edit, Write
FoundationDB Layer Engineering
Patterns for building robust FDB layers with the foundationdb crate.
Transaction Limits
| Resource | Target | Hard Limit |
|---|
| Duration | <1 sec | 5 sec (MVCC window) |
| Transaction size | <8 MB | 10 MB |
| Key size | — | 10 KB |
| Value size | — | 100 KB |
| Batch size | 100-1000 records | — |
Keep transactions short—autothrottle can't rate-limit large transactions effectively.
Transaction Rules
| Rule | Details |
|---|
Always use db.run() | Handles retry loop with exponential backoff |
| Design for OCC | Conflicts detected at commit, not during reads |
| Automatic idempotency | FDB 7.3+: use TransactionOption::AutomaticIdempotency |
| Use Batch priority | Set TransactionOption::PriorityBatch for background jobs |
| Idempotent writes | Generate unique IDs OUTSIDE retry loop |
Critical Error Codes
| Code | Name | Action |
|---|
| 1007 | transaction_too_old | Retry—exceeded 5s limit |
| 1020 | not_committed | Retry—conflict detected |
| 1021 | commit_unknown_result | May have committed—check maybe_committed |
| 2101 | transaction_too_large | Reduce 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
| Technique | When to Use |
|---|
| Salting | Prepend hash prefix to distribute sequential writes |
| Reversed numbers | MAX_VALUE - ts for latest-first ordering |
| Avoid monotonic keys | Sequential IDs/timestamps hotspot single storage nodes |
Atomic Operations
| Operation | Use Case |
|---|
Add | Counters (conflict-free increments) |
Max/Min | High/low watermarks |
SetVersionstampedKey | Ordered logs, event sourcing |
CompareAndClear | Conditional 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
| Scenario | Strategy |
|---|
| Single record CRUD | One txn, <100ms |
| Bulk import (100s) | Batched, 100-200 records/txn |
| Bulk import (millions) | Batched + continuation + Batch priority |
| Read then external API | Separate transactions |
| Hot key | Atomic ops or sharding |
| Background job | Batched + Batch priority |
Anti-patterns
- Long transactions - Break large ops into continuations
- Holding txn during external calls - Separate read and write transactions
- Fixed batch sizes - Use size-aware batching (~1MB per batch)
- Ignoring
maybe_committed - Causes duplicate side effects on retry
- Hot keys without sharding - Shard frequently updated keys
- Atomic ops + reads on same key - Loses conflict-free benefit
- Low-level
get_range - Use get_ranges/get_ranges_keyvalues instead
Full Reference