Offline Queue: principi, strategie e meccanismi di funzionamento

Autore: IT Sectr Pubblicato: 2026-06-13 Tempo di lettura: 10 min

Offline Queue è un meccanismo che salva le operazioni dell'utente localmente quando il dispositivo è offline e le invia al server dopo il ripristino della connessione. Senza una coda offline, l'utente perde tutte le azioni eseguite senza internet, il che è inaccettabile nelle applicazioni mobili. Secondo Google Developers (2025), l'implementazione di un'architettura offline-first aumenta la fidelizzazione degli utenti del 30% nelle regioni con internet instabile.

Punti chiave

  • Offline Queue — una coda FIFO di operazioni che l'utente esegue senza internet, per la successiva sincronizzazione.
  • Persistent storage — la coda viene memorizzata in un database locale (SQLite, Room) per essere conservata al riavvio dell'applicazione.
  • Exponential backoff — strategia di ripetizione con intervalli crescenti in caso di fallimento dell'invio.
  • Conflict resolution — meccanismo di risoluzione delle collisioni quando le modifiche offline entrano in conflitto con i dati del server.
  • Idempotency keys — chiavi di operazione uniche per prevenire la duplicazione sul server durante il reinvio.

Cos'è una coda offline?

Offline Queue è una raccolta ordinata di operazioni (creare, aggiornare, eliminare) che l'applicazione salva localmente quando il dispositivo non ha accesso alla rete. Una volta ripristinata la connessione, la coda invia le operazioni al server nello stesso ordine in cui l'utente le ha eseguite.

Immagina uno scenario: un utente di messaggistica scrive messaggi in metropolitana senza internet. Ogni tocco su “Invia” viene aggiunto alla Offline Queue. Quando il treno esce dal tunnel e la rete diventa disponibile, tutti i messaggi vengono inviati automaticamente. Esperienza utente — senza interruzioni: non si accorge di essere stato offline, tranne per un leggero ritardo nell'invio.

Secondo Uber Engineering (2024), la loro coda offline elabora oltre 2 milioni di operazioni al giorno in regioni con scarsa qualità di connessione. La coda utilizza l'archiviazione locale Room con ordine FIFO e un meccanismo di consegna garantita exactly-once.

kotlin
data class QueuedOperation(
    val id: String,
    val type: OperationType,
    val endpoint: String,
    val payload: String,
    val timestamp: Long,
    val retryCount: Int = 0,
    val idempotencyKey: String
)

Ogni operazione contiene tutti i dati necessari per il reinvio: endpoint, corpo della richiesta, timestamp e idempotencyKey. Il database Room garantisce la persistenza della coda al riavvio dell'applicazione e ai crash del sistema operativo.

Perché una coda di operazioni è necessaria in un'app mobile

Garanzia di consegna — lo scopo principale della coda. L'utente deve essere certo che la sua azione (inviare un messaggio, mettere like, fare un ordine) verrà completata, anche se la rete non è disponibile in quel momento. Offline Queue con meccanismo di ripetizione garantisce la consegna eventuale.

UX migliorata in condizioni di connettività scadente — secondo GSMA Mobile Economy Report (2025), circa il 40% degli utenti mobili nel mondo ha connessioni internet instabili. Offline Queue rende l'applicazione utilizzabile in metropolitana, ascensori, aree remote — ovunque la connettività sia intermittente.

Riduzione della perdita di dati — senza coda, tutte le azioni eseguite offline vengono perse. Un utente potrebbe compilare un modulo lungo, toccare “Invia” e vedere un errore di rete — tutto l'input viene perso. Offline Queue salva i dati e li invia alla prima opportunità. Il salvataggio automatico in Google Docs è un esempio classico di coda offline per i documenti.

Sincronizzazione asincrona — la coda consente all'applicazione di non bloccare l'interfaccia utente durante l'invio. L'utente continua a lavorare mentre il gestore di sincronizzazione elabora la coda in background. Questo segue i principi dell'Architettura Reattiva e migliora la reattività dell'interfaccia.

Architettura della coda offline: archiviazione ed elaborazione

Tre livelli della coda: archiviazione (persistenza), pianificatore (scheduler) ed esecutore. Archiviazione — Room con una tabella QueuedOperation. Pianificatore — WorkManager (Android) o BGTaskScheduler (iOS) che avvia la sincronizzazione quando la rete diventa disponibile. Esecutore — un iteratore FIFO sequenziale che invia le operazioni una per una.

