Skip to main content
ZombieMutex

30 results · ↑ ↓ navigate · Enter open · Esc close

All writing
November 6, 20263 min read

Idempotency under 100 competing requests: a reproducible in-process experiment

I scheduled 100 calls against one in-process idempotency key. The side-effect callback ran once; a real crash or a second worker is still untested.

By

Share

Send this article to someone or copy its link.

Suggest a correction

Reader tools

3 min remaining · 0% of article

Select a passage and choose Highlight selection to save it. Local browser storage only. Shift+F toggles focus.

I put 100 competing requests through a local idempotency handler to examine a specific failure: multiple callers receiving a timeout or ambiguous result for one logical write. The questions are whether one operation wins, whether duplicate payloads get the original result, and whether a reused key with different data is rejected.

I built a small Node.js harness around a tenant-scoped idempotency key and a shared pending Promise. I scheduled 100 calls, then tested the less obvious cases: different request bodies, separate tenants, expired keys, and an uncertain error. The side-effect callback executed once for the identical requests. A second worker or a process crash is a different problem; I haven't tested those here.

Setup

The implementation accepts a tenant ID, one operation key, exact request bytes, a retention period, and a callback representing the side effect. It computes a SHA-256 fingerprint of the request bytes. For the same tenant/key/fingerprint, every caller receives the first execution's Promise rather than invoking another callback.

The key detail is ordering: the registry inserts the pending Promise in its map before the asynchronous callback starts. Within one process, there is no second window for a fresh caller to claim the same key.

Run it

I ran npm test against the local reference project. The source repository is private, so the assertion below is shown for inspection rather than presented as a published repository anyone can clone.

The main test schedules 100 calls for the same logical request:

typescript
const results = await Promise.all(
  Array.from({length: 100}, () =>
    registry.execute(
      {tenant: "t1", key: "operation-001", requestBytes: request},
      async () => { effects++; return 201; }
    )
  )
);
assert.equal(effects, 1);

The excerpt below is from the local test. I've left the key and request structure in place so you can see what the assertion checks.

Observed local outcomes

Test caseVerified result
100 concurrently scheduled identical callsSide-effect callback executed once
Same key, different request bytesConflict rejected
Same operation key in two different tenantsSeparate executions allowed
Failure with an uncertain commitThe failure is retained for later calls during the retention window
Key reused after expiryA later execution becomes possible
Invalid key or TTLRejected before dispatch

All six local tests passed in Node.js 24.21. These outcomes describe logic, not network throughput, tail latency, crash consistency or DB isolation guarantees.

Where the simple model fails

The local map is neither shared between service instances nor persistent across restarts. Two workers using different maps will both execute the callback. A process crash after an external payment is accepted but before a durable result is recorded may also leave an ambiguous outcome.

A production design needs a transactionally enforced claim for the tenant-scoped operation key, a stable request fingerprint, an explicit in-progress state, a durable terminal outcome or reconciliation status, a documented retention policy, and an outbox or downstream idempotency support for external effects.

The private reference project contains an illustrative PostgreSQL table schema. It is intentionally not presented as a complete claim-and-commit transaction or a tested real database implementation.

The next useful experiment

Stand up PostgreSQL, send two requests in independent worker processes, and inject failure after the business mutation but before response persistence. Check whether the system can recover an unambiguous operation status without duplicating effects. That would provide evidence beyond the current in-memory reference.

For a related failure mode, read the HTTP timeout and idempotency explanation.


I used AI assistance on this small test project. The local assertions don't prove exactly-once execution across deployed services.

Related technical notes with overlapping topics.

Discuss a passage

Select a paragraph above, copy it with its article link, then paste into the discussion. Don't include private data.

Jump to discussion ↓

Public conversation / GitHub Discussions

Discuss this article

Read and reply using GitHub Discussions. A GitHub account is needed to post. Comments are public and subject to GitHub moderation.

For privacy, the external giscus widget does not load until you choose to open it. Opening it connects your browser to giscus and GitHub. Do not post credentials or private project data.