Comprendi le query in tempo reale su larga scala

Leggi questo documento per indicazioni sulla scalabilità della tua app serverless oltre migliaia di operazioni al secondo o centinaia di migliaia di utenti in contemporanea. Questo documento include argomenti avanzati per aiutarti a comprendere in modo approfondito il sistema. Se stai appena iniziando a utilizzare Cloud Firestore, consulta la guida rapida.

Cloud Firestore e gli SDK mobile/web di Firebase forniscono un modello efficace per lo sviluppo di app serverless in cui il codice lato client accede direttamente al database. Gli SDK consentono ai client di ascoltare gli aggiornamenti dei dati in tempo reale. Puoi utilizzare gli aggiornamenti in tempo reale per creare app adattabili che non richiedono un'infrastruttura di server. Sebbene sia molto facile rendere operativa un'app, aiuta a comprendere i vincoli nei sistemi che compongono Cloud Firestore in modo che la tua app serverless scala e funzioni bene quando il traffico aumenta.

Per consigli su come scalare la tua app, consulta le sezioni seguenti.

Scegli una posizione del database vicina agli utenti

Il seguente diagramma illustra l'architettura di un'app in tempo reale:

Esempio di architettura delle app in tempo reale

Quando un'app in esecuzione sul dispositivo di un utente (mobile o web) stabilisce una connessione a Cloud Firestore, la connessione viene instradata a un server frontend Cloud Firestore nella stessa regione in cui si trova il database. Ad esempio, se il database si trova in us-east1, la connessione viene trasferita anche a un frontend Cloud Firestore anche in us-east1. Queste connessioni sono di lunga durata e rimangono aperte finché non vengono chiuse esplicitamente dall'app. Il frontend legge i dati dai sistemi di archiviazione Cloud Firestore sottostanti.

La distanza tra la posizione fisica di un utente e la posizione del Cloud Firestore database influisce sulla latenza riscontrata dall'utente. Ad esempio, un utente in India la cui app comunica con un database in una regione Google Cloud del Nord America potrebbe trovare l'esperienza più lenta e l'app meno scattante rispetto a se il database si trovasse più vicino, ad esempio in India o in un'altra parte dell'Asia.

Progetta per l'affidabilità

I seguenti argomenti migliorano o influiscono sull'affidabilità della tua app:

Abilita modalità offline

Gli SDK Firebase forniscono la persistenza dei dati offline. Se l'app sul dispositivo dell'utente non riesce a connettersi a Cloud Firestore, rimane utilizzabile lavorando con i dati memorizzati nella cache locale. Ciò garantisce l'accesso ai dati anche quando gli utenti riscontrano connessioni a internet instabile o perdono completamente l'accesso per diverse ore o giorni. Per maggiori dettagli sulla modalità offline, vedi Attivare i dati offline.

Informazioni sui nuovi tentativi automatici

Gli SDK Firebase si occupano di riprovare le operazioni e di ristabilire le connessioni interrotte. In questo modo puoi aggirare gli errori temporanei causati dall'riavvio dei server o da problemi di rete tra il client e il database.

Scegliere tra località regionali e multiregionali

Esistono diversi compromessi quando si sceglie tra località regionali e su più regioni. La differenza principale è il modo in cui i dati vengono replicati. In questo modo, viene migliorata la disponibilità della tua app. Un'istanza multiregione offre una maggiore affidabilità di pubblicazione e aumenta la durabilità dei dati, ma il costo è maggiore.

Informazioni sul sistema di query in tempo reale

Le query in tempo reale, chiamate anche ascoltatori di snapshot, consentono all'app di ascoltare le modifiche nel database e di ricevere notifiche a bassa latenza non appena i dati subiscono modifiche. Un'app può ottenere lo stesso risultato eseguendo periodicamente il polling del database per verificare la presenza di aggiornamenti, ma spesso è più lenta, più costosa e richiede più codice. Per esempi su come configurare e utilizzare le query in tempo reale, consulta Ricevere aggiornamenti in tempo reale. Le sezioni seguenti illustrano in dettaglio il funzionamento degli ascoltatori di istantanee e descrivono alcune delle best practice per scalare le query in tempo reale mantenendo le prestazioni.