Ordine di elaborazione — critico per la coerenza dei dati. Se un utente ha creato un record e poi lo ha modificato, entrambe le operazioni devono essere inviate nello stesso ordine. Altrimenti, il server riceve prima un aggiornamento di un record inesistente — errore. FIFO sequenziale — ordine rigoroso con controllo delle dipendenze tra le operazioni.

Strategia di fusione — se la coda ha un CREATE seguito immediatamente da un DELETE dello stesso oggetto, entrambe le operazioni possono essere rimosse senza invio: lo stato finale è che l'oggetto non è stato creato. Allo stesso modo, CREATE + UPDATE possono essere fusi in un unico CREATE con i dati più recenti. L'ottimizzazione della coda riduce il numero di richieste HTTP e accelera la sincronizzazione.

Secondo Android Developers (2025), WorkManager è il metodo preferito per gestire Offline Queue su Android: garantisce l'esecuzione anche dopo il riavvio del dispositivo, supporta i vincoli di rete e consente di configurare le politiche di ripetizione tramite NetworkType.CONNECTED.

kotlin
class SyncWorker(
    private val context: Context,
    private val params: WorkerParameters
) : CoroutineWorker(context, params) {

    override suspend fun doWork(): Result = runCatching {
        queueRepository.processNextBatch(batchSize = 10)
        Result.success()
    }.getOrDefault(Result.retry())
}

CoroutineWorker elabora lotti di operazioni e restituisce Result.retry() in caso di fallimento — WorkManager riprova automaticamente con backoff esponenziale. Questo è il modo più semplice per ottenere una Offline Queue affidabile su Android.

Strategie di ripetizione: exponential backoff e politica di ripetizione

Exponential Backoff — una strategia di ripetizione standard con intervalli crescenti: 2 sec, 4 sec, 8 sec, 16 sec e così via fino a una soglia massima. Questo evita il sovraccarico ripetuto del server se è temporaneamente non disponibile. La libreria Java Resilience4j (2024) fornisce un'implementazione pronta di Retry con backoff configurabile.

Numero massimo di tentativi — un parametro critico. Se dopo 5–10 tentativi l'operazione fallisce, ulteriori ripetizioni sono dispendiose e inutili. Si raccomanda una dead letter queue: dopo aver esaurito i tentativi, l'operazione viene spostata in una tabella separata per l'analisi manuale. Secondo Microsoft Patterns & Practices (2024), una dead letter queue semplifica il debug dei problemi di sincronizzazione e impedisce che operazioni errate blocchino la coda.

Jitter — variazione casuale — aggiunta di un numero casuale all'intervallo di backoff. Se mille dispositivi ripristinano la rete contemporaneamente dopo un'interruzione, iniziano tutti a sincronizzare allo stesso tempo. Il Jitter li distribuisce nel tempo, prevenendo Cache Stampede sul server. Jitter completo: delay = random(0, backoff) — raccomandato da AWS (2024) per i client API.

Risoluzione dei conflitti: come risolvere le collisioni dei dati

Last Write Wins (LWW) — la strategia più semplice: in caso di conflitto, vince l'operazione con il timestamp più recente. LWW richiede la sincronizzazione dell'ora — il timestamp deve essere generato sul server o utilizzare un Orologio Logico (orologi di Lamport). Svantaggio: i dati di un utente possono essere sovrascritti da quelli di un altro senza preavviso.

OT (Trasformazione Operazionale) — l'algoritmo utilizzato da Google Docs e Figma per la modifica collaborativa in tempo reale, incluso il modo offline. OT trasforma le operazioni in modo che possano essere applicate a qualsiasi stato del documento, garantendo coerenza senza blocchi. CRDT (Tipi di Dati Replicati Senza Conflitto) — un'alternativa a OT che guadagna popolarità nelle app mobili: i dati sono strutturati in modo che i conflitti siano matematicamente risolvibili senza un server centrale.

Unione personalizzata — per app con modelli di dati semplici (note, contatti), è possibile implementare regole di unione personalizzate. Ad esempio, per una nota: se il testo viene modificato in due versioni, unirle come concatenazione con un separatore. Conflitto risolto dall'utente — se l'unione automatica è impossibile, mostrare all'utente entrambe le versioni e lasciarlo scegliere. Dropbox (2024) utilizza questo approccio per i conflitti di file offline, creando copie con il prefisso “Conflicted Copy”.

