Hacker DirectoryHacker Directory

WEB / WEB-19.S01

Races, transactions, and idempotency

Inspect a public-note revision and retry result at owner boundaries; keep stale decisions, commit, and caller knowledge separate.

CORE · RED · BLUE · ARTIFACT · PRACTICE

1. Core · Mechanism

Read every scenario without motion

Commit result

  1. Operation intent

    The caller prepares a public-note operation with declared purpose and an inert key alias; the key is not a credential.

    Caller / application
    Known public note operation
    Selected service
    Selected request scope
    SQLite note owner
    Tea note/version1 committed
    Scoped result record
    No saved result
  2. Normalize scope

    The service matches caller/purpose/key and note/title parameters to the selected result contract.

    Caller / application
    Known public note operation
    Selected service
    Scope+parameters selected
    SQLite note owner
    Tea note/version1 committed
    Scoped result record
    No saved result
  3. Pending mutation

    The known rename is pending in the transaction; an independent observer still sees the previously committed note.

    Caller / application
    Known public note operation
    Selected service
    Scope+parameters selected
    SQLite note owner
    Pending Updated note/version2
    Scoped result record
    No saved result
  4. Commit result

    Saved parameters/result and note mutation commit in one SQL transaction; external effects are excluded.

    Caller / application
    Known public note operation
    Selected service
    Scope+parameters selected
    SQLite note owner
    Updated note/version2 committed
    Scoped result record
    Committed result Updated note/version2
  5. Result known

    The caller learns noteId/title/version at selected local completion; real HTTP arrival is not claimed.

    Caller / application
    Updated note/version2; mutationCount1
    Selected service
    Scope+parameters selected
    SQLite note owner
    Updated note/version2 committed
    Scoped result record
    Committed result Updated note/version2

Stale condition

  1. Operation intent

    The caller prepares a public-note operation with declared purpose and an inert key alias; the key is not a credential.

    Caller / application
    Known public note operation
    Selected service
    Selected request scope
    SQLite note owner
    Tea note/version1 committed
    Scoped result record
    No saved result
  2. Read version1

    The application reads revision1 without a guarantee that state will be unchanged at later use.

    Caller / application
    Read expectation1
    Selected service
    Selected request scope
    SQLite note owner
    Tea note/version1 committed
    Scoped result record
    No saved result
  3. Ordinary update

    An ordinary authored independent change makes owner Coffee note/version2; no concurrent attack workflow executes.

    Caller / application
    Read expectation1
    Selected service
    Selected request scope
    SQLite note owner
    Coffee note/version2
    Scoped result record
    No saved result
  4. Condition selected

    The service binds expected1 to the authoritative UPDATE condition instead of relying on a UI check.

    Caller / application
    Read expectation1
    Selected service
    Expected1/current2 condition
    SQLite note owner
    Coffee note/version2
    Scoped result record
    No saved result
  5. Reject stale

    The guarded statement affects zero rows and preserves current state; no bypass or vulnerable branch exists.

    Caller / application
    Read expectation1
    Selected service
    Expected1/current2 condition
    SQLite note owner
    Coffee note/version2; changedRows0
    Scoped result record
    No saved result
  6. Conflict outcome

    The caller receives the selected conflict; stale work is not counted as a business mutation.

    Caller / application
    Conflict; current state preserved
    Selected service
    Expected1/current2 condition
    SQLite note owner
    Coffee note/version2; changedRows0
    Scoped result record
    No saved result

