Torna al blog Connettività

Chiavi di idempotenza in un’API SMS: come evitare duplicati in caso di retry e timeout

Una chiave di idempotenza può aiutare a gestire i retry di una richiesta HTTP, ma solo se l’API ne definisce l’ambito e il comportamento. Scopri cosa concordare prima di ripetere un invio e come distinguere l’accettazione della richiesta dallo stato di consegna.

Diagramma di un’integrazione SMS che mostra una chiave di idempotenza, retry HTTP e consultazione dello stato del messaggio

Quale problema risolve l’idempotenza nell’invio di SMS

Un client può inviare una richiesta HTTP e perdere la connessione prima di ricevere la risposta. In quel momento, non sa necessariamente se il server ha elaborato la richiesta. Se ripete alla cieca un invio che l’API tratta come una nuova operazione, può provocare una seconda accettazione e, potenzialmente, un messaggio duplicato.

Una chiave di idempotenza è un meccanismo che un’API può offrire per riconoscere che più richieste corrispondono alla stessa operazione. La sua utilità dipende dal contratto specifico dell’API: lo standard HTTP, da solo, non definisce una chiave di idempotenza né le relative regole per un endpoint di invio SMS.

  • Obiettivo: evitare che il retry di una stessa operazione venga interpretato come una nuova richiesta.
  • Limite: evitare un’accettazione duplicata non equivale a garantire che il messaggio venga consegnato una sola volta.
  • Prima di implementare i retry, verifica nella documentazione dell’API se supporta le chiavi, quale ambito hanno e quale risposta restituisce quando vengono riutilizzate.
Quale problema risolve l’idempotenza nell’invio di SMS

Ripetere una richiesta HTTP non significa sempre ripetere lo stesso invio

La semantica HTTP distingue tra metodi idempotenti e non idempotenti. Secondo RFC 9110, un’operazione è idempotente quando l’invio di più richieste identiche produce sul server lo stesso effetto previsto di una sola richiesta. La specifica identifica metodi come PUT e DELETE, ma non classifica POST come idempotente per impostazione predefinita.

Perciò, non bisogna presumere che sia sicuro reinviare un POST di invio SMS. Se la connessione si interrompe prima che arrivi la risposta, il client potrebbe non sapere se la richiesta è stata eseguita. RFC 9110 raccomanda di non ripetere automaticamente un’operazione non idempotente, salvo che si sappia che la sua semantica è idempotente o che si possa determinare che la richiesta originale non è stata applicata.

  • Un timeout descrive ciò che ha osservato il client, non necessariamente ciò che è avvenuto sul server.
  • Un rifiuto ricevuto è diverso da una risposta persa: se l’API ha restituito una risposta, usa il codice e il contratto per decidere il passo successivo.
  • Non trasformare ogni errore HTTP, chiusura della connessione o timeout in un reinvio automatico.
Ripetere una richiesta HTTP non significa sempre ripetere lo stesso invio

Genera una chiave stabile per ogni operazione di business

La chiave deve identificare un’operazione logica che il sistema possa riconoscere a ogni tentativo, per esempio la richiesta di un OTP associata a uno specifico evento interno. Se viene generata una nuova chiave a ogni retry, l’API non avrà un identificativo comune per collegare le richieste.

La chiave non dovrebbe contenere dati personali o segreti non necessari. Definisci come generarla e conservarla all’interno del tuo sistema, ed evita di riutilizzarla per un’operazione diversa. Le fonti disponibili non stabiliscono un metodo preciso per generare le chiavi, né il formato o il livello di entropia: questi aspetti devono essere concordati sulla base della documentazione dell’API e dei requisiti di sicurezza dell’integrazione.

  • Assegna una chiave quando crei l’operazione di business, prima del primo tentativo HTTP.
  • Mantieni la stessa chiave per tutti i retry relativi a quell’operazione.
  • Crea una chiave diversa per una nuova operazione, anche se destinatario e contenuto coincidono.
  • Non includere credenziali, token o dati personali se non è necessario.

Definisci l’ambito e la durata prima di fare affidamento sulla chiave

Una chiave è univoca solo nell’ambito stabilito dall’API. Il contratto deve chiarire se viene interpretata per account, endpoint, operazione o altra combinazione, oltre a specificare per quanto tempo viene conservata l’associazione tra chiave e richiesta. Non esiste una durata universale stabilita per le API SMS.

Se il client riprova dopo che l’API ha smesso di conservare la chiave, il server potrebbe trattare la richiesta come nuova. Di conseguenza, il periodo di conservazione dovrebbe coprire l’intervallo durante il quale il sistema potrebbe aver bisogno di recuperare una risposta persa, e l’applicazione deve sapere come comportarsi alla sua scadenza.

  • Verifica l’ambito di unicità della chiave ed evita di presumere che sia globale.
  • Documenta il periodo di conservazione e il comportamento dopo la sua scadenza.
  • Allinea il periodo di retry del client a quello garantito dal contratto.
  • Se l’API non documenta questi aspetti, chiedi chiarimenti prima di automatizzare i reinvii.

Concorda cosa succede se la stessa chiave viene usata con un payload diverso

Riutilizzare una chiave con dati diversi è un caso critico. Il contratto dovrebbe precisare come l’API confronta le richieste e cosa succede se la stessa chiave viene inviata con un payload incompatibile. Una regola prudente è non riutilizzare la chiave per modificare destinatario, contenuto o altri campi che cambiano l’operazione; la risposta concreta al conflitto va verificata nella documentazione del servizio.

