FA-101 / Distributed coordination / Open access
An aborted transaction publishes an external event · case 01
An event escapes before its associated business state commits, or committed events disappear before dispatch.
ROOT CAUSE
Publication is coupled to in-progress transaction state rather than a durable committed outbox.
VERIFIED REPAIR
Commit the staged state and event together, then dispatch undelivered committed outbox entries.
Unsuccessful approach: Waiting until dispatch but retaining only the latest transaction's event loses earlier committed work.
Case contract
Actions are ['stage',ID,value], ['commit'], ['rollback'], or ['dispatch']. Stage replaces an uncommitted pending write; commit atomically stores its value and appends its event; rollback discards pending work. Dispatch publishes each locally pending committed event once. IDs are unique per committed transaction. Return [business value,published event IDs]. No transport crash or exactly-once external-delivery guarantee is modeled.
Why this case matters
Models the database-to-message-broker boundary and why a relay needs a committed durable outbox, while explicitly excluding the separate problem of duplicate deliveries after relay crashes.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(actions):
pending, business, published = None, None, []
for action in actions:
if action[0] == 'stage':
pending = action[1:]
published.append(pending[0])
elif action[0] == 'commit' and pending is not None:
business = pending[1]
pending = None
elif action[0] == 'rollback':
pending = None
return [business, published]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('rollback cannot publish', solve([['stage', 'a', N], ['rollback'], ['dispatch']]), [None, []])
check('commit before publication', solve([['stage', 'a', N], ['dispatch']]), [None, []])
check('all committed events survive delay', solve([['stage', 'a', N], ['commit'], ['stage', 'b', N+1], ['commit'], ['dispatch']]), [N+1, ['a', 'b']])
check('single committed event', solve([['stage', 'a', N], ['commit'], ['dispatch']]), [N, ['a']])
check('repeat dispatch does not resend', solve([['stage', 'a', N], ['commit'], ['dispatch'], ['dispatch']]), [N, ['a']])
check('rollback preserves prior outbox', solve([['stage', 'a', N], ['commit'], ['stage', 'b', N+1], ['rollback'], ['dispatch']]), [N, ['a']])
check('empty transaction stream', solve([]), [None, []])
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| rollback cannot publish | [None, ['a']] | [None, []] | Failed |
| commit before publication | [None, ['a']] | [None, []] | Failed |
| all committed events survive delay | [2, ['a', 'b']] | [2, ['a', 'b']] | Passed |
| single committed event | [1, ['a']] | [1, ['a']] | Passed |
| repeat dispatch does not resend | [1, ['a']] | [1, ['a']] | Passed |
| rollback preserves prior outbox | [1, ['a', 'b']] | [1, ['a']] | Failed |
| empty transaction stream | [None, []] | [None, []] | Passed |
SHA-256 / e2eaf6cfb4857b60478876ef9e05381055cefa3ea899779364b94cf3707c019b
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(actions):
pending, business, latest, published = None, None, None, []
for action in actions:
if action[0] == 'stage':
pending = action[1:]
elif action[0] == 'commit' and pending is not None:
business, latest = pending[1], pending[0]
pending = None
elif action[0] == 'rollback':
pending = None
elif action[0] == 'dispatch' and latest is not None:
published.append(latest)
latest = None
return [business, published]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('rollback cannot publish', solve([['stage', 'a', N], ['rollback'], ['dispatch']]), [None, []])
check('commit before publication', solve([['stage', 'a', N], ['dispatch']]), [None, []])
check('all committed events survive delay', solve([['stage', 'a', N], ['commit'], ['stage', 'b', N+1], ['commit'], ['dispatch']]), [N+1, ['a', 'b']])
check('single committed event', solve([['stage', 'a', N], ['commit'], ['dispatch']]), [N, ['a']])
check('repeat dispatch does not resend', solve([['stage', 'a', N], ['commit'], ['dispatch'], ['dispatch']]), [N, ['a']])
check('rollback preserves prior outbox', solve([['stage', 'a', N], ['commit'], ['stage', 'b', N+1], ['rollback'], ['dispatch']]), [N, ['a']])
check('empty transaction stream', solve([]), [None, []])
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| rollback cannot publish | [None, []] | [None, []] | Passed |
| commit before publication | [None, []] | [None, []] | Passed |
| all committed events survive delay | [2, ['b']] | [2, ['a', 'b']] | Failed |
| single committed event | [1, ['a']] | [1, ['a']] | Passed |
| repeat dispatch does not resend | [1, ['a']] | [1, ['a']] | Passed |
| rollback preserves prior outbox | [1, ['a']] | [1, ['a']] | Passed |
| empty transaction stream | [None, []] | [None, []] | Passed |
SHA-256 / 13b47db6a04e5f5fe92b0aafd2ee16bf958f52ceccdf4c688bb8ace86d5f6463
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(actions):
pending, business, outbox, published = None, None, [], []
for action in actions:
if action[0] == 'stage':
pending = action[1:]
elif action[0] == 'commit' and pending is not None:
business = pending[1]
outbox.append(pending[0])
pending = None
elif action[0] == 'rollback':
pending = None
elif action[0] == 'dispatch':
published.extend(outbox)
outbox = []
return [business, published]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('rollback cannot publish', solve([['stage', 'a', N], ['rollback'], ['dispatch']]), [None, []])
check('commit before publication', solve([['stage', 'a', N], ['dispatch']]), [None, []])
check('all committed events survive delay', solve([['stage', 'a', N], ['commit'], ['stage', 'b', N+1], ['commit'], ['dispatch']]), [N+1, ['a', 'b']])
check('single committed event', solve([['stage', 'a', N], ['commit'], ['dispatch']]), [N, ['a']])
check('repeat dispatch does not resend', solve([['stage', 'a', N], ['commit'], ['dispatch'], ['dispatch']]), [N, ['a']])
check('rollback preserves prior outbox', solve([['stage', 'a', N], ['commit'], ['stage', 'b', N+1], ['rollback'], ['dispatch']]), [N, ['a']])
check('empty transaction stream', solve([]), [None, []])
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| rollback cannot publish | [None, []] | [None, []] | Passed |
| commit before publication | [None, []] | [None, []] | Passed |
| all committed events survive delay | [2, ['a', 'b']] | [2, ['a', 'b']] | Passed |
| single committed event | [1, ['a']] | [1, ['a']] | Passed |
| repeat dispatch does not resend | [1, ['a']] | [1, ['a']] | Passed |
| rollback preserves prior outbox | [1, ['a']] | [1, ['a']] | Passed |
| empty transaction stream | [None, []] | [None, []] | Passed |
SHA-256 / 7ecc29ca30dfff0bd15f1b6bdb77bc49947185d74efe7e7c2eba96a98398a088
Verification & scope
This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.
Observations recorded using Python 3.12.14 at 2026-09-29T14:36:50.453163+00:00.
Case digest / e23424818803346e4159b7f3441d8e195651337bf05dcd679eec84b82e066437