Unknown + retry

  1. Operation intent

    The caller prepares a public-note operation with declared purpose and an inert key alias; the key is not a credential.

    Caller / application
    Known public note operation
    Selected service
    Selected request scope
    SQLite note owner
    Tea note/version1 committed
    Scoped result record
    No saved result
  2. Normalize scope

    The service matches caller/purpose/key and note/title parameters to the selected result contract.

    Caller / application
    Known public note operation
    Selected service
    Scope+parameters selected
    SQLite note owner
    Tea note/version1 committed
    Scoped result record
    No saved result
  3. Pending mutation

    The known rename is pending in the transaction; an independent observer still sees the previously committed note.

    Caller / application
    Known public note operation
    Selected service
    Scope+parameters selected
    SQLite note owner
    Pending Updated note/version2
    Scoped result record
    No saved result
  4. Commit result

    Saved parameters/result and note mutation commit in one SQL transaction; external effects are excluded.

    Caller / application
    Known public note operation
    Selected service
    Scope+parameters selected
    SQLite note owner
    Updated note/version2 committed
    Scoped result record
    Committed result Updated note/version2
  5. Unknown response

    An injected caller-knowledge event leaves first completion unknown; committed data is not assumed rolled back.

    Caller / application
    First result knowledge unknown
    Selected service
    Scope+parameters selected
    SQLite note owner
    Updated note/version2 committed
    Scoped result record
    Committed result Updated note/version2
  6. Same-scope retry

    A finite retry with the same purpose/key and parameters looks up the saved result; no timeout traffic executes.

    Caller / application
    First result knowledge unknown
    Selected service
    Same scope+parameters retry
    SQLite note owner
    Updated note/version2 committed
    Scoped result record
    Committed result Updated note/version2
  7. Reuse saved result

    The stored result is found again; no second note mutation occurs and record count stays1.

    Caller / application
    First result knowledge unknown
    Selected service
    Same scope+parameters retry
    SQLite note owner
    Updated note/version2 committed
    Scoped result record
    Result reused; mutationCount1
  8. Result known

    The caller learns noteId/title/version at selected local completion; real HTTP arrival is not claimed.

    Caller / application
    Updated note/version2; mutationCount1
    Selected service
    Same scope+parameters retry
    SQLite note owner
    Updated note/version2 committed
    Scoped result record
    Result reused; mutationCount1

Rollback result

  1. Operation intent

    The caller prepares a public-note operation with declared purpose and an inert key alias; the key is not a credential.

    Caller / application
    Known public note operation
    Selected service
    Selected request scope
    SQLite note owner
    Tea note/version1 committed
    Scoped result record
    No saved result
  2. Normalize scope

    The service matches caller/purpose/key and note/title parameters to the selected result contract.

    Caller / application
    Known public note operation
    Selected service
    Scope+parameters selected
    SQLite note owner
    Tea note/version1 committed
    Scoped result record
    No saved result
  3. Pending mutation

    The known rename is pending in the transaction; an independent observer still sees the previously committed note.

    Caller / application
    Known public note operation
    Selected service
    Scope+parameters selected
    SQLite note owner
    Pending Updated note/version2
    Scoped result record
    No saved result
  4. Result constraint

    A harmless NOT NULL saved-result candidate fails; explicit rollback clears mutation and record together.

    Caller / application
    Known public note operation
    Selected service
    Scope+parameters selected
    SQLite note owner
    Pending Updated note/version2
    Scoped result record
    Constraint failure; no record
  5. Explicit rollback

    The database preserves previous Tea note/version1; no operation-result record committed.

    Caller / application
    Known public note operation
    Selected service
    Scope+parameters selected
    SQLite note owner
    Tea note/version1 restored
    Scoped result record
    Result record0
  6. Failure known

    The caller knows the selected failure; this is not the unknown-response-aftercommit outcome.

    Caller / application
    Failed; rollback observed
    Selected service
    Scope+parameters selected
    SQLite note owner
    Tea note/version1 restored
    Scoped result record
    Result record0

ACTORS · STATE · DATA FLOW

A read does not promise unchanged state at use

An application reads a public note at revision=1; the owner can change it before use. TOCTOU describes that knowledge gap between a check and use. Races are not all the same: order-dependent behavior, lost updates, and invariant violations can differ. This lesson races no vulnerable endpoint. It shows a guarded update rejecting old expectation=1 after an ordinary authored revision=2 transition.

The control evaluates an operation condition at the selected authoritative boundary, rather than repeating a UI check. The fixture binds note ID and expected revision in its UPDATE. A mismatch affects zero rows and preserves current revision/title. SQLite transaction and isolation settings define the selected mechanism scope. Starting a transaction alone does not fix a wrong predicate; explicitly define the invariant rows, fields, and operation.

A separate connection reads completed transactions; under the selected SQLite settings an uncommitted update is hidden from its observer. Visibility differs from predicate correctness and HTTP receipt. A sequential native test observes current revision and affected-row count through independent SELECTs. It uses no real threads, timing harness, concurrent traffic, or third-party target: evidence covers finite ordinary events.