Immagina due utenti che si connettono a Cloud Firestore tramite un'app di messaggistica creata con uno degli SDK mobile.

Il client A scrive nel database per aggiungere e aggiornare i documenti in una raccolta chiamata chatroom:

collection chatroom:
    document message1:
      from: 'Sparky'
      message: 'Welcome to Cloud Firestore!'

    document message2:
      from: 'Santa'
      message: 'Presents are coming'

Il cliente B ascolta gli aggiornamenti nella stessa raccolta utilizzando un ascoltatore di istantanee. Il cliente B riceve una notifica immediata ogni volta che qualcuno crea un nuovo messaggio. Il seguente diagramma mostra l'architettura alla base di un listener di snapshot:

Architettura di una connessione di listener degli snapshot

Quando il client B connette un ascoltatore di snapshot al database, si verifica la seguente sequenza di eventi:

  1. Il client B apre una connessione a Cloud Firestore e registra un ascoltatore effettuando una chiamata a onSnapshot(collection("chatroom")) tramite l'SDK Firebase. Questo ascoltatore può rimanere attivo per ore.
  2. Il frontend Cloud Firestore esegue query sul sistema di archiviazione sottostante per avviare il set di dati. Carica l'intero set di risultati dei documenti corrispondenti. Si tratta di una query di polling. Il sistema valuta quindi le regole di sicurezza di Firebase del database per verificare che l'utente possa accedere a questi dati. Se l'utente è autorizzato, il database restituisce i dati all'utente.
  3. La query del client B passa quindi in modalità di ascolto. L'ascoltatore si registra con un gestore degli abbonamenti e attende gli aggiornamenti dei dati.
  4. Il client A ora invia un'operazione di scrittura per modificare un documento.
  5. Il database esegue il commit della modifica del documento nel suo sistema di archiviazione.
  6. A livello di transazioni, il sistema esegue il commit dello stesso aggiornamento in un log delle modifiche interno. Il log delle modifiche stabilisce un ordine rigoroso delle modifiche man mano che vengono apportate.
  7. Il log delle modifiche, a sua volta, invia i dati aggiornati a un pool di gestori degli abbonamenti.
  8. Viene eseguito un corrispondenza di query inversa per verificare se il documento aggiornato corrisponde a eventuali listener di istantanee attualmente registrati. In questo esempio, il documento corrisponde all'ascoltatore di snapshot del cliente B. Come suggerisce il nome, puoi considerare il correlatore di query inversa come una normale query sul database, ma eseguita in modo inverso. Invece di cercare tra i documenti quelli corrispondenti a una query, cerca in modo efficiente tra le query quelle corrispondenti a un documento in entrata. Una volta trovata una corrispondenza, il sistema inoltra il documento in questione agli ascoltatori degli snapshot. Quindi il sistema valuta le regole di sicurezza di Firebase del database per garantire che solo gli utenti autorizzati ricevano i dati.
  9. Il sistema inoltra l'aggiornamento del documento all'SDK sul dispositivo del cliente B e viene attivato il callback onSnapshot. Se la persistenza locale è attivata, l'SDK applica l'aggiornamento anche alla cache locale.

Un aspetto fondamentale della scalabilità di Cloud Firestore dipende dalla distribuzione dal log delle modifiche agli handler degli abbonamenti e ai server frontend. Il fan-out consente di propagare in modo efficiente una singola modifica dei dati per gestire milioni di query in tempo reale e di utenti connessi. Eseguendo molte repliche di tutti questi componenti su più zone (o più regioni nel caso di un deployment multi-regione), Cloud Firestore raggiunge un'alta disponibilità e scalabilità.

