~13 min · idempotency, sqlite, transactions, retention, storage-growth
Level 0Loose Note
0 XP0/36 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
The Worst Moment for a Network to Fail
A remote write can fail in three places. Before the server saw it: harmless, nothing happened. After the server answered: harmless, you know what happened. And in between: the server did the work and the answer never reached you. The last lesson's client refuses to guess about that middle case. It keeps an uncertain receipt and never resends. That refusal leaves something to reconcile because of what sits on the other side: every write a caller asks the engine for is a command with a key, and the key remembers its answer.
Engine.command, Line by Line
A bounded key. Empty, or longer than 200 characters, and the call stops with A bounded idempotency key is required. The key becomes a primary key in a table that lives for a week, so it has to be finite.
A fingerprint of the whole input. SHA-256 over the JSON of [action, payload, metadata]. Metadata is in there on purpose: the same key arriving from a different actor or session is different input, not a replay.
One decision, under the write lock. Inside the writer mutex, on a fresh connection, the engine opens BEGIN IMMEDIATE, which takes SQLite's write lock at the start of the transaction instead of at its first write. Nothing can slip between asking is this key new? and recording the answer.
A known key. Different fingerprint: Conflict('Idempotency key was already used for different input'). Same fingerprint: the stored result is decoded and returned, and the operation does not run again.
A new key. The operation runs inside a request context whose causation is the key, so every change it causes is stamped with the command behind it (the next lesson follows that stamp). Then (key, fingerprint, result, time) goes into engine_commands and the transaction commits.
Anything goes wrong. Rollback. The work and its receipt vanish together, so no key ever remembers an answer that did not happen.
Arrives
Engine does
New key
Runs the operation, stores the answer
Same key, same input
Returns the stored answer; nothing runs
Same key, different input
Idempotency key was already used for different input
The Receipt That Ate the Store
At first every compatibility call went through command, reads included, and every answer was kept. Three production days after the cutover, the 2026-09-30 review measured what that cost: the largest single command result was a list_history answer of 236 KB, and stored results had added up to 1.45 MB in three days. A second leak sat beside it: the chief's run preparation wrote a command every five seconds, 458 of the 1,288 rows in engine_commands.
Commit 4a99306 drew the line in code. A frozen set named WRITES listed the 24 domain operations that mutate the ledger. It holds 25 today: ack_todo_replies joined on 2026-10-05, when replies started needing an explicit review. Everything else is a read, run in a transaction that is rolled back and answered without a receipt. The comment above the set keeps the reason next to the rule: a read result kept for ever was the store's largest single growth. Repeating a read harms nothing, and a read's answer is only true at the moment it is computed, so remembering it bought nothing.
Look at who is on the list, though. read_latest_letter sounds like a read and is a write, because it moves an unread letter to read. The test for membership is whether the ledger changes, never what the verb is called.
Seven Days, Then a Stranger
maintain() runs at engine start and every six hours, and deletes command receipts older than seven days. It is the engine's own housekeeping, not a caller's request, so it does not go through command at all: it runs in a plain transaction with no key and no receipt, and the feed rows its pruning causes (an expired notification, a deleted chief run) carry the causation maintain. So the replay promise has a window. Inside the week, a key resent with the same input gets its original answer. After that, the same key is a stranger and would run as new work. This is normal for the pattern: Stripe documents its own idempotency keys as removable after at least 24 hours. Treat it as part of the contract: an uncertain receipt is worth reconciling while its answer still exists.
Idempotency is a promise about writes, kept for a stated time. Name every write, remember each answer under its key long enough to reconcile, and let reads go: an answer about the present does not improve with age.
Code
Coop's engine: what counts as a write, and the keyed command (trimmed excerpt)·python
# Coop's engine: what counts as a write, and the keyed command (trimmed excerpt)
# Domain operations that mutate the ledger. Every other operation is a read,
# answered without an idempotency receipt: a read result kept for ever was
# the store's largest single growth (one list_history answer was 236 KB).
WRITES = frozenset({'add_todo', 'take_todo', 'take_scope', 'release_todo', 'abandon_todo', 'assign', 'edit_todo', 'done_todo', 'ack_todo_replies',
'send_request', 'assign_request', 'take_request', 'release_request', 'reply', 'close_request',
'write_letter', 'read_latest_letter', 'start_letter', 'ack_letter', 'abandon_letter',
'add_history_entry', 'edit_history_entry', 'add_learning_entry', 'supersede_learning', 'edit_learning_entry'})
RETENTION = {'engine_commands': 7 * 86400, 'engine_changes': 30 * 86400, ...}
def command(self, key, action, payload, metadata, operation):
if not key or len(key) > 200:
raise ValueError('A bounded idempotency key is required')
fingerprint = hashlib.sha256(dump([action, payload, metadata]).encode()).hexdigest()
with self._mutex, self.connection() as conn:
conn.execute('BEGIN IMMEDIATE')
try:
previous = conn.execute('SELECT * FROM engine_commands WHERE id=?', (key,)).fetchone()
if previous:
if previous['fingerprint'] != fingerprint:
raise Conflict('Idempotency key was already used for different input')
result = json.loads(previous['result'], object_hook=decode)
else:
with context.request(conn, {**metadata, "causation":key}):
result = operation(conn)
conn.execute('INSERT INTO engine_commands VALUES (?,?,?,?)', (key, fingerprint, dump(result), time.time()))
conn.commit()
return result
except BaseException:
conn.rollback()
raise
A minimal sketch you can run: replay, conflict, and a read that leaves no receipt·python
"""A minimal sketch of Coop's idempotent command: a bounded key, a fingerprint
of the input and a stored answer, decided inside one BEGIN IMMEDIATE."""
import hashlib
import json
import sqlite3
import time
conn = sqlite3.connect(":memory:", isolation_level=None)
conn.execute("CREATE TABLE todos (id INTEGER PRIMARY KEY, title TEXT)")
conn.execute("CREATE TABLE commands (id TEXT PRIMARY KEY, fingerprint TEXT, result TEXT, created REAL)")
class Conflict(Exception):
pass
def add_todo(title):
return conn.execute("INSERT INTO todos(title) VALUES (?)", (title,)).lastrowid
def list_todos():
return [row[0] for row in conn.execute("SELECT title FROM todos ORDER BY id")]
OPERATIONS = {"add_todo": add_todo, "list_todos": list_todos}
WRITES = frozenset({"add_todo"})
def call(key, action, *args, metadata=None):
if action not in WRITES:
return OPERATIONS[action](*args) # a read is answered, never remembered
if not key or len(key) > 200:
raise ValueError("A bounded idempotency key is required")
fingerprint = hashlib.sha256(json.dumps([action, args, metadata or {}]).encode()).hexdigest()
conn.execute("BEGIN IMMEDIATE")
try:
previous = conn.execute("SELECT fingerprint, result FROM commands WHERE id=?", (key,)).fetchone()
if previous:
if previous[0] != fingerprint:
raise Conflict("Idempotency key was already used for different input")
result = json.loads(previous[1]) # replay the stored answer
else:
result = OPERATIONS[action](*args)
conn.execute("INSERT INTO commands VALUES (?,?,?,?)",
(key, fingerprint, json.dumps(result), time.time()))
conn.execute("COMMIT")
return result
except BaseException:
conn.execute("ROLLBACK")
raise
print("first call ->", call("k-1", "add_todo", "Only once"))
print("same again ->", call("k-1", "add_todo", "Only once"))
try:
call("k-1", "add_todo", "Changed")
except Conflict as exc:
print("changed ->", exc)
print("todos ->", call(None, "list_todos"))
print("receipts ->", conn.execute("SELECT COUNT(*) FROM commands").fetchone()[0])
# Expected output:
# first call -> 1
# same again -> 1
# changed -> Idempotency key was already used for different input
# todos -> ['Only once']
# receipts -> 1
Pick a service you own that stores per-request results: an idempotency table, a request log, a job ledger. Measure it: total rows, total bytes, and the five largest stored results with the operation that produced each. For every operation in the top five, decide whether it actually mutates state. Write down the retention you would set, and what a client must do if its key falls outside that window.
Hint
In SQLite, SELECT length(result) FROM … ORDER BY 1 DESC LIMIT 5 finds the giants quickly. Expect the biggest rows to be answers, not effects. Lists and histories are large, and they are exactly the calls that never needed remembering.
Progress
Progress is local-only — sign in to sync across devices.