Retry identity includes result and purpose

If a caller receives no completion response, it cannot infer that nothing executed. Here note rename commits while response knowledge stays unknown; a retry with the same request alias reuses the committed result. The alias is no credential or bearer token. Application idempotency scope includes caller alias, operation purpose, and key, with normalized note ID/title parameters. The same scope/key with different parameters yields a selected conflict rather than another operation result.

The idempotency record and business mutation commit in one SQLite transaction; a harmless NOT NULL failure rolls both back. A record alone is no database guarantee: the server defines meaning, validation, retention, and key reuse. Stripe saved status/body and parameter comparison belong to its own API contract; pruning or failure-result retention can differ in other products. Our finite ledger recommends no indefinite production retention.

HTTP safe/idempotent methods do not implement this application retry contract. Endpoint names, a key header, or a database transaction do not automatically provide end-to-end exactly-once effects. External emails, files, and network effects stay outside the selected SQL transaction. AWS retry guidance distinguishes caller intent and parameter mismatch; no cloud API is called here. Unknown-response is an injected knowledge event, not an actual socket timeout.

2. Red · Trust & Impact

Read every scenario without motion

Old revision

  1. The caller first read version 1

    ReadVersion=1 identifies the copy used to form the rename intent. It does not establish that the owner still holds version 1.

    Rename intent
    readVersion=1
    Record owner
    Current revision not read yet
    Admission contract
    Update decision pending
  2. The record is version 2 before use

    CurrentBeforeUse=2 and the title is Coffee note. The earlier read differs from the owner’s current revision.

    Rename intent
    expectedVersion=1
    Record owner
    Coffee note / currentVersion=2
    Admission contract
    Revision condition required
  3. Decision and write share one statement

    The fixed UPDATE evaluates the version condition alongside id. ExpectedVersion=1 does not match the currentVersion=2 record, so the write is not admitted.

    Rename intent
    expectedVersion=1
    Record owner
    affectedRowsOnStale=0
    Admission contract
    revision mismatch
  4. Coffee note is preserved

    The after record remains Coffee note at version=2. AffectedRows=0 shows the guarded update was not applied; it does not erase the earlier read.

    Rename intent
    readVersion=1
    Record owner
    after: Coffee note / version=2
    Admission contract
    stale update not applied
  5. Parameter binding does not make this decision

    Title and noteId are bound as data; the revision guard separately constrains applicability to the current record. Data/query separation and freshness answer different questions.

    Rename intent
    parameters remain data
    Record owner
    affectedRowsOnStale=0
    Admission contract
    Binding != revision agreement
  6. Report the scoped result

    The verified result is that this update with an old revision condition preserved the record. It does not establish the same concurrency guarantee for every distributed writer or store.

    Rename intent
    readVersion=1
    Record owner
    Coffee note / version=2
    Admission contract
    guarded update rejected

Parameter change

  1. Establish the operation scope

    Caller-demo, rename-public-note purpose and request-a key are evaluated together. Knowing the key alone does not prove identity or permission.

    Rename intent
    caller-demo / rename-public-note / request-a
    Record owner
    Saved result not inspected yet
    Admission contract
    keyIsCredential=false
  2. The first operation has a saved result

    The normal result records tea as Updated note at version=2. It belongs to the first operation’s parameters within the selected scope.

    Rename intent
    title=Updated note
    Record owner
    saved result: Updated note / version=2
    Admission contract
    Original parameters bound
  3. The same key carries a different title

    The new candidate requests Different note title while sameScopeKey=true. The same key label does not make different parameters the same operation.

    Rename intent
    newTitle=Different note title
    Record owner
    saved result unchanged
    Admission contract
    sameScopeKey=true
  4. A parameter conflict decision

    The outcome is parameter-conflict. The application does not apply a candidate with different parameters as a new rename under the same scope and key.

    Rename intent
    new parameters differ
    Record owner
    existingResultPreserved=true
    Admission contract
    outcome=parameter-conflict
  5. The previous result was not erased

    ExistingResultPreserved=true records preservation of the prior outcome. Conflict does not mean the first operation rolled back or its result was erased.

    Rename intent
    parameter conflict
    Record owner
    saved result preserved
    Admission contract
    No second rename admitted
  6. New work needs its own contract

    Another purpose or different parameters can define new work; its permission and identity scope need separate evaluation. An idempotency key cannot substitute for authorization.

    Rename intent
    separatePurposeIsSeparateScope=true
    Record owner
    existing result preserved
    Admission contract
    keyIsCredential=false

