Comprendi le query in tempo reale su larga scala

Leggi questo documento per avere indicazioni su come scalare la tua applicazione serverless oltre migliaia di operazioni al secondo o centinaia di migliaia di utenti simultanei. Questo documento include argomenti avanzati per aiutarti a comprendere il sistema in modo approfondito. Se hai appena iniziato con Cloud Firestore, consulta invece la guida rapida .

Cloud Firestore e gli SDK per dispositivi mobili/web Firebase forniscono un modello potente 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 reattive che non richiedono l'infrastruttura del server. Sebbene sia molto semplice mettere in funzione qualcosa, è utile comprendere i vincoli nei sistemi che compongono Cloud Firestore in modo che la tua app serverless si ridimensioni e funzioni bene quando il traffico aumenta.

Consulta le sezioni seguenti per consigli su come ridimensionare la tua app.

Scegli una posizione del database vicino ai tuoi utenti

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

Esempio di architettura dell'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 tuo database si trova in us-east1 , la connessione va anche a un frontend Cloud Firestore anch'esso in us-east1 . Queste connessioni durano a lungo 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 database Cloud Firestore influisce sulla latenza sperimentata dall'utente. Ad esempio, un utente in India la cui app comunica con un database in una regione Google Cloud nel Nord America potrebbe trovare l'esperienza più lenta e l'app meno scattante rispetto a se il database fosse invece situato più vicino, come in India o in un'altra parte dell'Asia .

Progettare per l'affidabilità

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

Abilita la 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, l'app rimane utilizzabile lavorando con i dati memorizzati nella cache locale. Ciò garantisce l'accesso ai dati anche quando gli utenti riscontrano connessioni Internet instabili o perdono completamente l'accesso per diverse ore o giorni. Per ulteriori dettagli sulla modalità offline, consulta Abilitare i dati offline .

Comprendere i tentativi automatici

Gli SDK Firebase si occupano di ritentare le operazioni e ristabilire le connessioni interrotte. Ciò aiuta a risolvere gli errori temporanei causati dal riavvio dei server o da problemi di rete tra il client e il database.

Scegli tra sedi regionali e multiregionali

Esistono diversi compromessi quando si sceglie tra località regionali e multiregionali. La differenza principale è il modo in cui i dati vengono replicati. Ciò determina le garanzie di disponibilità della tua app. Un'istanza multiregione offre una maggiore affidabilità del servizio e aumenta la durabilità dei dati, ma il compromesso è il costo.

Comprendere il sistema di query in tempo reale

Le query in tempo reale, chiamate anche listener di snapshot, consentono all'app di ascoltare le modifiche nel database e ricevere notifiche a bassa latenza non appena i dati cambiano. Un'app può ottenere lo stesso risultato eseguendo periodicamente il polling del database per gli aggiornamenti, ma spesso è più lento, più costoso e richiede più codice. Per esempi su come impostare e utilizzare le query in tempo reale, vedere Ottenere aggiornamenti in tempo reale . Le sezioni seguenti approfondiscono in dettaglio il funzionamento degli snapshot listener e descrivono alcune delle migliori pratiche per ridimensionare 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 per dispositivi mobili.

Il client A scrive al 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 client B ascolta gli aggiornamenti nella stessa raccolta utilizzando un listener di snapshot. Il client B riceve una notifica immediata ogni volta che qualcuno crea un nuovo messaggio. Il diagramma seguente mostra l'architettura dietro un listener di snapshot:

Architettura di una connessione del listener di snapshot

