Remote / Declared writes

Failure and recovery

OutcomeMeaningWhat the client does
Writtenthe row as it now is, under the caller's rules; a row the write hid from them as deletednothing; the answer replaces what showed
Forbiddena hidden row, or a rule that does not holdshow it; asking again is refused again
Conflictthe input's revision is not the row'sre-read, then let the author decide
Unmetthe row does not hold the write's whenshow why; the state changed
Invalidthe row the write would leave breaks an invariant, over a row or over the changeshow its message beside its path; change the input
Takenan insert's id is already a row's, and the ledger holds no record of this operation making it (a 409 over HTTP)choose another id; the refusal names the Entity and the id, nothing of the row
"changed while it was written"the row kept moving through three decisionsretry later
Unreachableno answer came back; the write may or may not have landedData.retry (a durable write is sent again on its own); safe as far as the retry table says
"needs a database with transactions"a ledger over a database with neither a transaction nor D1's batchgive the writer a Drizzle database

What each database guarantees

SQLitePostgresD1
Revision, when, increment, invariantone statementone statementone statement
checkConstraints, entityChecksin create tablealter table … addin create table
Ledgera synchronous transactionan awaited transaction; the primary key settles a racea read, then one batch; the primary key settles a race
revisionTriggeryesyesyes (SQLite)

One statement is atomic on every database, so a precondition and an increment need no transaction. D1 runs no BEGIN, so its ledger writes the row and the record in one batch, the record decided in SQL (changes() > 0) since a batch reads nothing between its statements. Opening a transaction is not by itself a guarantee: the ledger relies on its primary key, not on isolation, to refuse a second copy.

Limits

  • One row by id. Bulk predicates, nested writes and multi-row programs are handlers.
  • An invariant reads one row. Uniqueness, foreign keys and limits across rows need the database to coordinate them; a predicate cannot say them.
  • A computed assignment reads its own row before the write and the input, never another row or another assignment's result.
  • A rule that reads facts outside the database (a remote permission service) cannot be made atomic with the write; only what the statement can read is.
  • Without the trigger, the pin trusts every writer to move the revision.
  • A ledger's record lasts its retention; an increment retried later is applied again.