Timeout neznamená, že se nic nestalo. Bez idempotence vede druhý pokus ke dvojí platbě, dvěma účtům nebo dvěma stejným e-mailům zákazníkovi.
Idempotence je vlastnost, díky které může systém bezpečně zkusit totéž znovu.
V distribuovaném systému se ke stejnému požadavku dřív nebo později dostaneme dvakrát. Otázka nezní, jestli se to stane, ale co se v tu chvíli stane s daty.
Obsah
- Co je idempotence
- Proč na ní v distribuovaných systémech záleží
- Co se stane bez ní
- Jak idempotenci zajistit
- Idempotence v HTTP API
- Typické chyby
- Pět otázek pro váš systém
Co je idempotence
Operace je idempotentní, když má její opakované provedení stejný výsledek jako provedení jediné.
Nejlépe to ukazuje rozdíl mezi dvěma příkazy:
- „Nastav zůstatek na 5 000 Kč.“ Ať ho provedu jednou nebo třikrát, zůstatek je 5 000 Kč.
- „Přičti 5 000 Kč.“ Každé další provedení výsledek změní.
První příkaz je idempotentní, druhý ne. Podobně funguje tlačítko u výtahu: když ho zmáčknu podruhé, výtah nepřijede dvakrát.
V softwaru nás nezajímá matematická definice, ale účinek na stav systému. Idempotentní operace může vrátit jinou odpověď nebo zapsat záznam do logu. Podstatné je, že druhé provedení nezmění data jinak než to první.
Proč na ní v distribuovaných systémech záleží
Dokud vše běží v jednom procesu a jedné transakci, vidíme, jestli operace proběhla. Jakmile mezi dvěma částmi systému stojí síť, ztrácíme jistotu.
Duplicity mají tři typické zdroje:
- Ztracené potvrzení. Služba zprávu zpracuje, ale potvrzení se cestou ztratí. Broker ji doručí znovu.
- Retry klienta. Volání skončí timeoutem a klient ho zopakuje. Netuší, jestli první pokus prošel.
- Redelivery brokeru. Po výpadku spojení nebo uzlu broker doručí zprávy, které už konzument mohl vidět.
Dokumentace RabbitMQ to říká přímo: zprávy mohou být doručeny znovu a konzumenti musí být připraveni zpracovat doručení, které už viděli. Doporučuje navrhovat konzumenty jako idempotentní [1].
Broker garantuje „aspoň jednou“, ne „právě jednou“.
Na našem školení Event-Driven Architecture to ukazujeme na výplatě hypotéky. Služba Disbursement zavolá platební systém a volání skončí timeoutem. Peníze mohly odejít, nebo ne. Poslat výplatu znovu je riziko, neposlat ji je taky riziko.

Co se stane bez ní
Vezměme službu, která po schválení hypotéky otevírá úvěrový účet. Dostane zprávu „otevři účet pro žádost 42“, účet založí a potvrdí zpracování.
Potvrzení se ztratí. Broker zprávu doručí znovu. Služba ji zpracuje jako novou a založí druhý účet.
Intuitivní reakce bývají dvě a obě jsou špatně:
- Vypnout retry. Pak se při skutečném výpadku zpráva ztratí úplně a účet nevznikne vůbec.
- Zkoušet „opatrněji“. Delší timeout nebo méně pokusů duplicitu jen zřídí, neodstraní ji.
Spolehlivé doručení a správná data spolu nejdou dohromady bez idempotence. V praxi se proto „právě jednou“ skládá ze dvou částí: doručení aspoň jednou plus idempotentní zpracování.
Jak idempotenci zajistit
Idempotentní klíč
Klient ke každému požadavku přidá jedinečný identifikátor. Server si u klíče uloží výsledek prvního zpracování. Když přijde stejný klíč znovu, operaci neprovádí a vrátí uložený výsledek.
Takto fungují například platby ve Stripe. Server uloží stavový kód a tělo odpovědi prvního požadavku s daným klíčem a opakované požadavky se stejným klíčem dostanou uloženou odpověď. Klíče lze po uplynutí nejméně 24 hodin odstranit [2].
Důležité je, kdo klíč vytvoří: klient, ještě před prvním pokusem. Klíč vygenerovaný až na serveru by byl u každého retry jiný.
Inbox: evidence zpracovaných zpráv
U zpráv z brokeru je klíčem ID zprávy. Konzument si ve vlastní databázi eviduje, které zprávy už zpracoval, a duplicity zahodí. Vzor se jmenuje Idempotent Consumer [3].
Podmínkou je, aby se záznam o zpracování a změna dat zapsaly v jedné transakci:
public async Task Handle(OpenMortgageAccount message)
{
await using var tx = await db.BeginTransactionAsync();
// Duplicita: zpráva už byla zpracována, nic dalšího nedělat.
if (await db.ProcessedMessages.AnyAsync(m => m.Id == message.MessageId))
return;
db.Accounts.Add(Account.Open(message.ApplicationId, message.Amount));
db.ProcessedMessages.Add(new ProcessedMessage(message.MessageId));
await db.SaveChangesAsync(); // obojí, nebo nic
await tx.CommitAsync();
}
Kontrola a zápis musí být v jedné transakci a tabulka zpracovaných zpráv potřebuje unikátní index na ID. Jinak dvě souběžná doručení projdou kontrolou současně.

