[{"data":1,"prerenderedAt":560},["ShallowReactive",2],{"learn-concept-/learn/idempotency":3},{"id":4,"title":5,"body":6,"cardImage":504,"cardImageAlt":505,"date":506,"description":507,"domain":508,"domainKey":509,"extension":510,"featured":511,"fullName":5,"interaction":12,"maturity":512,"mentalModel":513,"meta":514,"navigation":515,"neighbors":516,"ogImage":542,"path":543,"published":515,"robots":542,"seo":544,"shortName":5,"sitemap":545,"socialImage":542,"socialImageAlt":542,"sources":546,"stem":553,"tags":554,"translationKey":12,"updated":506,"__hash__":559},"learnEn/learn/idempotency.md","Idempotency",{"type":7,"value":8,"toc":481},"minimal",[9,13,22,25,31,36,44,66,69,73,76,86,93,108,112,117,120,126,129,135,138,142,145,151,154,181,185,188,194,197,200,205,209,213,216,220,223,227,234,238,241,245,248,333,336,340,378,382,403,407,424,428,445,449],[10,11,5],"h1",{"id":12},"idempotency",[14,15,16,17,21],"p",{},"Idempotency solves the hardest kind of distributed-system failure: not a clear success or failure, but an ",[18,19,20],"strong",{},"unknown outcome",".",[14,23,24],{},"A client sends a payment request. The server charges the card, but the response disappears on the network. The client sees a timeout. If it gives up, a payment that should complete may look failed. If it retries blindly, the customer may be charged twice.",[14,26,27,28],{},"An idempotent contract separates recovery from duplication: ",[18,29,30],{},"the request may be attempted again, but the same business intent does not create the business effect again.",[32,33,35],"h2",{"id":34},"a-restaurant-ticket","A restaurant ticket",[14,37,38,39,43],{},"Imagine handing ticket ",[40,41,42],"code",{},"A-842"," to a kitchen and hearing no confirmation. You hand over the same ticket again.",[45,46,47,51,57,60],"ul",{},[48,49,50],"li",{},"Without an order number, the kitchen may cook two meals.",[48,52,53,54,56],{},"With a stable order number, it finds ",[40,55,42],{}," and returns the existing order status.",[48,58,59],{},"A genuinely new meal needs a new number.",[48,61,62,63,65],{},"Reusing ",[40,64,42],{}," with a different dish should be rejected, not guessed.",[14,67,68],{},"The ticket is only an identifier. The kitchen still needs a reliable ledger, and recording the number cannot be separated from accepting the order by an unsafe gap.",[32,70,72],{"id":71},"mathematical-and-system-meaning","Mathematical and system meaning",[14,74,75],{},"An idempotent function satisfies:",[77,78,84],"pre",{"className":79,"code":81,"language":82,"meta":83},[80],"language-text","f(f(x)) = f(x)\n","text","",[40,85,81],{"__ignoreMap":83},[14,87,88,89,92],{},"In software systems, the useful definition is semantic: making the same request multiple times has the same ",[18,90,91],{},"intended effect"," as making it once. It does not require byte-identical responses or forbid extra logs, metrics, and timestamps.",[14,94,95,96,99,100,103,104,107],{},"For example, the first ",[40,97,98],{},"DELETE /documents/42"," might return ",[40,101,102],{},"204"," and the second ",[40,105,106],{},"404",". The responses differ, but the intended state — document 42 is absent — has converged.",[32,109,111],{"id":110},"two-ways-to-get-idempotency","Two ways to get idempotency",[113,114,116],"h3",{"id":115},"make-the-operation-naturally-idempotent","Make the operation naturally idempotent",[14,118,119],{},"State assignment tends to converge:",[77,121,124],{"className":122,"code":123,"language":82,"meta":83},[80],"SET order.status = \"PAID\"     repeated → still PAID\nDELETE document 42            repeated → still absent\nPUT /profile { name: \"Li\" }   repeated → same representation\n",[40,125,123],{"__ignoreMap":83},[14,127,128],{},"Relative changes usually do not:",[77,130,133],{"className":131,"code":132,"language":82,"meta":83},[80],"balance = balance + 10\ntoggle subscription\nsend welcome email\ncreate a new charge\n",[40,134,132],{"__ignoreMap":83},[14,136,137],{},"This gives a practical first question: can “change it again” be rewritten as “make it equal to this state”?",[113,139,141],{"id":140},"add-an-idempotency-key","Add an Idempotency Key",[14,143,144],{},"Creating a payment, booking, or cloud resource cannot always be reduced to a simple assignment. The client can instead generate one stable key for one business intent and reuse it for every retry.",[77,146,149],{"className":147,"code":148,"language":82,"meta":83},[80],"Idempotency-Key: order-842-payment\nPOST /payments { amount: 42, currency: \"CAD\" }\n",[40,150,148],{"__ignoreMap":83},[14,152,153],{},"A complete protocol normally needs to:",[155,156,157,160,163,166,169,172,175,178],"ol",{},[48,158,159],{},"use one key for one intent, and a new key for a new intent;",[48,161,162],{},"scope the key by caller, account, and operation;",[48,164,165],{},"reserve it atomically with a unique constraint or transaction;",[48,167,168],{},"bind it to a request fingerprint so changed parameters are rejected;",[48,170,171],{},"record whether the operation is processing, complete, or failed;",[48,173,174],{},"replay the stored or semantically equivalent result to a completed duplicate;",[48,176,177],{},"define what a concurrent duplicate sees; and",[48,179,180],{},"declare the TTL (Time to Live) after which the key may be treated as new.",[32,182,184],{"id":183},"the-atomicity-gap-is-the-real-danger","The atomicity gap is the real danger",[14,186,187],{},"This implementation has a race:",[77,189,192],{"className":190,"code":191,"language":82,"meta":83},[80],"if key does not exist:\n  charge_card()\n  save(key, result)\n",[40,193,191],{"__ignoreMap":83},[14,195,196],{},"Two concurrent requests can both observe a missing key and both charge. The service can also crash after charging but before saving the record, leaving a retry indistinguishable from a new request.",[14,198,199],{},"Key reservation, business state transition, and result recording should share one atomic boundary where possible. If the effect crosses a database, queue, email provider, or external payment service, each boundary needs its own idempotency strategy — often a state machine, Transactional Outbox, or consumer Inbox.",[14,201,202],{},[18,203,204],{},"A header without an atomic state machine is decoration, not a guarantee.",[32,206,208],{"id":207},"four-cases-the-contract-must-name","Four cases the contract must name",[113,210,212],{"id":211},"a-completed-duplicate","A completed duplicate",[14,214,215],{},"Return the first recorded result or a semantically equivalent current result. Do not execute the business action again.",[113,217,219],{"id":218},"the-same-key-with-different-parameters","The same key with different parameters",[14,221,222],{},"Reject it. Otherwise the service cannot know whether it received a retry or a mistakenly reused key. Stripe and Amazon Elastic Compute Cloud (Amazon EC2) both treat parameter mismatch as an error.",[113,224,226],{"id":225},"a-concurrent-duplicate","A concurrent duplicate",[14,228,229,230,233],{},"The second request must not also begin the effect. It can wait, receive an ",[40,231,232],{},"in progress"," response, or get a conflict; the API (Application Programming Interface) contract must choose.",[113,235,237],{"id":236},"an-expired-key","An expired key",[14,239,240],{},"Idempotency memory is usually bounded. Once the record is pruned, the same key may execute again. The server's guarantee window must cover the client's maximum retry horizon.",[32,242,244],{"id":243},"http-safe-is-not-the-same-as-idempotent","HTTP: safe is not the same as idempotent",[14,246,247],{},"HTTP (Hypertext Transfer Protocol) distinguishes safe methods from idempotent methods.",[249,250,251,270],"table",{},[252,253,254],"thead",{},[255,256,257,261,264,267],"tr",{},[258,259,260],"th",{},"Method",[258,262,263],{},"Safe?",[258,265,266],{},"Idempotent by semantics?",[258,268,269],{},"Meaning",[271,272,273,289,304,318],"tbody",{},[255,274,275,281,284,286],{},[276,277,278],"td",{},[40,279,280],{},"GET",[276,282,283],{},"yes",[276,285,283],{},[276,287,288],{},"Requests a read and should not ask for a state change.",[255,290,291,296,299,301],{},[276,292,293],{},[40,294,295],{},"PUT",[276,297,298],{},"no",[276,300,283],{},[276,302,303],{},"Replaces the target with a representation; repeats converge.",[255,305,306,311,313,315],{},[276,307,308],{},[40,309,310],{},"DELETE",[276,312,298],{},[276,314,283],{},[276,316,317],{},"Changes state once, but repeated deletion has the same intent.",[255,319,320,325,327,330],{},[276,321,322],{},[40,323,324],{},"POST",[276,326,298],{},[276,328,329],{},"no, by default",[276,331,332],{},"Often means “create another”; needs a business-level contract.",[14,334,335],{},"An operation can therefore be state-changing and idempotent. Idempotent does not mean harmless or read-only.",[32,337,339],{"id":338},"know-the-neighboring-concepts","Know the neighboring concepts",[45,341,342,348,354,360,366,372],{},[48,343,344,347],{},[18,345,346],{},"Retry is a recovery policy."," It controls timeouts, attempt limits, exponential backoff, and jitter. Idempotency makes those attempts safe for the business effect.",[48,349,350,353],{},[18,351,352],{},"Deduplication is a detection mechanism."," It may be used to implement idempotency, but the semantic contract is broader than dropping duplicates.",[48,355,356,359],{},[18,357,358],{},"At-least-once delivery is a delivery guarantee."," It can repeat messages, so consumers need idempotent handling.",[48,361,362,365],{},[18,363,364],{},"Exactly-once is a stronger and often misleading claim."," Idempotency does not prevent repeated delivery or execution; it makes a scoped effect converge as if it happened once.",[48,367,368,371],{},[18,369,370],{},"Optimistic Concurrency Control (OCC) protects against stale writes."," OCC distinguishes competing intentions; idempotency recognizes another delivery of the same intention.",[48,373,374,377],{},[18,375,376],{},"Transactional Outbox closes a cross-system consistency gap."," It still expects duplicate publication and therefore an idempotent consumer.",[32,379,381],{"id":380},"what-it-does-not-solve","What it does not solve",[45,383,384,387,390,393,400],{},[48,385,386],{},"It does not prevent retry storms; use bounded attempts, backoff, jitter, rate limits, and circuit breaking.",[48,388,389],{},"It does not automatically cross databases, queues, emails, and third-party APIs.",[48,391,392],{},"It does not prevent two different keys from racing for the same inventory.",[48,394,395,396,399],{},"It does not decide whether an API should cache and replay a first ",[40,397,398],{},"500"," response.",[48,401,402],{},"It does not create global exactly-once processing.",[32,404,406],{"id":405},"remember-these-five-things","Remember these five things",[155,408,409,412,415,418,421],{},[48,410,411],{},"Idempotency is a response to unknown outcomes: retry the attempt without repeating the intent's effect.",[48,413,414],{},"Prefer naturally idempotent state assignment when the domain permits it.",[48,416,417],{},"One intent gets one key; retries reuse it; a new intent gets a new key.",[48,419,420],{},"Reject the same key with changed parameters, and define concurrent and expired-key behavior.",[48,422,423],{},"The most dangerous bug lives in the non-atomic gap between the side effect and its idempotency record.",[32,425,427],{"id":426},"self-test","Self-test",[155,429,430,433,436,439,442],{},[48,431,432],{},"If the payment committed but its response vanished, what evidence makes a retry safe?",[48,434,435],{},"Does your key identify a business intent, or only hash a payload?",[48,437,438],{},"What happens when the same key carries a different amount?",[48,440,441],{},"Which request wins when two duplicates arrive concurrently?",[48,443,444],{},"Does the key's lifetime cover the client's longest retry horizon?",[32,446,448],{"id":447},"further-reading","Further reading",[45,450,451,460,467,474],{},[48,452,453],{},[454,455,459],"a",{"href":456,"rel":457},"https://www.rfc-editor.org/rfc/rfc9110.html#section-9.2.2",[458],"nofollow","RFC 9110 · HTTP Semantics §9.2.2 Idempotent Methods",[48,461,462],{},[454,463,466],{"href":464,"rel":465},"https://docs.stripe.com/api/idempotent_requests",[458],"Stripe API · Idempotent requests",[48,468,469],{},[454,470,473],{"href":471,"rel":472},"https://aws.amazon.com/builders-library/making-retries-safe-with-idempotent-APIs/",[458],"AWS Builders' Library · Making retries safe with idempotent APIs",[48,475,476],{},[454,477,480],{"href":478,"rel":479},"https://docs.aws.amazon.com/ec2/latest/devguide/ec2-api-idempotency.html",[458],"Amazon EC2 · Ensuring idempotency in API requests",{"title":83,"searchDepth":482,"depth":482,"links":483},2,[484,485,486,491,492,498,499,500,501,502,503],{"id":34,"depth":482,"text":35},{"id":71,"depth":482,"text":72},{"id":110,"depth":482,"text":111,"children":487},[488,490],{"id":115,"depth":489,"text":116},3,{"id":140,"depth":489,"text":141},{"id":183,"depth":482,"text":184},{"id":207,"depth":482,"text":208,"children":493},[494,495,496,497],{"id":211,"depth":489,"text":212},{"id":218,"depth":489,"text":219},{"id":225,"depth":489,"text":226},{"id":236,"depth":489,"text":237},{"id":243,"depth":482,"text":244},{"id":338,"depth":482,"text":339},{"id":380,"depth":482,"text":381},{"id":405,"depth":482,"text":406},{"id":426,"depth":482,"text":427},{"id":447,"depth":482,"text":448},"/learn-img/idempotency/card-4x5.jpg","Three paper receipts with the same order ID converge into one completed receipt, illustrating repeated attempts producing one business effect.","2026-07-16","Make an uncertain operation safe to retry: one intent may arrive many times, but its business effect happens once.","Software systems","software-systems","md",false,"growing","The same intent may be delivered many times; the system creates the business effect once and replays that outcome to retries.",{},true,[517,521,525,529,533,537],{"name":518,"fullName":518,"category":519,"summary":520},"Retry","recovery policy","Decides when and how another attempt is made; idempotency decides whether that attempt can repeat the business effect.",{"name":522,"fullName":522,"category":523,"summary":524},"Idempotency Key","request identity mechanism","Names one business intent so every retry can be recognized as the same operation.",{"name":526,"fullName":526,"category":527,"summary":528},"Deduplication","duplicate detection mechanism","Detects or suppresses repeats; one possible mechanism for delivering idempotent behavior.",{"name":530,"fullName":530,"category":531,"summary":532},"At-least-once Delivery","delivery guarantee","May deliver a message repeatedly, requiring an idempotent consumer to absorb duplicates safely.",{"name":534,"fullName":534,"category":535,"summary":536},"Transactional Outbox","consistency pattern","Bridges a database change and message publication without an unsafe gap, while consumers still handle duplicates.",{"name":538,"fullName":539,"category":540,"summary":541},"Exactly-once","Exactly-once Processing","stronger guarantee","A frequently overclaimed end-to-end guarantee; idempotency usually converges the effect rather than preventing repeated delivery or execution.",null,"/learn/idempotency",{"title":5,"description":507},{"loc":543},[547,549,551,552],{"title":548,"url":456},"RFC 9110 · Idempotent Methods",{"title":550,"url":464},"Stripe · Idempotent requests",{"title":473,"url":471},{"title":480,"url":478},"learn/idempotency",[555,556,557,558],"reliability","retry","distributed-systems","api-design","obOAANazP6Oy-Xup7MINTmZaH79WsfMHLvRTgREw_Fs",1785418434186]