È importante notare che tutte le operazioni di lettura emesse dagli SDK mobile e web seguono il modello riportato sopra. Eseguono una query di polling seguita dalla modalità di ascolto per mantenere le garanzie di coerenza. Questo vale anche per gli ascoltatori in tempo reale, le chiamate per recuperare un documento e le query una tantum. Puoi considerare i singoli recupero di documenti e le query one-shot come ascoltatori di snapshot di breve durata che presentano vincoli simili in termini di prestazioni.

Applica le best practice per scalare le query in tempo reale

Applica le seguenti best practice per progettare query scalabili in tempo reale.

Comprendi il traffico di scrittura elevato nel sistema

Questa sezione ti aiuta a capire come il sistema risponde a un numero crescente di richieste di scrittura.

I log delle modifiche Cloud Firestore che generano le query in tempo reale si scalano automaticamente in orizzontale con l'aumento del traffico di scrittura. Man mano che la frequenza di scrittura per un database aumenta oltre il limite che un singolo server può gestire, il log delle modifiche viene suddiviso su più server e l'elaborazione delle query inizia a consumare i dati di più gestori degli abbonamenti anziché di uno. Dal punto di vista del client e dell'SDK, tutto è trasparente e non è richiesta alcuna azione da parte dell'app quando si verificano le suddivisioni. Il seguente diagramma mostra come vengono scalate le query in tempo reale:

Architettura di fan-out del log delle modifiche

La scalabilità automatica ti consente di aumentare il traffico di scrittura senza limiti, ma con l'aumento del traffico, il sistema potrebbe impiegare un po' di tempo per rispondere. Segui i consigli della regola 5-5-5 per evitare di creare un hotspot di scrittura. Key Visualizer è uno strumento utile per analizzare gli hotspot di scrittura.

Molte app hanno una crescita organica prevedibile, che Cloud Firestore può gestire senza precauzioni. Tuttavia, i carichi di lavoro batch come l'importazione di un set di dati di grandi dimensioni possono aumentare troppo rapidamente le scritture. Durante la progettazione dell'app, tieni conto della provenienza del traffico in scrittura.

Scopri come interagiscono le scritture e le letture

Puoi pensare al sistema di query in tempo reale come a una pipeline che collega le operazioni di scrittura ai lettori. Ogni volta che un documento viene creato, aggiornato o eliminato, la modifica si propaga dal sistema di archiviazione agli ascoltatori attualmente registrati. La struttura del log delle modifiche di Cloud Firestore garantisce una forte coerenza, il che significa che la tua app non riceve mai notifiche di aggiornamenti fuori sequenza rispetto al momento in cui il database ha eseguito il commit delle modifiche ai dati. In questo modo, lo sviluppo delle app viene semplificato rimuovendo i casi limite relativi alla coerenza dei dati.

Questa pipeline collegata significa che un'operazione di scrittura che causa hotspot o contese per i blocchi può influire negativamente sulle operazioni di lettura. Quando le operazioni di scrittura non riescono o sono limitate, una lettura potrebbe bloccarsi in attesa di dati coerenti dal log delle modifiche. In questo caso, potresti notare operazioni di scrittura lente e tempi di risposta lenti correlati per le query. Evitare gli hotspot è la chiave per evitare questo problema.

Mantieni i documenti e le operazioni di scrittura di piccole dimensioni

Quando crei app con ascoltatori di istantanee, in genere vuoi che gli utenti vengano informati rapidamente delle modifiche ai dati. Per farlo, cerca di mantenere le cose semplici. Il sistema è in grado di inviare rapidamente documenti di piccole dimensioni con decine di campi al sistema. I documenti più grandi con centinaia di campi e dati di grandi dimensioni richiedono più tempo per essere elaborati.

Allo stesso modo, favorisci operazioni di commit e scrittura brevi e veloci per mantenere bassa la latenza. I batch di grandi dimensioni potrebbero offrire una velocità effettiva più elevata dal punto di vista dell'autore, ma in realtà potrebbero aumentare il tempo di notifica per i listener di snapshot. Spesso questo approccio è controintuitivo rispetto all'utilizzo di altri sistemi di database in cui potresti utilizzare il batching per migliorare le prestazioni.

