Guarantees¶
fdyno maps a DynamoDB action to one or more FoundationDB transactions. FoundationDB supplies the transaction guarantees. fdyno tests the mapping and its failure behavior. These guarantees do not cover unrelated HTTP calls or an underconfigured FoundationDB cluster.
Atomicity, application consistency, isolation, and durability have different scopes and failure modes.
Guarantees by operation¶
| Assertion | Scope | Exception |
|---|---|---|
| A committed data-plane transaction is all-or-nothing. | One FoundationDB transaction. | BatchWriteItem, GSI backfill, backup/restore, and maintenance can span transactions. |
| One-transaction operations are strictly serializable. | Keys and ranges read and written by that transaction. | Pagination is a new snapshot per page; concurrent requests may be ordered either way. |
| A successful write has committed before fdyno replies. | The FoundationDB cluster selected by the deployment. | A lost response can leave the client with an unknown outcome; cluster redundancy determines survival of hardware failure. |
| Base items and active index entries change together. | Existing registered GSIs/LSIs on a normal item mutation. | A new GSI is incomplete while its status is CREATING. |
| A stream record and its item mutation commit together. | Stream-enabled, state-changing writes. | Delivery and consumer effects are not exactly-once; order is per shard. |
A live ClientRequestToken deduplicates the same transaction. |
TransactWriteItems within the 10-minute token window. |
Tokenless retries and replay after the window are not deduplicated. |
| An fdyno process can be replaced without replaying database state. | State persisted in FoundationDB. | Configuration, metrics, in-flight requests, and some compatibility state are process-local. |
| A large restore is invisible until complete and can resume after process loss. | Durable staged source, target identity, progress, lease, and fence in FoundationDB. | Small restores are one transaction; deleting a CREATING target cancels its job. |
| Historical PITR is never simulated with current data. | Continuous-backup enablement and RestoreTableToPointInTime. | Unsupported and returned as an explicit unavailable error; use tested backups. |
| Conformance results establish the selected tested API cases. | Listed suite revisions and scenarios. | They do not prove unselected options, replicated failover, off-host recovery, or every concurrent history. |
The operation-to-transaction map is in Transactions, and cross-page and concurrent-read behavior is in Consistency model.
Atomicity: one commit exposes all related writes¶
Assertion. For a normal state-changing item operation, fdyno commits the base
item (including chunks), old-index removal, new-index entries, and an optional
change-stream record in one FoundationDB transaction. A tokened
TransactWriteItems also commits its idempotency record in that transaction. A
reader cannot observe only part of that commit.
Scope. This applies to PutItem, UpdateItem, DeleteItem,
TransactWriteItems, and accepted write statements in PartiQL
ExecuteTransaction. A TransactWriteItems request may span tables, provided the
whole request fits one FoundationDB transaction.
Exception and failure behavior. Validation and failed conditions commit
nothing. A process failure before commit exposes none of the buffered writes. A
process or network failure after commit may prevent the response from arriving, but
does not turn one commit into a partial commit. BatchWriteItem is explicitly
non-atomic and may split an oversized valid batch; GSI backfill, backup, restore,
TTL cleanup, and stream trimming use bounded multi-transaction protocols.
Evidence. The write set is assembled in
item_ops.go,
txn_ops.go,
fdb_store.go,
and
streams.go.
Transaction rollback is exercised by
ExtendDB transaction tests
and
nubo-db transaction tests.
The underlying all-or-nothing commit guarantee is FoundationDB's
transaction guarantee.
Consistency: invariants must be expressed in the transaction¶
Assertion. A transaction can enforce an invariant that it reads and checks. Every
writer must preserve the same invariant in its transaction. Conflicting attempts cannot
both commit from stale data. Conditions such as attribute_not_exists(pk) and
read-modify-write updates therefore have one serializable outcome.
Scope. This covers the keys and ranges fdyno reads through FoundationDB's normal, non-snapshot transaction API. Point and range reads create conflict ranges, and a conflicting attempt is retried against a newer read version.
Exception and failure behavior. The āCā in ACID does not invent application rules. An invariant omitted from conditions or split over separate API calls is not made atomic by fdyno. Retryable conflicts can increase latency and eventually fail at the request deadline; they do not authorize a stale commit.
Evidence. Retry and deadline handling is in
transaction_runner.go.
Focused tests cover one-winner conditional writes and concurrent counters, list/set
updates, and nested updates in
test_conditional_writes.py
and
test_concurrency.py.
These are targeted histories, not a formal verification of every workload.
Isolation and read recency: strict serializability per transaction¶
Assertion. A successful one-transaction operation is strictly serializable. It can be placed at one instant between invocation and response, and a transaction started after another operation completed cannot be ordered before that completed operation.
Scope. GetItem, one Query or Scan page, BatchGetItem, and
TransactGetItems each read one FoundationDB MVCC version. Normal fdyno reads use
that transactional path even when ConsistentRead is false or omitted.
Exception and failure behavior. Concurrent operations may be serialized in
either order. LastEvaluatedKey is a key position, not a retained read version, so
a multi-page query or scan is not one repeatable snapshot. Separate GetItem calls
are separate transactions. ConsistentRead=true remains invalid on a GSI for
DynamoDB request compatibility even though active GSI maintenance is synchronous.
If FoundationDB cannot supply a read version, fdyno fails the request rather than
serving a process-local stale copy.
Evidence. The operation boundaries are implemented in
query_ops.go,
batch_ops.go,
and
txn_ops.go.
FoundationDB documents global ACID transactions with strict serializability in its
developer guide.
fdyno publishes one narrow two-instance history for dropped responses, process loss,
and single-FDB restart. It is not Jepsen or Elle and does not inject an fdyno-to-FDB
network partition. Replicated failover and broad history analysis remain evidence gaps.
Durability: success means FoundationDB accepted the commit¶
Assertion. fdyno sends a successful write response only after the FoundationDB commit completes. No fdyno-local write-behind queue or WAL must later be flushed.
Scope. Durability covers the committed keys in the attached FoundationDB database. Table metadata, items, chunks, indexes, stream records, transaction-token records, and fdyno backup objects are durable database state.
Exception and failure behavior. Durability against machine, disk, zone, or site failure is only as strong as the FoundationDB redundancy and backup design selected by the operator. More fdyno replicas do not add storage replicas. A timeout while a commit is in flight is explicitly returned as an outcome that may be unknown; an error or missing HTTP response is not proof of rollback. fdyno publishes no general RPO, RTO, or availability SLO.
Evidence. Commit acknowledgement and unknown-outcome mapping are in
transaction_runner.go.
The bounded two-instance campaign is
scripts/fault-smoke.sh.
Its checked invariants and limits are in Failure testing.
FoundationDB's cluster is the durable-state owner; deployment responsibilities are
spelled out in Durability & recovery and
Deployment & operations.
Secondary indexes: synchronous after registration¶
Assertion. A successful normal item mutation cannot expose a new base item with an old active GSI/LSI entry. Old entries are removed and new projected entries are written in the base mutation's transaction.
Scope. This applies to indexes already registered in table metadata, including a
GSI registered as CREATING for live writes after the registration commit.
Exception and failure behavior. Existing rows are copied into a newly added GSI
across bounded backfill transactions. Until status becomes ACTIVE, old rows may be
missing. A stopped backfill is retried on process startup; readiness does not wait
for it. Index projection also increases transaction size and latency, so a logically
valid item can fail when the amplified FoundationDB write is too large.
Evidence. Index writes are in
fdb_store.go,
and the CREATING ā backfill ā ACTIVE protocol is in
table_ops.go.
Black-box coverage includes
test_gsi.py,
test_lsi.py,
and
test_gsi_updatetable.py.
Streams: atomic capture and per-shard commit order¶
Assertion. For a stream-enabled table, a state-changing mutation and its stream record commit together. Versionstamped keys order records by FoundationDB commit order within each shard; records for one partition key remain in one shard.
Scope. The guarantee covers stream record creation and stored ordering. With the default single shard, one table has one stored commit order. With multiple static shards, ordering is per shard and therefore per partition key.
Exception and failure behavior. This is not exactly-once delivery or exactly-once sink processing. A consumer can replay a sequence number and must checkpoint and apply records idempotently. There is no global API delivery order across shards. Iterators do not expire, and trimming can remove unread history without an explicit gap error. Large records use versioned, integrity-checked chunks. They still increase the size of the item mutation's FoundationDB transaction.
Evidence. Atomic record creation and versionstamps are in
streams.go,
with client-level coverage in
test_streams.py.
Consumer and retention boundaries are detailed in Change streams.
Retry deduplication: token and data share the commit¶
Assertion. Repeating the same TransactWriteItems request with the same live
ClientRequestToken does not apply the mutation again. Reusing that token for a
different request fails with IdempotentParameterMismatchException.
Scope. The request fingerprint and token timestamp are stored in FoundationDB in the same transaction as the protected writes. The behavior survives process restart and works across fdyno instances for the 10-minute window.
Exception and failure behavior. A client retry does not add a token to other APIs. A tokenless read-modify-write can apply twice after a lost response. A replay after the token window is a new request. Malformed token records fail closed.
Evidence. The durable format and window are in
idempotency.go.
Cross-instance, concurrent, replay, mismatch, and expiry behavior is exercised in
retry_safety_integration_test.go,
retry_safety_test.go,
and
test_transact.py.
No end-to-end test currently injects a real lost commit acknowledgement; see
Reliable writes for required client behavior.
Process replacement: durable state is not tied to one server¶
Assertion. Replacing an fdyno process does not require replaying table data. Another correctly configured process attached to the same FoundationDB database can read committed user state.
Scope. This covers schema, items, indexes, stream topology and records, backups, restore jobs, TTL configuration, receipts, transaction tokens, and worker state.
Exception and failure behavior. Credentials, environment defaults, open requests, metrics, token-GC cursor, and some compatibility configuration are process-local. Process replacement does not recover a failed FoundationDB cluster.
Evidence. The state boundary is visible in
types.go,
lease.go,
ttl.go,
and Architecture overview.
Not guaranteed¶
- They do not make
BatchWriteItematomic or make a sequence of HTTP calls one transaction. - They do not provide a snapshot across query/scan pages.
- They do not provide multi-Region replication, detached reads, or writes while FoundationDB is unavailable.
- They do not make a
CREATINGGSI complete, a stream consumer exactly-once, or an in-cluster backup independent disaster recovery. - They do not specify a throughput floor, availability percentage, RPO, or RTO.
Before relying on these guarantees, review Tradeoffs, Limits, Compatibility, and the operator-owned failure domain in Durability & recovery.