È inoltre utile sapere quale risultato riceve il client quando ripete esattamente la richiesta. Alcune API potrebbero restituire un risultato associato alla prima operazione, ma non ci sono elementi disponibili per affermare che lo facciano tutte. Non presumere che venga riprodotta la risposta originale né che venga restituito uno specifico codice di conflitto senza averlo confermato con il fornitore.

  • Mantieni invariato il payload associato a una chiave durante i retry.
  • Definisci quali campi determinano l’identità dell’operazione.
  • Verifica il comportamento in caso di riutilizzo della chiave con un payload diverso.
  • Registra la risposta ricevuta senza interpretarla come prova di consegna al terminale.

Progetta la gestione di timeout e risposte perse

Quando non arriva una risposta, la prima decisione non è semplicemente reinviare: occorre stabilire se l’API permette di ripetere in sicurezza la richiesta con la stessa chiave o di consultare l’operazione. Se non esiste un contratto di idempotenza o un meccanismo per conoscerne l’esito, lo stato può rimanere indeterminato; un nuovo POST potrebbe creare un’altra operazione.

Distingui nella logica gli errori per cui è stata ricevuta una risposta dai casi in cui non ne è arrivata nessuna. Per questi ultimi, applica solo le opzioni documentate dall’API. Non considerare un timeout come prova che il server non abbia eseguito la richiesta.

  • Salva la chiave e i dati necessari per correlare il tentativo prima di inviare la richiesta.
  • In caso di risposta persa, riprova con la stessa chiave solo se il contratto conferma che è sicuro farlo.
  • Se esiste una funzione documentata per consultare l’operazione o il suo stato, usala prima di creare un nuovo invio.
  • Se non esiste un modo documentato per risolvere l’incertezza, evita il reinvio alla cieca e gestisci il caso come indeterminato.

Gestisci le richieste simultanee e la persistenza

Due processi potrebbero tentare di inviare la stessa operazione nello stesso momento, per esempio se una coda consegna di nuovo un’attività mentre un altro worker la sta ancora elaborando. L’integrazione dovrebbe evitare che la concorrenza locale generi chiavi diverse per lo stesso evento di business o perda il collegamento tra chiave e payload.

Gli elementi disponibili non definiscono un metodo universale di archiviazione atomica né uno specifico meccanismo di blocco per un’API SMS. Progetta il controllo a livello applicativo e verifica come l’API gestisce le richieste simultanee con la stessa chiave. Non presumere che elimini i duplicati dovuti a condizioni di race, a meno che il contratto non lo specifichi.

  • Salva la chiave e l’identità dell’operazione prima di avviare l’invio.
  • Assicurati che i worker concorrenti recuperino la stessa chiave per la stessa operazione.
  • Definisci quale record locale prevale se due processi tentano di creare l’operazione nello stesso momento.
  • Esegui test con richieste simultanee e verifica il comportamento documentato dell’endpoint.

L’accettazione non conferma lo stato finale del messaggio

Se l’API la supporta, la chiave di idempotenza gestisce la ripetizione di una richiesta nell’ambito di un contratto definito. Non conferma che il messaggio sia arrivato al terminale e non sostituisce il monitoraggio dello stato. Nel modello dati e nei report, tieni separati l’esito della richiesta HTTP e le informazioni successive sul messaggio.

Se la richiesta è stata accettata ma non conosci ancora lo stato finale, conserva gli identificativi e i dati di correlazione forniti dall’API e usa i meccanismi di consultazione documentati. Non generare un nuovo invio solo perché lo stato impiega tempo ad aggiornarsi. Anche un DLR ricevuto non deve essere presentato come verifica indipendente della ricezione sul terminale, se tale verifica non è disponibile.

  • Registra separatamente la chiave di idempotenza, l’esito HTTP e gli identificativi del messaggio disponibili.
  • Consulta o riconcilia gli stati usando le funzionalità documentate dell’API.
  • Non confondere l’accettazione, lo stato riportato dal percorso di invio e la ricezione verificata in modo indipendente.
  • Non promettere una consegna univoca end-to-end basandoti soltanto sull’idempotenza dell’API.
FAQ

Domande frequenti

Una chiave di idempotenza garantisce che l’SMS venga consegnato una sola volta?

No. Può aiutare a evitare che un’API accetti più volte la stessa operazione, se il servizio definisce e applica un contratto in tal senso. Non dimostra la ricezione sul terminale né garantisce una consegna univoca end-to-end.

Devo riprovare a inviare un SMS quando si verifica un timeout?

Non alla cieca. Un timeout non dimostra che il server non abbia elaborato la richiesta. Riprova con la stessa chiave solo se l’API documenta questo comportamento, oppure consulta l’operazione tramite un meccanismo documentato.

Che cosa succede se uso la stessa chiave con un payload diverso?

Dipende dal contratto dell’API. Non è dimostrata l’esistenza di una regola universale per le API SMS. Mantieni invariato il payload associato a ogni chiave e verifica quale risposta fornisce il servizio per una richiesta incompatibile.

Per quanto tempo deve essere conservata una chiave?

La durata dipende dall’API. Verifica la finestra di conservazione e allinea a essa il periodo di retry del client; non presumere che esista una durata standard.

Un DLR conferma che l’utente ha ricevuto il messaggio sul telefono?

Non va considerato automaticamente una prova indipendente della ricezione sul terminale. Distingui lo stato DLR comunicato da un’eventuale verifica indipendente, se disponibile.

Fonti consultate

  1. HTTP Semantics (RFC 9110)IETF
  2. SMPP Protocol Specification v3.4SMPP Developers Forum