La seguente sequenza di eventi ha luogo quando il client B connette un listener di snapshot al database:

  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 di Cloud Firestore interroga il sistema di archiviazione sottostante per eseguire il bootstrap del set di dati. Carica l'intero set di risultati dei documenti corrispondenti. La chiamiamo query di polling . Il sistema valuta quindi le regole di sicurezza 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 alla modalità di ascolto . Il listener si registra con un gestore di sottoscrizione e attende gli aggiornamenti ai dati.
  4. Il client A ora invia un'operazione di scrittura per modificare un documento.
  5. Il database conferma la modifica del documento nel proprio sistema di archiviazione.
  6. A livello transazionale, il sistema inserisce lo stesso aggiornamento in un registro delle modifiche interno. Il registro delle modifiche stabilisce un ordine rigoroso delle modifiche man mano che si verificano.
  7. Il registro delle modifiche a sua volta distribuisce i dati aggiornati a un pool di gestori di sottoscrizioni.
  8. Viene eseguito un matcher di query inversa per verificare se il documento aggiornato corrisponde a qualsiasi listener di snapshot attualmente registrato. In questo esempio, il documento corrisponde al listener di snapshot del Cliente B. Come suggerisce il nome, puoi pensare al matcher di query inversa come una normale query di database ma eseguita al contrario. Invece di cercare tra i documenti per trovare quelli che corrispondono a una query, ricerca in modo efficiente tra le query per trovare quelli che corrispondono a un documento in arrivo. Una volta trovata una corrispondenza, il sistema inoltra il documento in questione agli ascoltatori dello snapshot. Quindi il sistema valuta le regole di sicurezza 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 client B e viene attivato il callback onSnapshot . Se la persistenza locale è abilitata, l'SDK applica l'aggiornamento anche alla cache locale.

Una parte fondamentale della scalabilità di Cloud Firestore dipende dal fan-out dal registro delle modifiche ai gestori delle sottoscrizioni e ai server frontend. Il fan-out consente a una singola modifica dei dati di propagarsi in modo efficiente per servire milioni di query in tempo reale e utenti connessi. Eseguendo numerose repliche di tutti questi componenti su più zone (o più regioni nel caso di una distribuzione su più regioni), Cloud Firestore raggiunge disponibilità e scalabilità elevate.

Vale la pena notare che tutte le operazioni di lettura eseguite dagli SDK per dispositivi mobili e Web seguono il modello riportato sopra. Eseguono una query di polling seguita dalla modalità di ascolto per mantenere le garanzie di coerenza. Ciò vale anche per ascoltatori in tempo reale, chiamate per recuperare un documento e query one-shot . Puoi pensare ai recuperi di singoli documenti e alle 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.

Comprendere l'elevato traffico di scrittura nel sistema

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

I log delle modifiche di Cloud Firestore che guidano le query in tempo reale si ridimensionano automaticamente in orizzontale all'aumentare del traffico di scrittura. Man mano che la velocità di scrittura di un database aumenta oltre ciò che un singolo server può gestire, il log delle modifiche viene suddiviso su più server e l'elaborazione delle query inizia a consumare dati da più gestori di sottoscrizione anziché da uno solo. Dal punto di vista del client e dell'SDK, tutto ciò è trasparente e non è richiesta alcuna azione da parte dell'app quando si verificano delle divisioni. Il diagramma seguente illustra la scalabilità delle query in tempo reale:

Architettura del fan-out del log delle modifiche

Il ridimensionamento automatico ti consente di aumentare il traffico di scrittura senza limiti, ma man mano che il traffico aumenta, il sistema potrebbe impiegare del tempo per rispondere. Seguire le raccomandazioni 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ò soddisfare senza precauzioni. I carichi di lavoro batch, come l'importazione di un set di dati di grandi dimensioni, tuttavia, possono accelerare le scritture troppo rapidamente. Mentre progetti la tua app, tieni presente da dove proviene il traffico di scrittura.

Comprendere 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 registro delle modifiche di Cloud Firestore garantisce una forte coerenza, il che significa che la tua app non riceve mai notifiche di aggiornamenti fuori servizio rispetto a quando il database ha confermato le modifiche ai dati. Ciò semplifica lo sviluppo di app rimuovendo i casi limite relativi alla coerenza dei dati.

Questa pipeline connessa significa che un'operazione di scrittura che causa hotspot o conflitti di blocco può influire negativamente sulle operazioni di lettura. Quando le operazioni di scrittura falliscono o subiscono limitazioni, una lettura potrebbe bloccarsi in attesa di dati coerenti dal registro delle modifiche. Se ciò accade nella tua app, potresti vedere sia operazioni di scrittura lente che tempi di risposta lenti correlati per le query. Evitare gli hotspot è la chiave per evitare questo problema.

Mantieni i documenti e scrivi le operazioni di piccole dimensioni

Quando crei app con listener di snapshot, in genere desideri che gli utenti vengano a conoscenza rapidamente delle modifiche ai dati. Per raggiungere questo obiettivo, cerca di mantenere le cose piccole. Il sistema può inviare piccoli documenti con decine di campi attraverso il sistema molto rapidamente. Documenti più grandi con centinaia di campi e dati di grandi dimensioni richiedono più tempo per l'elaborazione.