Přirozeně idempotentní operace
Někdy jde idempotenci zajistit bez evidence:
- Nastavit stav místo změny o hodnotu. „Stav žádosti = schválena“ místo „posuň stav o krok dál“.
- Unikátní omezení v databázi. Účet může pro jednu žádost existovat jen jednou. Druhý pokus skončí chybou unikátnosti a služba ji vyhodnotí jako „už hotovo“.
- Verze agregátu. Optimistic concurrency odmítne zápis, který počítá se starou verzí dat.
Kde idempotence končí
Idempotentní příjemce chrání jen vlastní data. Pokud služba v rámci zpracování volá další systém, například posílá e-mail nebo platbu, musí idempotentní klíč poslat i dál. Jinak se duplicita jen přesune o patro níž.
Na straně odesílatele pomáhá vzor Transactional Outbox: zpráva se uloží do stejné databáze jako změna dat a odešle se až potom [4]. Outbox zaručí, že se zpráva neztratí. Za duplicity, které při odesílání vzniknou, odpovídá idempotentní příjemce.
Idempotence v HTTP API
Specifikace HTTP definuje idempotentní metody: opakovaný identický požadavek má na serveru stejný zamýšlený účinek jako jediný. Idempotentní jsou PUT, DELETE a bezpečné metody jako GET. POST idempotentní není [5].
Proto se u plateb, objednávek a dalších POST požadavků používá hlavička Idempotency-Key. IETF ji standardizuje právě proto, aby šly neidempotentní metody bezpečně opakovat [6].
Prakticky:
- klient generuje klíč (například UUID) a při retry posílá stejný;
- server uloží klíč spolu s výsledkem a parametry požadavku;
- stejný klíč s jinými parametry je chyba klienta, ne nový požadavek.
Typické chyby
- Kontrola duplicity mimo transakci. Dvě souběžná doručení projdou kontrolou současně a obě zapíšou data.
- Klíč generovaný na serveru. Každý retry pak vypadá jako nový požadavek.
- Deduplikace jen v paměti. Po restartu služby evidence zmizí a duplicity projdou.
- Příliš krátké uchování klíčů. Retry po výpadku může přijít za hodiny. Doba uchování musí být delší než nejdelší možné opakování.
- Idempotence jen v první službě. Duplicita se přesune do navazujícího systému, který klíč nedostal.
Pět otázek pro váš systém
- Co se stane, když tahle zpráva dorazí dvakrát?
- Kdo vytváří idempotentní klíč a zůstává stejný při retry?
- Zapisuje se evidence zpracování ve stejné transakci jako data?
- Jak dlouho klíče uchováváme a stačí to na nejdelší výpadek?
- Posíláme klíč dál do systémů, které voláme?
Idempotence není optimalizace. Je to podmínka, aby retry byl bezpečný.
Duplicitu si můžete vyzkoušet na našem školení Event-Driven Architecture v části o idempotenci a ve cvičení 12, kde simulátor ukáže stejný scénář bez Inboxu a s ním. Na stejné téma navazuje článek Event Sourcing jako paměť systému.
Literatura a zdroje
Zdroje ověřeny 26. října 2026.
[1] RabbitMQ. Reliability Guide. Dokumentace RabbitMQ. Část Data Safety on the Consumer Side: opakované doručení a doporučení idempotentních konzumentů.
[2] Stripe. Idempotent requests. Stripe API Reference. Ukládání odpovědi prvního požadavku s klíčem, opakované požadavky a uchování klíčů.
[3] Richardson, Chris. Pattern: Idempotent Consumer. Microservices.io. Evidence ID zpracovaných zpráv v databázi a zahazování duplicit.
[4] Richardson, Chris. Pattern: Transactional Outbox. Microservices.io.
[5] Fielding, R.; Nottingham, M.; Reschke, J. RFC 9110: HTTP Semantics, oddíl 9.2.2 Idempotent Methods. IETF, 2022.
[6] IETF HTTPAPI Working Group. The Idempotency-Key HTTP Header Field. Internet-Draft, IETF. Návrh standardu, ne finální RFC.

