Skip to main content
Version: Current

IdempotencyStore

@rotorsoft/act-root


@rotorsoft/act-root / act-ops/src/idempotency / IdempotencyStore

Interface: IdempotencyStore

Defined in: libs/act-ops/src/idempotency/port.ts:26

Receiver-side idempotency contract: atomically claim a key as processed-by-this-caller, report whether the claim succeeded.

The verb mirrors "@rotorsoft/act".Store.claim โ€” both are atomic acquire-or-fail operations on a contested resource. There, competing workers race for the right to drain a stream; here, competing requests race for the right to be processed as the canonical first-time delivery for an Idempotency-Key. One caller wins; the others see the claim has already been made and treat their request as a duplicate.

Not a Cache. In this codebase Cache means "rebuildable from a source of truth" (e.g. snapshot cache). Dedup state is authoritative โ€” losing it allows duplicate side effects, not just a rebuild. Hence Store. Implementations should preserve records for at least the sender's full retry envelope; see external integration for TTL sizing guidance (the matching helper lands in #747).

Implementations may be sync (in-memory) or async (durable adapters backed by Postgres, Redis, etc.). The middleware that consumes this port (#744) awaits unconditionally, so either shape composes cleanly with framework-agnostic receivers.

Methodsโ€‹

claim()โ€‹

claim(key, now?): boolean | Promise<boolean>

Defined in: libs/act-ops/src/idempotency/port.ts:47

Atomically claim key for this caller. Returns true if the caller won the claim (the key was fresh and is now recorded), false if another caller already claimed it โ€” the request should be treated as a duplicate.

The claim is tentative until the caller confirms the outcome with commit or release. A tentative claim still dedups a concurrent duplicate that arrives while the handler is in flight โ€” the second caller sees false and serializes behind the first โ€” but it is not durable across the caller's own retries until committed. This two-phase shape is what stops a transient handler failure from permanently dropping a delivery: the sender retries with the same key, and because the failed attempt released (or never committed) the claim, the retry re-processes instead of being deduped into a silent success.

now is exposed for tests; production callers should leave it undefined so wall-clock is used.

Parametersโ€‹

keyโ€‹

string

now?โ€‹

number

Returnsโ€‹

boolean | Promise<boolean>


commit()โ€‹

commit(key, now?): void | Promise<void>

Defined in: libs/act-ops/src/idempotency/port.ts:60

Promote a tentative claim to a durable record so every later delivery of the same key dedups. Call this once the business handler has succeeded. Committing a key that was never claimed records it too, so a durable adapter that lost the tentative reservation (process crash between claim and commit) still lands in a consistent "seen" state.

now is exposed for tests; production callers should leave it undefined so wall-clock is used.

Parametersโ€‹

keyโ€‹

string

now?โ€‹

number

Returnsโ€‹

void | Promise<void>


release()โ€‹

release(key): void | Promise<void>

Defined in: libs/act-ops/src/idempotency/port.ts:69

Drop a tentative claim so a retry re-processes. Call this when the business handler failed transiently and the delivery should be re-attempted under the same key. Releasing a key that has already been committed is a no-op โ€” a successful delivery stays deduped even if a late release arrives.

Parametersโ€‹

keyโ€‹

string

Returnsโ€‹

void | Promise<void>