Allo stesso modo, privilegiare operazioni di commit e scrittura brevi e veloci per mantenere bassa la latenza. Batch di grandi dimensioni potrebbero offrire una produttività più elevata dal punto di vista dello scrittore, ma potrebbero effettivamente aumentare il tempo di notifica per gli ascoltatori di snapshot. Ciò è spesso controintuitivo rispetto all'utilizzo di altri sistemi di database in cui è possibile utilizzare l'invio in batch per migliorare le prestazioni.

Utilizza ascoltatori efficienti

Man mano che le velocità di scrittura per il tuo database aumentano, Cloud Firestore suddivide l'elaborazione dei dati su più server. L'algoritmo di partizionamento orizzontale di Cloud Firestore tenta di co-localizzare i dati dalla stessa raccolta o gruppo di raccolte sullo stesso server di registro delle modifiche. Il sistema cerca di massimizzare la possibile velocità di scrittura mantenendo il più basso possibile il numero di server coinvolti nell'elaborazione di una query.

Tuttavia, alcuni modelli potrebbero comunque portare a un comportamento non ottimale per gli ascoltatori di snapshot. Ad esempio, se la tua app archivia la maggior parte dei dati in un'unica raccolta di grandi dimensioni, l'ascoltatore potrebbe dover connettersi a più server per ricevere tutti i dati di cui ha bisogno. Ciò rimane vero 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 passare a molti server diversi. Potrebbe funzionare meglio suddividere i dati in raccolte più piccole con velocità di scrittura inferiori.

Ciò è simile a pensare alle query sulle prestazioni in un database relazionale che richiedono scansioni complete della tabella. In un database relazionale, una query che richiede una scansione completa della tabella è l'equivalente di un listener di snapshot che controlla una raccolta ad alto tasso di abbandono. Potrebbe essere eseguito lentamente rispetto a una query che il database può fornire utilizzando un indice più specifico. Una query con un indice più specifico è come un ascoltatore di snapshot che guarda un singolo documento o una raccolta che cambia meno spesso. Dovresti caricare il test della tua app per comprendere meglio il comportamento e le esigenze del tuo caso d'uso.

Continua a interrogare velocemente le query

Un'altra parte fondamentale delle query reattive in tempo reale consiste nel garantire che la query di polling per eseguire il bootstrap dei dati sia veloce ed efficiente. La prima volta che un nuovo listener di snapshot si connette, il listener deve caricare l'intero set di risultati e inviarlo al dispositivo dell'utente. Le query lente rendono la tua app meno reattiva. Ciò include, ad esempio, query che tentano di leggere molti documenti o query che non utilizzano gli indici appropriati.

In alcune circostanze, un ascoltatore potrebbe anche tornare dallo stato di ascolto allo stato di polling. Ciò avviene automaticamente ed è trasparente per gli SDK e la tua app. Le seguenti condizioni potrebbero attivare uno stato di polling:

  • Il sistema ribilancia un registro delle modifiche a causa di modifiche nel carico.
  • Gli hotspot causano scritture non riuscite o ritardate nel database.
  • I riavvii temporanei del server influiscono temporaneamente sugli ascoltatori.

Se le query di polling sono sufficientemente veloci, lo stato del polling diventa trasparente per gli utenti della tua app.

Preferisci gli ascoltatori longevi

Aprire e mantenere in vita gli ascoltatori il più a lungo possibile è spesso il modo più conveniente per creare un'app che utilizza Cloud Firestore. Quando utilizzi Cloud Firestore, ti vengono fatturati i documenti restituiti alla tua app e non il mantenimento di una connessione aperta. Un listener di snapshot di lunga durata legge solo i dati necessari per servire la query per tutta la sua vita. Ciò include un'operazione di polling iniziale seguita da notifiche quando i dati cambiano effettivamente. Le query one-shot, invece, rileggono i dati che potrebbero non essere cambiati dall'ultima esecuzione della query da parte dell'app.

Nei casi in cui la tua app deve consumare una velocità elevata 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 query one-shot eseguite con una frequenza inferiore.

Qual è il prossimo