ASSUMPTION · BOUNDARY · CONTROL

An earlier check has limited knowledge scope

The controlled ledger separates readVersion1 from currentVersion2 at use. The condition is treating stale knowledge as current state; a possible impact is an old decision overwriting a newer title. This lesson runs only the correct guarded condition, observing zero affected rows and preserved state. It provides no bypass, vulnerability reproduction, or race-winning timing instructions. Inspection finds the authoritative statement that evaluates expected revision.

A transaction declaration does not prove the application invariant is covered. Another row, external effect, or owner can exceed the SQLite boundary. The selected transaction covers mutation, result, and record together; independent reads see no uncommitted state. Generalizing to every isolation level or distributed topology is incorrect. Fixed statements and bound values here are not a query-injection lesson.

An unknown response is not a failed operation

Caller response loss and store rollback have different ledger states. Running committed work with a new alias can express another intent; using the same alias and parameters retrieves the stored result under the selected contract. Reusing an alias for another title or purpose needs a correctness decision. An idempotency key carries no browser identity or permission, so success is no authorization outcome.

A record with only a success flag may not identify the completed note/revision. The saved result includes noteId, title, and revision; mismatch preserves it. A sent flag for an external effect could describe another receipt outside SQL. Review tests exactly-once claims against outcome owners; this native work proves only ordinary local state transitions.

3. Blue · Secure development & defense

Read every scenario without motion

Unknown result

  1. The first result is unknown to the caller

    Before/after revisions and affectedRows come from a local SQLite statement and independent SELECT reads; caller UNKNOWN is a separately supplied scenario condition. CallerFirstKnowledge=unknown describes the caller’s missing knowledge. It is not evidence that the database did not commit.

    Caller observation
    callerFirstKnowledge=unknown
    Operation result
    Commit not inspected yet
    Evidence interpretation
    Outcome evidence pending
  2. Database commit was observed separately

    FirstCommitted=true accompanies a saved Updated note result at version=2. Caller uncertainty can coexist with a durable commit.

    Caller observation
    callerFirstKnowledge=unknown
    Operation result
    firstCommitted=true / result version=2
    Evidence interpretation
    Commit != caller receipt
  3. The retry carries the same parameters

    SameParameters=true and the same caller, purpose and key scope correlate the retry to the original operation. Similar titles alone do not establish that relation.

    Caller observation
    same scope / sameParameters=true
    Operation result
    saved result version=2
    Evidence interpretation
    Same operation correlated
  4. The saved result was reused

    ResultReused=true and mutationCount=1 show that the retry used the original outcome without a second rename.

    Caller observation
    retry correlated
    Operation result
    resultReused=true / mutationCount=1
    Evidence interpretation
    One mutation observed
  5. Do not classify unknown as failure

    The evidence shows the first operation committed and the retry created no additional effect. Earlier caller uncertainty cannot be rewritten as first-operation failure.

    Caller observation
    first caller knowledge unknown
    Operation result
    firstCommitted=true / mutationCount=1
    Evidence interpretation
    Unknown knowledge != failed operation
  6. Keep the verified boundary visible

    This example reuses a saved result. External API effects do not join the SQLite transaction; a local record does not guarantee exactly-once behavior for the whole system.

    Caller observation
    same operation
    Operation result
    mutationCount=1
    Evidence interpretation
    externalEffectsAtomic=false

Known rollback

  1. Read the revision before the transaction

    Before/after revisions and affectedRows come from a local SQLite statement and independent SELECT reads; caller UNKNOWN is a separately supplied scenario condition. The rollback fixture has beforeVersion=1. Establish this starting point before comparing the same transaction’s outcome.

    Caller observation
    Caller outcome not known yet
    Operation result
    beforeVersion=1
    Evidence interpretation
    Transaction outcome pending
  2. The result record hit a constraint

    A harmless NOT NULL result candidate fails and explicit rollback follows. This is a transaction outcome, unlike merely absent caller observation.

    Caller observation
    Caller outcome evaluated separately
    Operation result
    constraint failure / explicit rollback
    Evidence interpretation
    Known rollback event
  3. Revision and result were inspected together

    AfterVersion=1 and resultRecords=0 show the prior revision preserved and no committed result from this transaction.

    Caller observation
    Caller outcome evaluated separately
    Operation result
    afterVersion=1 / resultRecords=0
    Evidence interpretation
    No committed result from this transaction
  4. This branch has rollback evidence

    Explicit rollback and the independent after record support a known unsuccessful transaction. FirstCommitted=true from the unknown-response branch cannot be copied here.

    Caller observation
    different branch: unknown response
    Operation result
    afterVersion=1 / resultRecords=0
    Evidence interpretation
    Known rollback != unknown commit
  5. Missing observation is a third state

    Without observations needed for commit or rollback, neither branch can be selected. Zero selector matches do not complete a missing transaction record.

    Caller observation
    Missing outcome remains unknown
    Operation result
    rollback fixture separately verified
    Evidence interpretation
    No branch borrowed for absent evidence
  6. Tie retry decisions to known state

    Evaluate a saved result for the same operation, account for absent durable results after known rollback, and require outcome evidence when state is unknown. Key possession makes none of these decisions alone.

    Caller observation
    keyIsCredential=false
    Operation result
    rollback resultRecords=0
    Evidence interpretation
    Result / rollback / unknown separate

IMPLEMENTATION · SECURITY CONTROL · VERIFICATION

Evaluate the invariant in an authoritative statement

Before/after revisions and affectedRows come from a local SQLite statement and independent SELECT reads; caller UNKNOWN is a separately supplied scenario condition. The invariant is that a title changes only when current revision equals expected revision. Benign expected1/current2 mismatch yields rowcount0 and leaves current title/revision unchanged. Independent SELECTs compare before and after state without assuming the UI copy improved. Transaction visibility and operation condition are different controls, together supporting the selected outcome.

The SQLite fixture uses explicit transaction control, an owned file, and two independent connections with recorded settings. It does not benchmark contention or timeouts. Predicates and markers cover known public-note inputs without real targets or secrets. A wider invariant requires its row set, locking/condition semantics, and failure handling to be defined using the chosen database documentation.

Commit mutation and saved result together

The scope tuple caller-demo / rename-public-note / request-a stores normalized tea +Updated note parameters. Normal response and same-parameter retry produce the same result with one business mutation. The key is an instructional alias, not entropy, production-retention, or authentication guidance. Native checks cover parameter conflict within a scope and a separate purpose namespace.

Rollback preserves the original revision and leaves no result record. Under UNKNOWN caller response, commit survives and retry finds the saved result. These cases may both trigger retry intent while having different known business states. Preserve that distinction; unobserved browser receipts or external effects retain their own UNKNOWN outcomes.

NOW APPLY IT TO THE CASE

Which evidence distinguishes an old revision, repeated operation and unknown outcome?

Duru wants to rename Atlas’s public tea note. The application separately checks a revision condition and an operation record bound to caller, purpose and key. A result can be committed even when it is unknown to the caller.

Read accepted, delivery and committed-effect records at their respective owners. Do not rename unknown caller outcome as business rollback.

Open the related case file ↗

READ THE EXAMPLE TOGETHER

Public note condition/retry result ledger

Select a field and examine what it supports and where the evidence ends.

Read the raw artifact and field explanations
{
  "invariant": {
    "noteId": "tea",
    "readVersion": 1,
    "currentBeforeUse": 2,
    "affectedRowsOnStale": 0,
    "after": {
      "title": "Coffee note",
      "version": 2
    },
    "statement": "UPDATE notes SET title=?,version=version+1 WHERE id=? AND version=?",
    "parameters": [
      "Updated note",
      "tea",
      1
    ]
  },
  "scope": {
    "callerAlias": "caller-demo",
    "purpose": "rename-public-note",
    "keyAlias": "request-a",
    "parameters": {
      "noteId": "tea",
      "title": "Updated note"
    },
    "keyIsCredential": false,
    "separatePurposeIsSeparateScope": true
  },
  "normal": {
    "result": {
      "noteId": "tea",
      "title": "Updated note",
      "version": 2
    },
    "mutationCount": 1,
    "resultRecords": 1
  },
  "retryUnknown": {
    "firstCommitted": true,
    "callerFirstKnowledge": "unknown",
    "sameParameters": true,
    "resultReused": true,
    "result": {
      "noteId": "tea",
      "title": "Updated note",
      "version": 2
    },
    "mutationCount": 1
  },
  "mismatch": {
    "sameScopeKey": true,
    "newTitle": "Different note title",
    "outcome": "parameter-conflict",
    "existingResultPreserved": true
  },
  "rollback": {
    "beforeVersion": 1,
    "afterVersion": 1,
    "resultRecords": 0,
    "reason": "benign NOT NULL result candidate; explicit rollback"
  },
  "limits": {
    "concurrentRequestsExecuted": false,
    "unknownResponse": "injected caller knowledge event, not socket loss",
    "externalEffectsAtomic": false,
    "universalExactlyOnce": false
  },
  "selector": {
    "language": "XPath",
    "xml": "<audit><revision expected=\"1\" current=\"2\" mutationAccepted=\"false\"/></audit>",
    "xpath": "count(/audit/revision[@expected != @current and @mutationAccepted='true'])",
    "expected": 0,
    "positiveFixtureXml": "<audit><revision expected=\"1\" current=\"2\" mutationAccepted=\"true\"/></audit>",
    "positiveExpected": 1,
    "scope": "Only authored protocol-decision observations, not product log field names or a live HTTP capture.",
    "missingFieldBehavior": "Missing observation is insufficient evidence; zero matches is not proof of correct runtime behavior."
  }
}
invariant
Fixed guarded SQL and observed stale rejection.
scope
Caller/purpose/key and normalized parameters.
retryUnknown/rollback
Preserved commit and rollback are distinct outcomes.
selector
Synthetic stale-mutation acceptance decision.

Selected own SQLite and finite sequential public-note operations. No vulnerable reproduction, concurrency harness, real network loss, or external-effect guarantee.

Two reality checks

Starting a transaction resolves all application races and invariants.

Isolation, predicates, and row scope are separate choices; a transaction declaration alone is no correct business condition.

No response means the operation rolled back.

Caller knowledge can stay unknown while a committed result is reused by scoped retry; rollback needs separate evidence.

YOUR TURN

Work through this story.

Predict the outcome, find the evidence boundary, and explain it in your own words.

The read used version=1, but current version=2 at use. UPDATE requires version=1. Is the new title written?

  1. Yes. Once read, the record is locked against every later state change.
  2. No. affectedRowsOnStale=0; Coffee note version=2 is retained.
Read the explained answer

No. affectedRowsOnStale=0; Coffee note version=2 is retained. — The version condition is reapplied at write time. An old read does not permit overwriting the current record.

The caller does not know the first result, but it committed. Why does a retry with the same scoped key and parameters not create a second mutation?

Selected lesson artifact fields; teaching example

retryUnknown.firstCommitted
true
retryUnknown.callerFirstKnowledge
unknown
retryUnknown.sameParameters
true
retryUnknown.resultReused
true
retryUnknown.mutationCount
1
  1. The previous result is reused; mutationCount stays one.
  2. An unknown caller result rolls back all earlier server writes.
Read the explained answer

The previous result is reused; mutationCount stays one. — callerFirstKnowledge=unknown is not absence of a server commit. Same-parameter dedupe returns the retained result without repeating the effect.

The same scope/key with a different title produces parameter-conflict. What boundaries apply to idempotency scope, transactions and real concurrency?

Read the explained answer

The key identifies the same operation within caller, purpose and parameter bindings; a changed title does not overwrite the retained result. The key is neither a credential nor authorization. A local transaction can protect the write and result record together; failed result creation needs explicit rollback. This sequential example measures neither parallel-request isolation nor external-effect atomicity and gives no universal exactly-once guarantee.

□ I bound the idempotency key to purpose/parameters rather than permission.

□ I inferred no concurrency or external-atomicity guarantee from local result reuse.

Open the application console · request / response / browser
Also open request, URL, cookie and DOM examples

Sources and scope