Utilizzare listener efficienti

Con l'aumento delle frequenze di scrittura del database, Cloud Firestore suddivide l'elaborazione dei dati su molti server. L'algoritmo di sharding di Cloud Firestore tenta di collocare i dati della stessa raccolta o dello stesso gruppo di raccolte sullo stesso server di log delle modifiche. Il sistema tenta di massimizzare il possibile throughput di scrittura mantenendo il numero di server coinvolti nell'elaborazione di una query il più basso possibile.

Tuttavia, alcuni pattern potrebbero comunque comportare un comportamento non ottimale per gli ascoltatori degli snapshot. Ad esempio, se la tua app memorizza la maggior parte dei dati in una raccolta di grandi dimensioni, l'ascoltatore potrebbe dover connettersi a molti server per ricevere tutti i dati di cui ha bisogno. Questo vale anche se applichi un filtro di query. La connessione a molti server aumenta il rischio di risposte più lente.

Per evitare queste risposte più lente, progetta lo schema e l'app in modo che il sistema possa servire gli ascoltatori senza dover accedere a molti server diversi. Potrebbe essere preferibile suddividere i dati in raccolte più piccole con velocità di scrittura inferiori.

È simile alle query sul rendimento in un database relazionale che richiedono scansioni complete della tabella. In un database relazionale, una query che richiede una scansione completa della tabella è equivalente a un ascoltatore di snapshot che monitora una raccolta con un tasso di turnover elevato. Potrebbe funzionare lentamente rispetto a una query che il database può gestire usando un indice più specifico. Una query con un indice più specifico è come un listener di snapshot che controlla un singolo documento o una raccolta che cambia con minore frequenza. Devi caricare testare la tua app per comprendere meglio il comportamento e le esigenze del tuo caso d'uso.

Mantieni le query di polling rapide

Un'altra parte fondamentale delle query in tempo reale dinamiche consiste nell'assicurarsi che la query di polling per l'avvio dei dati sia rapida ed efficiente. La prima volta che un nuovo ascoltatore di istantanee si connette, deve caricare l'intero insieme di risultati e inviarlo al dispositivo dell'utente. Le query lente rendono la tua app meno reattiva. Sono incluse, ad esempio, le query che tentano di leggere molti documenti o che non utilizzano gli indici appropriati.

In alcune circostanze, un ascoltatore potrebbe anche passare da uno stato di ascolto a uno di polling. Questo avviene automaticamente ed è trasparente per gli SDK e la tua app. Le seguenti condizioni potrebbero attivare uno stato di polling:

  • Il sistema riequilibra un log delle modifiche a causa delle variazioni del carico.
  • Gli hotspot causano scritture non riuscite o in ritardo nel database.
  • I riavvii temporanei del server influiscono temporaneamente sugli ascoltatori.

Se le query di polling sono abbastanza rapide, uno stato di polling diventa trasparente per gli utenti della tua app.

Favorire gli ascoltatori di lunga durata

Aprire e mantenere attivi gli ascoltatori il più a lungo possibile è spesso il modo più economico per creare un'app che utilizza Cloud Firestore. Quando utilizzi Cloud Firestore, ti vengono addebitati i documenti restituiti alla tua app e non il mantenimento di una connessione aperta. Un ascoltatore di snapshot a lungo termine legge solo i dati di cui ha bisogno per eseguire la query per tutta la sua durata. Ciò include un'operazione di polling iniziale seguita da notifiche quando i dati cambiano effettivamente. Le query una tantum, invece, rileggono i dati che potrebbero non essere stati modificati dall'ultima esecuzione della query da parte dell'app.

Nei casi in cui la tua app deve utilizzare un'elevata frequenza di dati, i listener di snapshot potrebbero non essere appropriati. Ad esempio, se il tuo caso d'uso invia molti documenti al secondo attraverso una connessione per un periodo di tempo prolungato, potrebbe essere meglio optare per le query one-shot da eseguire a una frequenza inferiore.

Passaggi successivi