算法艺术创作
algorithmic-art
通过代码和可调参数创作生成艺术与交互式视觉效果。
idempotency-enforcer【设计与创意】Designs client-supplied idempotency keys, deduplication storage, and replay semantics so at-least-once delivery and client retries return the first result instead of double-charging or double-shipping. Use when someone asks "how do I stop duplicate charges on retry", "add an Idempotency-Key header to this endpoint", "the queue redelivered a job and w…
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
Make the second arrival of any non-reversible write a no-op that replays the first result. Without this, every retrying client, queue redelivery, and impatient double-click is a potential duplicate charge, duplicate shipment, or duplicate email - and the failure only shows up in production, under load, as a customer complaint.
Steps 1-3 are design decisions; 4-7 are the runtime protocol. Do them in order - the storage decision (step 3) depends on the scope decision (step 2), and the replay rules (step 6) are meaningless until intent is persisted atomically (step 5).
Collect before designing. Label guesses as guesses.
Store a hash of the request body with the key. If the same key returns with a different body, respond 422 - that is a client bug, not a retry. Never execute it.
In one transaction: insert the key row behind the unique constraint with status=in_progress, perform the side effect, then persist the response and set status=succeeded. The side-effect write and the key write must commit together - no committed charge without a stored key, no stored key claiming success without the effect.
For external side effects that cannot share your transaction (a card charge): write your key row first, pass the provider's own idempotency key on the outbound call, and on crash reconcile via the provider's status lookup - never re-issue the call blind.
| Stored state | Response to a duplicate |
|---|---|
| succeeded | Replay the stored status + body verbatim, byte-for-byte |
| in_progress | 409 (or Retry-After) - never run the work twice concurrently |
| failed (definitive) | Replay the stored error; the client must send a new key to retry the operation |
| absent / expired | Treat as a first request |
Delete rows past TTL. Alert on rows stuck in_progress longer than your slowest legitimate operation (typically 60 seconds for a synchronous API) - each one is a crashed request needing reconciliation.
CREATE TABLE idempotency_keys (
tenant_id UUID NOT NULL,
endpoint TEXT NOT NULL,
key TEXT NOT NULL CHECK (length(key) <= 255),
request_hash TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'in_progress', -- in_progress|succeeded|failed
response_code INT,
response_body JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
expires_at TIMESTAMPTZ NOT NULL DEFAULT now() + interval '24 hours',
PRIMARY KEY (tenant_id, endpoint, key)
);
// Express-style middleware sketch - the shape, not a library
async function idempotency(req, res, next) {
const key = req.header('Idempotency-Key');
if (!key) return res.status(400).json({ error: 'Idempotency-Key required' });
const hash = sha256(req.rawBody);
const row = await db.tryInsertKey(req.tenantId, req.route.path, key, hash); // INSERT .. ON CONFLICT DO NOTHING RETURNING *
if (row.inserted) return next(); // first arrival: handler runs, then persists response + status
if (row.request_hash !== hash) return res.status(422).json({ error: 'key reused with different body' });
if (row.status === 'in_progress') return res.status(409).set('Retry-After', '2').end();
return res.status(row.response_code).json(row.response_body); // replay
}
Produce an idempotency design doc containing: the list of protected endpoints, the key scope tuple, the storage choice with TTL and sweep schedule, the state/replay table, the body-hash conflict rule, and the reconciliation path for external side effects.
Inbound webhook dedup keyed on provider event IDs belongs to webhook-receiver-hardener. Client-side retry and backoff policy belongs to rate-limit-handler. If duplicates cross a service boundary you are extracting, the consistency design belongs with monolith-decomposer.