Fastify request lifecycle and Redis idempotency store: duplicate POST handling at integration boundary
0 reputation · 25 Feb 2022, 15:52 UTC
0 reputation · 25 Feb 2022, 15:52 UTC
Goal is to prevent duplicate writes when clients retry POST requests that pass through Fastify routes to a downstream data store. Fastify provides no built-in idempotency or retry deduplication, so the integration boundary between the request lifecycle hooks/plugins and an external dedup store must be defined by application code.
Encapsulation allows scoping deduplication per route context, and hook ordering determines when an Idempotency-Key can be reliably read from headers versus body. Proxy style plugins do not add idempotency semantics, so retries are re-executed downstream unless the Fastify layer enforces it.
One unresolved decision is the handling of concurrent duplicates while the original request is still in-flight. Fastify does not provide a request-coalescing primitive, leaving the policy to the plugin author.
Should duplicates arriving during in-flight processing be rejected, wait for completion, or be allowed to proceed? How does encapsulation affect visibility of a shared dedup store across sibling route prefixes? Does placement of the check in onRequest versus preHandler change reliability when body parsing is involved?
34775 reputation · 25 Feb 2022, 22:41 UTC
In-flight duplicates: do not allow them to proceed and do not hard-reject them. Claim the key with an atomic Redis SET NX PX in-flight marker and, if the claim fails, wait for completion and replay the stored response. Replaying matches Stripe-style idempotency and avoids 409s for legitimate retries. Hard rejection loses the client outcome; allowing proceed defeats deduplication.
Shared dedup store visibility: register the Redis idempotency plugin at root scope or with fastify-plugin so the decorator is visible to all prefixes. A plugin registered inside one prefixed context is not visible to sibling prefixes due to Fastify encapsulation.
Hook placement for key read: onRequest runs before body parsing. preHandler runs after body parsing. If Idempotency-Key is header-only, onRequest is earliest safe. If the key can be in the body, you must read it in preValidation or preHandler.
One diagnostic that changes the recommendation: is the Idempotency-Key guaranteed to be in the header, or can it also be carried in the parsed body? If body-carried, the check must move from onRequest to preHandler/preValidation.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.