Chiavi di idempotenza — protezione contro la duplicazione

Chiave di Idempotenza — un identificatore unico di operazione che il server utilizza per rilevare richieste duplicate. Se il client invia la stessa richiesta con la stessa chiave, il server restituisce il risultato dell'operazione già completata senza eseguirla di nuovo. Questo è criticamente importante per Offline Queue, dove i reinvii sono possibili a causa di errori di rete.

Il formato della chiave di idempotenza è un UUID o un hash dei parametri della richiesta. Il server deve memorizzare le chiavi completate insieme al risultato per un certo periodo (di solito 24 ore) per rilevare i duplicati. L'API Stripe (2024) è l'esempio di riferimento: la chiave viene passata nell'intestazione Idempotency-Key e le richieste ripetute con la stessa chiave restituiscono una risposta memorizzata nella cache.

Generazione lato client — la chiave viene creata sul client prima di inviare l'operazione e memorizzata nella tabella QueuedOperation. Al nuovo tentativo, la chiave non cambia. Architettura exactly-once — la combinazione di una chiave di idempotenza sul client e la deduplicazione sul server è l'unico modo per garantire che un'operazione non venga eseguita due volte.

kotlin
fun createOperation(type: OperationType, payload: String): QueuedOperation =
    QueuedOperation(
        id = UUID.randomUUID().toString(),
        type = type,
        endpoint = type.endpoint,
        payload = payload,
        timestamp = currentTimeMillis(),
        idempotencyKey = UUID.randomUUID().toString()
    )

Ogni operazione riceve due UUID: uno — l'identificatore del record nella coda, il secondo — la chiave di idempotenza per il server. La deduplicazione lato server tramite idempotencyKey garantisce che anche al reinvio, l'ordine non venga duplicato.

Domande frequenti

In che modo Offline Queue differisce dalla cache?

La cache memorizza copie di dati per la lettura rapida offline. Offline Queue memorizza le operazioni dell'utente per la successiva scrittura sul server. La cache funziona per la lettura, la coda per la scrittura. Entrambi i componenti possono coesistere in un'architettura offline-first.

Quale dimensione della coda è sicura per un dispositivo mobile?

Limite raccomandato — 100–500 operazioni. Di più crea rischio di overflow della memoria e lunga sincronizzazione al ripristino della rete. Al superamento del limite, l'app dovrebbe avvisare l'utente e suggerire di prioritizzare le operazioni. Limite ragionevole — 50 operazioni di aggiornamento + 10 operazioni di creazione.

Come gestire le operazioni obsolete nella coda?

Operazioni più vecchie di 7 giorni con zero successi vengono spostate in una dead letter queue. Analizzale manualmente: l'API potrebbe essere cambiata e l'endpoint potrebbe non esistere più. Pulizia automatica — un'attività HealthCheck eseguita quotidianamente elimina o archivia le operazioni scadute.

Cosa succede se un'operazione dipende da una precedente che non è stata ancora inviata?

Utilizza un grafo delle dipendenze (DAG): ogni operazione contiene un elenco di parentOperationId che devono essere completati prima del suo invio. Una query Room con ORDER BY parent restituisce le operazioni nella sequenza corretta. Invio a cascata — dopo il completamento di ogni operazione, verifica se le operazioni figlie sono sbloccate.

Come testare Offline Queue?

Utilizza il Network Less Tool in Android Emulator o Network Link Conditioner in iOS Simulator per simulare la perdita di rete. Scrivi test che aggiungono operazioni alla coda in modalità offline, ripristinano la connessione e verificano che tutte le operazioni siano state inviate ed elaborate dal server.

Riepilogo

  • Offline Queue — una coda FIFO di operazioni salvata localmente per l'invio dopo il ripristino della connessione.
  • Persistent storage (Room / SQLite) — necessario per preservare la coda al riavvio dell'app.
  • Exponential backoff con jitter — la strategia di ripetizione standard per evitare il sovraccarico del server.
  • Conflict resolution — LWW, OT, CRDT o regole personalizzate per risolvere le collisioni dei dati offline.
  • Chiave di idempotenza — UUID per ogni operazione per garantire la consegna exactly-once sul server.
  • Dead letter queue — isolamento delle operazioni problematiche dopo l'esaurimento dei tentativi per l'analisi manuale.
  • Miglior pratica per Android — WorkManager + Room + ExponentialBackoff — una combinazione provata di Google.

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche