A user can click again after losing a save response even when the server already committed the first request. The second attempt should recover the result of that intent rather than create another order.

This synthetic local transaction lab goes beyond the earlier event-ID example. It does not promise exactly-once execution of external payments or orders.

Distinguish a repeated click from another business action

Disabling a button helps with accidental clicks, but does not control refreshes, network retries or requests from another window. The lab accepts a request identifier scoped to a company and user. A matching string from another person must not expose the earlier person’s result.

A document identifier is not the request identifier: the same document can legitimately receive another quantity edit later. The request names one intent, while the document names a changing business object. The code validates a short identifier format; generating and retaining that key belongs to the client integration contract.

The same key must represent the same command

Replaying solely by key could make a different quantity look successfully applied. I serialize the action, document, expected version, reason and quantity in a deterministic JSON representation and compute a SHA-256 fingerprint. Identical key and fingerprint replay the committed result. Different content under the same key returns request_key_reused.

The fingerprint compares content. It does not authenticate a requester or encrypt private values. A real server must first establish identity and company scope. Actor is trusted test context, not a role field accepted from a browser. Reusing a response does not remove the need to enforce the current access boundary.

Do not leave a gap between the mutation and its receipt

Saving the document and storing the request result afterwards leaves a failure window. A retry might not find the earlier success and mutate again. The SQLite experiment commits the document, event, queued follow-up and successful request result together. Injected failure verifies that no partial subset remains.

A separate test runs identical requests concurrently through two connections. The first write transaction records success; the next reads that completed result. Both responses indicate success, but exactly one is marked replayed and only one event and queue row exist. Two button attempts are distinguished from two business changes.

Recover a committed result after losing the response

The example reopens the database and repeats an already successful request. Although its expected document version is now old, the stored matching request result is returned without another mutation. Returning only a version conflict would obscure the earlier successful action. A request’s result and the document’s latest state are different queries.

The replayed document snapshot represents that request’s completion. If later changes occurred, fetch the current document separately. Failed uncommitted operations are not stored as successful results here. The lab also does not implement recovery of an in-flight remote operation or request-key expiry. Retention and key reuse require an explicit production policy.

Local idempotency is not every external guarantee

The package covers mutations inside one local database. Sending a document to another provider and losing its response requires reconciliation with that provider. I do not expand this lab into a claim of exactly-once execution across networks. It is still useful to state precisely what was committed and which result can be recovered.

Tests verify concurrent duplicates, changed content under the same key, replay after reopening and rollback on injected failure. Stored results have no automatic expiry, so long-running adoption needs a retention policy. The code and recorded runs expose that boundary for readers adapting the method to another workflow and storage system.