Idimpotenza e chiavi
Cos'è l'idimpotenza
L'idempotenza è una proprietà dell'operazione in cui una ripetizione con lo stesso ID non cambia l'effetto finale. Nei sistemi distribuiti, questo è il metodo principale per rendere il risultato equivalente a «una sola lavorazione», nonostante i retrai, i messaggi duplicati e i timeout.
Idea chiave: ogni operazione potenzialmente ripetuta deve essere contrassegnata con una chiave in base alla quale il sistema riconosce «già fatto» e applica il risultato al massimo una volta.
Dove è importante
Pagamenti e bilanci: prelievi/accessi per «operation _ id».
Prenotazioni/quote/limiti: stesso slot/risorsa.
Webhook/Notifiche: la ricarica non deve duplicare l'effetto.
Importa/Migrazione: riprova file/pacchetti.
Strim processing: prese da broker/CDC.
Vista e ambito delle chiavi
1. Operation key - Identificatore di un particolare tentativo di operazione aziendale
Esempi sono «idempotency _ key» (HTTP), «operation _ id» (RPC).
Area: servizio/aggregazione; memorizzato nella tabella di deduplicazione.
2. Event key - Identificatore univoco evento/messaggio
Esempi: 'event _ id' (UUID), '(producer _ id, sequence)'.
Area: consumatore/gruppo di consumatori; protegge le proiezioni.
3. Business key - la chiave naturale dell'area oggetto
Esempi sono «payment _ id», «invoice _ number», «(user _ id, day)».
Area: aggregazione; si applica ai controlli di unicità/versione.
TTL e criteri di conservazione
Le chiavi TTL includono una possibile finestra di ripetizione: ritenzione del login + ritardi di rete/processo.
Per i domini critici (pagamenti) TTL - giorni/settimane; per la telemetria è un orologio.
Pulire le tabelle di controllo background-jobs; per il controllo - Archiviare.
Storage chiavi (deduplicazione)
Database transazionale (consigliato): indici upsert/unici affidabili, transazione congiunta con effetto.
KV/Redis: veloce, conveniente per una TTL breve, ma senza una transazione OLTP condivisa, attenta.
State store del processore strim: locale + cheinjlog nel broker; Bene a Flink/KStreams.
- idempotency_keys
`consumer_id` (или `service`), `op_id` (PK на пару), `applied_at`, `ttl_expires_at`, `result_hash`/`response_status` (опц.) .
Indici: '(consumer _ id, op _ id)' è univoco.
Procedure di implementazione di base
1) Transazione effetto + progresso
Scrittura dei risultati e fissa i progressi di lettura/posizione in una singola transazione.
pseudo begin tx if not exists(select 1 from idempotency_keys where consumer=:c and op_id=:id) then
-- apply effect atomically (upsert/merge/increment)
apply_effect(...)
insert into idempotency_keys(consumer, op_id, applied_at)
values(:c,:id, now)
end if
-- record reading progress (offset/position)
upsert offsets set pos=:pos where consumer=:c commit
2) Ottimistico Concertency (versione dell'unità)
Protegge dal doppio effetto durante le corse:sql update account set balance = balance +:delta,
version = version + 1 where id=:account_id and version=:expected_version;
-- if 0 rows are updated → retry/conflict
3) Sinks idempotenti (upsert/merge)
Operazione di versamento una volta:sql insert into bonuses(user_id, op_id, amount)
values(:u,:op,:amt)
on conflict (user_id, op_id) do nothing;
Idimpotenza nei protocolli
HTTP/REST
Titolo «Idempotency-Key: <ouid 'hash>».
Il server memorizza la voce chiave e restituisce nuovamente la stessa risposta (o codice «409 »/« 422» in caso di conflitto tra invarianti).
Per i «non sicuri» POST - obbligatorio «Idempotency-Key» + timeout/retrai-policy sostenibile.
gRPC/RPC
Metadati «idempotency _ key», «sollest _ id» + deadline.
Implementazione server - Come in REST. tabella di deduplicazione nella transazione.
Broker/streaming (Kafka/NATS/Pulsar)
Produttore: stabile'event _ id '/produttore idimpotente (dove supportato).
Notiziario per «(consumer _ id, event _ id)» e/o secondo la versione aziendale dell'unità.
DLQ separato per i messaggi non idonei o danneggiati.
Web hook e partner esterni
Richiedi «Idempotency-Key »/« event _ id» nel contratto; La nuova consegna deve essere sicura.
Memorizzare «notification _ id» e gli stati di invio; nel retro, non duplicate.
Progettazione delle chiavi
Determinabilità: i retrai devono inviare la stessa chiave (generare in anticipo su client/orchestratore).
Area di visibilità: formare "op _ id" comè service: aggregate: id: purpose ".
Collisioni: utilizzare UUIDv7/ULID o hash dei parametri aziendali (con sale se necessario).
Gerarchia: la generica «operation _ id» sul fronte → viene trasmessa a tutti i soggetti (catena Idempotent).
UX e aspetti alimentari
Una nuova richiesta di chiave deve restituire lo stesso risultato (compreso il corpo/stato) oppure un esplicito «già completato».
Visualizzare agli utenti gli stati «l'operazione viene elaborata/completata» invece di riprovare «per fortuna».
Per lunghe operazioni, polling su chiave ('GET/operations/{ op _ id}').
Osservabilità
Logica «op _ id», «event _ id», «trace _ id», «APPLIED »/« ALREADY _ APPLIED».
Metriche: percentuale di ripetizioni, dimensione delle tabelle di deduzione, tempo delle transazioni, conflitti di versione, puntata DLQ.
La chiave deve passare attraverso il comando dell'evento, la proiezione e la chiamata esterna.
Protezione e compliance
Non memorizzare PII in chiave; chiave - ID, non payload.
Crittografare i campi sensibili nei record di deduplicazione con TTL a lungo termine.
Criteri di archiviazione: TTL e archivi diritto all'oblio - attraverso la crittografia delle risposte/metadati (se contengono PII).
Test
1. Duplicati: scansione di un messaggio/richiesta 2-5 volte - l'effetto è esattamente uno.
2. La caduta tra i passaggi: prima/dopo la registrazione dell'effetto, prima/dopo la fissazione dell'offset.
3. Restart/rebalance dei consumatori: nessun uso doppio.
4. Concorrenza: query parallele con un unico «op _ id» ha un effetto, l'altro è «ALREADY _ APPLIED/409».
5. Chiavi a lunga vita: verifica della scadenza della TTL e delle ripetizioni dopo il ripristino.
Antipattern
Nuova chiave casuale per ogni retrai, il sistema non riconosce le ripetizioni.
Due committenti separati, prima l'effetto, poi la caduta tra di loro duplica l'effetto.
La fiducia è solo per il broker, l'assenza di deducibili nel nastro/aggregazione.
Nessuna versione dell'aggregazione - Il nuovo evento cambia stato una seconda volta.
Fat keys - La chiave include campi aziendali/PII per le fughe e indici complessi.
Nessuna risposta ripetuta: il client non può ritrarre in modo sicuro.
Esempi
Post di pagamento
Client: «POST/payments» + «Idempotency-Key: k-789».
Server: transazione - Crea «payment» e scrittura in «idempotency _ keys».
Ripetizione: restituisce lo stesso «201 »/corpo; in conflitto invariante - '409'.
Rimborso bonus (sink)
sql insert into credits(user_id, op_id, amount, created_at)
values(:u,:op,:amt, now)
on conflict (user_id, op_id) do nothing;
Proiezione da eventi
Consumare «seen (event _ id)» e «variante» dell'unità; ripetizione - upsert ignoto/idpotente.
I progressi della lettura vengono registrati nella stessa transazione dell'aggiornamento della proiezione.
Foglio di assegno di produzione
- Per tutte le operazioni non sicure, è stata definita una chiave idropotente e la relativa area di visibilità.
- Ci sono tabelle di deduplicazione con TTL e indici univoci.
- Gli effetti e i progressi della lettura sono atomatici.
- Il modello write include una concorrenza ottimistica (versione/sequence).
- I contratti API registrano «Idempotency-Key »/« operation _ id» e il comportamento delle ripetizioni.
- Le metriche e i loghi contengono «op _ id »/« event _ id »/« trace _ id».
- Test per duplicati, cadute e corse - in CI.
- I criteri TTL/archivio e la sicurezza PII sono stati rispettati.
FAQ
Cosa è diverso da «Idempotency-Key»?
«Richiest-Id» - Traccia; «Idempotency-Key» è un identificativo semantico dell'operazione, obbligatorio uguale quando ripetuto.
È possibile fare idempotenza senza database?
Per una breve finestra, sì (Redis/crash intracessuale), ma senza una transazione congiunta, il rischio di ripresa aumenta. Nei domini critici è meglio in una singola transazione database.
Cosa fare con i partner esterni?
Ordinare chiavi e risposte ripetute. Se il partner non supporta, ruotare la chiamata nel proprio livello idipotente e memorizzare «già applicato».
Come scegliere TTL?
Riassumere i ritardi massimi: retenza del login + worst-case della rete/rebalance + buffer. Aggiungere una scorta (x 2).
Totale
L'idempotenza è una disciplina di chiavi, transazioni e versioni. Identificatori operativi sostenibili + fissazione atomica degli effetti e dei progressi di lettura + Sinks/Proiezioni Idompotenti producono «esattamente un effetto» senza la magia del livello di trasporto. Rendete le chiavi determinate, la TTL realistica e i test malevoli. Allora i retrai e i duplicati diventeranno routine, non incidenti.