Utilizza le best practice elencate qui come riferimento rapido durante la creazione di un'applicazione che utilizza Cloud Firestore.
Posizione della banca dati
Quando crei l'istanza del database, seleziona la posizione del database più vicina agli utenti e alle risorse di calcolo. Gli hop di rete di vasta portata sono più soggetti a errori e aumentano la latenza delle query.
Per massimizzare la disponibilità e la durabilità della tua applicazione, seleziona una posizione in più aree geografiche e colloca le risorse di calcolo critiche in almeno due aree geografiche.
Seleziona una località regionale per costi inferiori, per una minore latenza di scrittura se la tua applicazione è sensibile alla latenza o per la co-ubicazione con altre risorse GCP .
ID documento
- Evita gli ID documento
.
e..
- Evita di utilizzare
/
barre negli ID documento. Non utilizzare ID documento che aumentano in modo monotono come:
-
Customer1
,Customer2
,Customer3
, ... -
Product 1
,Product 2
,Product 3
, ...
Tali ID sequenziali possono portare a hotspot che influiscono sulla latenza.
-
Nomi di campo
Evita i seguenti caratteri nei nomi dei campi perché richiedono caratteri di escape aggiuntivi:
-
.
periodo -
[
parentesi sinistra -
]
parentesi quadra destra -
*
asterisco -
`
apice inverso
-
Indici
Evitare di utilizzare troppi indici. Un numero eccessivo di indici può aumentare la latenza di scrittura e aumentare i costi di archiviazione per le voci di indice.
Tieni presente che l'indicizzazione di campi con valori crescenti in modo monotono, come i timestamp, può portare a hotspot che influiscono sulla latenza per le applicazioni con velocità di lettura e scrittura elevate.
Esenzioni dall'indice
Per la maggior parte delle app, puoi fare affidamento sull'indicizzazione automatica e su eventuali collegamenti ai messaggi di errore per gestire i tuoi indici. Tuttavia, potresti voler aggiungere esenzioni a campo singolo nei seguenti casi:
Caso | Descrizione |
---|---|
Grandi campi stringa | Se disponi di un campo stringa che contiene spesso valori stringa lunghi che non utilizzi per le query, puoi ridurre i costi di archiviazione esentando il campo dall'indicizzazione. |
Velocità di scrittura elevate in una raccolta contenente documenti con valori sequenziali | Se indicizzi un campo che aumenta o diminuisce in sequenza tra i documenti in una raccolta, come un timestamp, la velocità massima di scrittura nella raccolta è di 500 scritture al secondo. Se non esegui query in base al campo con valori sequenziali, puoi esentare il campo dall'indicizzazione per ignorare questo limite. In un caso d'uso IoT con un'elevata velocità di scrittura, ad esempio, una raccolta contenente documenti con un campo timestamp potrebbe avvicinarsi al limite di 500 scritture al secondo. |
Campi TTL | Se utilizzi i criteri TTL (time-to-live) , tieni presente che il campo TTL deve essere un timestamp. L'indicizzazione sui campi TTL è abilitata per impostazione predefinita e può influire sulle prestazioni a tassi di traffico più elevati. Come best practice, aggiungi esenzioni a campo singolo per i tuoi campi TTL. |
Matrice di grandi dimensioni o campi mappa | I campi matrice o mappa di grandi dimensioni possono avvicinarsi al limite di 40.000 voci di indice per documento. Se non stai eseguendo una query in base a un campo di matrice o mappa di grandi dimensioni, dovresti esentarlo dall'indicizzazione. |
Operazioni di lettura e scrittura
La velocità massima esatta con cui un'app può aggiornare un singolo documento dipende fortemente dal carico di lavoro. Per ulteriori informazioni, vedere Aggiornamenti a un singolo documento .
Utilizzare le chiamate asincrone, ove disponibili, anziché le chiamate sincrone. Le chiamate asincrone riducono al minimo l'impatto della latenza. Ad esempio, si consideri un'applicazione che necessita del risultato di una ricerca di documenti e dei risultati di una query prima di eseguire il rendering di una risposta. Se la ricerca e la query non hanno una dipendenza dati, non è necessario attendere in modo sincrono il completamento della ricerca prima di avviare la query.
Non utilizzare offset. Invece, usa i cursori . L'utilizzo di un offset evita solo di restituire i documenti saltati all'applicazione, ma questi documenti vengono comunque recuperati internamente. I documenti ignorati influiscono sulla latenza della query e all'applicazione vengono fatturate le operazioni di lettura necessarie per recuperarli.
Tentativi di transazione
Gli SDK e le librerie client di Cloud Firestore ritentano automaticamente le transazioni non riuscite per gestire gli errori temporanei. Se la tua applicazione accede a Cloud Firestore direttamente tramite le API REST o RPC anziché tramite un SDK, la tua applicazione dovrebbe implementare tentativi di transazione per aumentare l'affidabilità.
Aggiornamenti in tempo reale
Per le migliori prestazioni dell'ascoltatore di istantanee , mantieni i tuoi documenti piccoli e controlla la velocità di lettura dei tuoi clienti. Le seguenti raccomandazioni forniscono linee guida per ottimizzare le prestazioni. Il superamento di questi consigli può comportare un aumento della latenza delle notifiche.
Raccomandazione | Dettagli |
---|---|
Riduci il tasso di abbandono dell'ascoltatore di istantanee | Evita ascoltatori che si agitano frequentemente, soprattutto quando il tuo database è sottoposto a un carico di scrittura significativo Idealmente, la tua applicazione dovrebbe configurare tutti i listener di snapshot richiesti subito dopo aver aperto una connessione a Cloud Firestore. Dopo aver configurato i listener di snapshot iniziali, dovresti evitare di aggiungere o rimuovere rapidamente listener di snapshot nella stessa connessione. Per garantire la coerenza dei dati, Cloud Firestore deve eseguire il priming di ogni nuovo snapshot listener dai dati di origine e quindi aggiornarsi sulle nuove modifiche. A seconda della velocità di scrittura del database, questa può essere un'operazione costosa. I tuoi snapshot listener possono riscontrare una maggiore latenza se aggiungi o rimuovi spesso snapshot listener ai riferimenti. In generale, un listener costantemente collegato offre prestazioni migliori rispetto al collegamento e allo scollegamento di un listener in quella posizione per la stessa quantità di dati. Per prestazioni ottimali, gli snapshot listener dovrebbero avere una durata di almeno 30 secondi. Se riscontri problemi di prestazioni dell'ascoltatore nella tua app, prova a monitorare gli ascolti e i non ascolti dell'app per determinare se potrebbero verificarsi troppo frequentemente. |
Limita i listener di snapshot per client | 100 Mantieni il numero di listener di istantanee per client al di sotto di 100. |
Limita la velocità di scrittura della raccolta | 1.000 operazioni/secondo Mantieni la frequenza delle operazioni di scrittura per una singola raccolta al di sotto di 1.000 operazioni al secondo. |
Limita la frequenza di push del singolo client | 1 documento/secondo Mantieni la velocità dei documenti inviati dal database a un singolo cliente al di sotto di 1 documento/secondo. |
Limita la frequenza di push del client globale | 1.000.000 documenti/secondo Mantieni la velocità dei documenti inviati dal database a tutti i client al di sotto di 1.000.000 di documenti al secondo. Questo è un limite morbido. Cloud Firestore non ti impedisce di superare questa soglia ma influisce notevolmente sulle prestazioni. |
Limita il payload del singolo documento | 10 KiB/secondo Mantieni la dimensione massima del documento scaricato da un singolo cliente al di sotto di 10 KiB/secondo. |
Limita il payload del documento globale | 1 GiB/secondo Mantieni la dimensione massima del documento scaricato su tutti i client al di sotto di 1 GiB/secondo. |
Limita il numero di campi per documento | 100 I tuoi documenti dovrebbero avere meno di 100 campi. |
Comprendi i limiti standard di Cloud Firestore | Tieni presente i limiti standard per Cloud Firestore . Prestare particolare attenzione al limite di 1 scrittura al secondo per i documenti e al limite di 1.000.000 di connessioni simultanee per database. Questi sono limiti flessibili che Cloud Firestore non ti impedisce di superare. Tuttavia, il superamento di questi limiti potrebbe influire sulle prestazioni, a seconda delle velocità totali di lettura e scrittura. |
Progettare per la scala
Le procedure consigliate seguenti descrivono come evitare situazioni che creano problemi di contesa.
Aggiornamenti a un singolo documento
Mentre progetti la tua app, considera quanto velocemente la tua app aggiorna i singoli documenti. Il modo migliore per caratterizzare le prestazioni del tuo carico di lavoro è eseguire il test del carico. La velocità massima esatta con cui un'app può aggiornare un singolo documento dipende fortemente dal carico di lavoro. I fattori includono la velocità di scrittura, la contesa tra le richieste e il numero di indici interessati.
Un'operazione di scrittura di un documento aggiorna il documento e gli eventuali indici associati e Cloud Firestore applica l'operazione di scrittura in modo sincrono a un quorum di repliche. A velocità di scrittura sufficientemente elevate, il database inizierà a riscontrare contesa, latenza più elevata o altri errori.
Elevate velocità di lettura, scrittura ed eliminazione su un intervallo di documenti ristretto
Evita velocità di lettura o scrittura elevate per chiudere lessicograficamente i documenti, altrimenti la tua applicazione riscontrerà errori di contesa. Questo problema è noto come hotspotting e la tua applicazione può riscontrare hotspotting se esegue una delle seguenti operazioni:
Crea nuovi documenti a una velocità molto elevata e alloca i propri ID in modo monotono.
Cloud Firestore alloca gli ID documento utilizzando un algoritmo di dispersione. Non dovresti riscontrare hotspotting durante le scritture se crei nuovi documenti utilizzando ID documento automatici.
Crea nuovi documenti a una velocità elevata in una raccolta con pochi documenti.
Crea nuovi documenti con un campo in aumento monotono, come un timestamp, a una velocità molto elevata.
Elimina i documenti in una raccolta a una velocità elevata.
Scrive nel database a una velocità molto elevata senza aumentare gradualmente il traffico.
Evita di saltare i dati cancellati
Evita le query che ignorano i dati eliminati di recente. Una query potrebbe dover ignorare un numero elevato di voci di indice se i primi risultati della query sono stati eliminati di recente.
Un esempio di un carico di lavoro che potrebbe dover ignorare molti dati eliminati è quello che tenta di trovare gli elementi di lavoro in coda meno recenti. La query potrebbe essere simile a:
docs = db.collection('WorkItems').order_by('created').limit(100)
delete_batch = db.batch()
for doc in docs.stream():
finish_work(doc)
delete_batch.delete(doc.reference)
delete_batch.commit()
Ogni volta che questa query viene eseguita, esegue la scansione delle voci di indice per il campo created
su tutti i documenti eliminati di recente. Questo rallenta le query.
Per migliorare le prestazioni, utilizza il metodo start_at
per trovare il punto di partenza migliore. Per esempio:
completed_items = db.collection('CompletionStats').document('all stats').get()
docs = db.collection('WorkItems').start_at(
{'created': completed_items.get('last_completed')}).order_by(
'created').limit(100)
delete_batch = db.batch()
last_completed = None
for doc in docs.stream():
finish_work(doc)
delete_batch.delete(doc.reference)
last_completed = doc.get('created')
if last_completed:
delete_batch.update(completed_items.reference,
{'last_completed': last_completed})
delete_batch.commit()
NOTA: l'esempio precedente utilizza un campo in aumento monotono che è un anti-pattern per velocità di scrittura elevate.
Aumento del traffico
Dovresti aumentare gradualmente il traffico verso nuove raccolte o chiudere lessicograficamente i documenti per dare a Cloud Firestore il tempo sufficiente per preparare i documenti per l'aumento del traffico. Ti consigliamo di iniziare con un massimo di 500 operazioni al secondo per una nuova raccolta e quindi aumentare il traffico del 50% ogni 5 minuti. Allo stesso modo puoi aumentare il tuo traffico di scrittura, ma tieni a mente i limiti standard di Cloud Firestore . Assicurati che le operazioni siano distribuite in modo relativamente uniforme in tutta la gamma di tasti. Questa è chiamata la regola "500/50/5".
Migrazione del traffico verso una nuova raccolta
L'aumento graduale è particolarmente importante se esegui la migrazione del traffico delle app da una raccolta a un'altra. Un modo semplice per gestire questa migrazione è leggere dalla vecchia raccolta e, se il documento non esiste, leggere dalla nuova raccolta. Tuttavia, ciò potrebbe causare un improvviso aumento del traffico per chiudere lessicograficamente i documenti della nuova raccolta. Cloud Firestore potrebbe non essere in grado di preparare in modo efficiente la nuova raccolta per l'aumento del traffico, soprattutto quando contiene pochi documenti.
Un problema simile può verificarsi se si modificano gli ID documento di molti documenti all'interno della stessa raccolta.
La migliore strategia per la migrazione del traffico a una nuova raccolta dipende dal modello di dati. Di seguito è riportato un esempio di strategia nota come letture parallele . Dovrai determinare se questa strategia è efficace o meno per i tuoi dati e una considerazione importante sarà l'impatto sui costi delle operazioni parallele durante la migrazione.
Letture parallele
Per implementare letture parallele durante la migrazione del traffico a una nuova raccolta, leggi prima dalla vecchia raccolta. Se manca il documento, leggi dalla nuova raccolta. Un alto tasso di letture di documenti inesistenti può portare a hotspotting, quindi assicurati di aumentare gradualmente il carico della nuova raccolta. Una strategia migliore consiste nel copiare il vecchio documento nella nuova raccolta, quindi eliminare il vecchio documento. Aumenta gradualmente le letture parallele per assicurarti che Cloud Firestore sia in grado di gestire il traffico verso la nuova raccolta.
Una possibile strategia per aumentare gradualmente le letture o le scritture in una nuova raccolta consiste nell'usare un hash deterministico dell'ID utente per selezionare una percentuale casuale di utenti che tentano di scrivere nuovi documenti. Assicurati che il risultato dell'hash dell'ID utente non sia distorto dalla tua funzione o dal comportamento dell'utente.
Nel frattempo, esegui un lavoro batch che copia tutti i tuoi dati dai vecchi documenti alla nuova raccolta. Il tuo processo batch dovrebbe evitare le scritture su ID documento sequenziali per evitare hotspot. Al termine del processo batch, è possibile leggere solo dalla nuova raccolta.
Un perfezionamento di questa strategia consiste nel migrare piccoli batch di utenti alla volta. Aggiungi un campo al documento dell'utente che tenga traccia dello stato di migrazione di quell'utente. Seleziona un batch di utenti di cui eseguire la migrazione in base a un hash dell'ID utente. Utilizzare un processo batch per migrare i documenti per quel batch di utenti e utilizzare le letture parallele per gli utenti nel mezzo della migrazione.
Si noti che non è possibile eseguire facilmente il rollback a meno che non si eseguano doppie scritture sia della vecchia che della nuova entità durante la fase di migrazione. Ciò aumenterebbe i costi sostenuti per Cloud Firestore.
Impedire l'accesso non autorizzato
Impedisci le operazioni non autorizzate sul tuo database con le regole di sicurezza di Cloud Firestore. Ad esempio, l'utilizzo delle regole potrebbe evitare uno scenario in cui un utente malintenzionato scarica ripetutamente l'intero database.
Ulteriori informazioni sull'utilizzo delle regole di sicurezza di Cloud Firestore .
,Utilizza le best practice elencate qui come riferimento rapido durante la creazione di un'applicazione che utilizza Cloud Firestore.
Posizione della banca dati
Quando crei l'istanza del database, seleziona la posizione del database più vicina agli utenti e alle risorse di calcolo. Gli hop di rete di vasta portata sono più soggetti a errori e aumentano la latenza delle query.
Per massimizzare la disponibilità e la durabilità della tua applicazione, seleziona una posizione in più aree geografiche e colloca le risorse di calcolo critiche in almeno due aree geografiche.
Seleziona una località regionale per costi inferiori, per una minore latenza di scrittura se la tua applicazione è sensibile alla latenza o per la co-ubicazione con altre risorse GCP .
ID documento
- Evita gli ID documento
.
e..
- Evita di utilizzare
/
barre negli ID documento. Non utilizzare ID documento che aumentano in modo monotono come:
-
Customer1
,Customer2
,Customer3
, ... -
Product 1
,Product 2
,Product 3
, ...
Tali ID sequenziali possono portare a hotspot che influiscono sulla latenza.
-
Nomi di campo
Evita i seguenti caratteri nei nomi dei campi perché richiedono caratteri di escape aggiuntivi:
-
.
periodo -
[
parentesi sinistra -
]
parentesi quadra destra -
*
asterisco -
`
apice inverso
-
Indici
Evitare di utilizzare troppi indici. Un numero eccessivo di indici può aumentare la latenza di scrittura e aumentare i costi di archiviazione per le voci di indice.
Tieni presente che l'indicizzazione di campi con valori crescenti in modo monotono, come i timestamp, può portare a hotspot che influiscono sulla latenza per le applicazioni con velocità di lettura e scrittura elevate.
Esenzioni dall'indice
Per la maggior parte delle app, puoi fare affidamento sull'indicizzazione automatica e su eventuali collegamenti ai messaggi di errore per gestire i tuoi indici. Tuttavia, potresti voler aggiungere esenzioni a campo singolo nei seguenti casi:
Caso | Descrizione |
---|---|
Grandi campi stringa | Se disponi di un campo stringa che contiene spesso valori stringa lunghi che non utilizzi per le query, puoi ridurre i costi di archiviazione esentando il campo dall'indicizzazione. |
Velocità di scrittura elevate in una raccolta contenente documenti con valori sequenziali | Se indicizzi un campo che aumenta o diminuisce in sequenza tra i documenti in una raccolta, come un timestamp, la velocità massima di scrittura nella raccolta è di 500 scritture al secondo. Se non esegui query in base al campo con valori sequenziali, puoi esentare il campo dall'indicizzazione per ignorare questo limite. In un caso d'uso IoT con un'elevata velocità di scrittura, ad esempio, una raccolta contenente documenti con un campo timestamp potrebbe avvicinarsi al limite di 500 scritture al secondo. |
Campi TTL | Se utilizzi i criteri TTL (time-to-live) , tieni presente che il campo TTL deve essere un timestamp. L'indicizzazione sui campi TTL è abilitata per impostazione predefinita e può influire sulle prestazioni a tassi di traffico più elevati. Come best practice, aggiungi esenzioni a campo singolo per i tuoi campi TTL. |
Matrice di grandi dimensioni o campi mappa | I campi matrice o mappa di grandi dimensioni possono avvicinarsi al limite di 40.000 voci di indice per documento. Se non stai eseguendo una query in base a un campo di matrice o mappa di grandi dimensioni, dovresti esentarlo dall'indicizzazione. |
Operazioni di lettura e scrittura
La velocità massima esatta con cui un'app può aggiornare un singolo documento dipende fortemente dal carico di lavoro. Per ulteriori informazioni, vedere Aggiornamenti a un singolo documento .
Utilizzare le chiamate asincrone, ove disponibili, anziché le chiamate sincrone. Le chiamate asincrone riducono al minimo l'impatto della latenza. Ad esempio, si consideri un'applicazione che necessita del risultato di una ricerca di documenti e dei risultati di una query prima di eseguire il rendering di una risposta. Se la ricerca e la query non hanno una dipendenza dati, non è necessario attendere in modo sincrono il completamento della ricerca prima di avviare la query.
Non utilizzare offset. Invece, usa i cursori . L'utilizzo di un offset evita solo di restituire i documenti saltati all'applicazione, ma questi documenti vengono comunque recuperati internamente. I documenti ignorati influiscono sulla latenza della query e all'applicazione vengono fatturate le operazioni di lettura necessarie per recuperarli.
Tentativi di transazione
Gli SDK e le librerie client di Cloud Firestore ritentano automaticamente le transazioni non riuscite per gestire gli errori temporanei. Se la tua applicazione accede a Cloud Firestore direttamente tramite le API REST o RPC anziché tramite un SDK, la tua applicazione dovrebbe implementare tentativi di transazione per aumentare l'affidabilità.
Aggiornamenti in tempo reale
Per le migliori prestazioni dell'ascoltatore di istantanee , mantieni i tuoi documenti piccoli e controlla la velocità di lettura dei tuoi clienti. Le seguenti raccomandazioni forniscono linee guida per ottimizzare le prestazioni. Il superamento di questi consigli può comportare un aumento della latenza delle notifiche.
Raccomandazione | Dettagli |
---|---|
Riduci il tasso di abbandono dell'ascoltatore di istantanee | Evita ascoltatori che si agitano frequentemente, soprattutto quando il tuo database è sottoposto a un carico di scrittura significativo Idealmente, la tua applicazione dovrebbe configurare tutti i listener di snapshot richiesti subito dopo aver aperto una connessione a Cloud Firestore. Dopo aver configurato i listener di snapshot iniziali, dovresti evitare di aggiungere o rimuovere rapidamente listener di snapshot nella stessa connessione. Per garantire la coerenza dei dati, Cloud Firestore deve eseguire il priming di ogni nuovo snapshot listener dai dati di origine e quindi aggiornarsi sulle nuove modifiche. A seconda della velocità di scrittura del database, questa può essere un'operazione costosa. I tuoi snapshot listener possono riscontrare una maggiore latenza se aggiungi o rimuovi spesso snapshot listener ai riferimenti. In generale, un listener costantemente collegato offre prestazioni migliori rispetto al collegamento e allo scollegamento di un listener in quella posizione per la stessa quantità di dati. Per prestazioni ottimali, gli snapshot listener dovrebbero avere una durata di almeno 30 secondi. Se riscontri problemi di prestazioni dell'ascoltatore nella tua app, prova a monitorare gli ascolti e i non ascolti dell'app per determinare se potrebbero verificarsi troppo frequentemente. |
Limita i listener di snapshot per client | 100 Mantieni il numero di listener di istantanee per client al di sotto di 100. |
Limita la velocità di scrittura della raccolta | 1.000 operazioni/secondo Mantieni la frequenza delle operazioni di scrittura per una singola raccolta al di sotto di 1.000 operazioni al secondo. |
Limita la frequenza di push del singolo client | 1 documento/secondo Mantieni la velocità dei documenti inviati dal database a un singolo cliente al di sotto di 1 documento/secondo. |
Limita la frequenza di push del client globale | 1.000.000 documenti/secondo Mantieni la velocità dei documenti inviati dal database a tutti i client al di sotto di 1.000.000 di documenti al secondo. Questo è un limite morbido. Cloud Firestore non ti impedisce di superare questa soglia ma influisce notevolmente sulle prestazioni. |
Limita il payload del singolo documento | 10 KiB/secondo Mantieni la dimensione massima del documento scaricato da un singolo cliente al di sotto di 10 KiB/secondo. |
Limita il payload del documento globale | 1 GiB/secondo Mantieni la dimensione massima del documento scaricato su tutti i client al di sotto di 1 GiB/secondo. |
Limita il numero di campi per documento | 100 I tuoi documenti dovrebbero avere meno di 100 campi. |
Comprendi i limiti standard di Cloud Firestore | Tieni presente i limiti standard per Cloud Firestore . Prestare particolare attenzione al limite di 1 scrittura al secondo per i documenti e al limite di 1.000.000 di connessioni simultanee per database. Questi sono limiti flessibili che Cloud Firestore non ti impedisce di superare. Tuttavia, il superamento di questi limiti potrebbe influire sulle prestazioni, a seconda delle velocità totali di lettura e scrittura. |
Progettare per la scala
Le procedure consigliate seguenti descrivono come evitare situazioni che creano problemi di contesa.
Aggiornamenti a un singolo documento
Mentre progetti la tua app, considera quanto velocemente la tua app aggiorna i singoli documenti. Il modo migliore per caratterizzare le prestazioni del tuo carico di lavoro è eseguire il test del carico. La velocità massima esatta con cui un'app può aggiornare un singolo documento dipende fortemente dal carico di lavoro. I fattori includono la velocità di scrittura, la contesa tra le richieste e il numero di indici interessati.
Un'operazione di scrittura di un documento aggiorna il documento e gli eventuali indici associati e Cloud Firestore applica l'operazione di scrittura in modo sincrono a un quorum di repliche. A velocità di scrittura sufficientemente elevate, il database inizierà a riscontrare contesa, latenza più elevata o altri errori.
Elevate velocità di lettura, scrittura ed eliminazione su un intervallo di documenti ristretto
Evita velocità di lettura o scrittura elevate per chiudere lessicograficamente i documenti, altrimenti la tua applicazione riscontrerà errori di contesa. Questo problema è noto come hotspotting e la tua applicazione può riscontrare hotspotting se esegue una delle seguenti operazioni:
Crea nuovi documenti a una velocità molto elevata e alloca i propri ID in modo monotono.
Cloud Firestore alloca gli ID documento utilizzando un algoritmo di dispersione. Non dovresti riscontrare hotspotting durante le scritture se crei nuovi documenti utilizzando ID documento automatici.
Crea nuovi documenti a una velocità elevata in una raccolta con pochi documenti.
Crea nuovi documenti con un campo in aumento monotono, come un timestamp, a una velocità molto elevata.
Elimina i documenti in una raccolta a una velocità elevata.
Scrive nel database a una velocità molto elevata senza aumentare gradualmente il traffico.
Evita di saltare i dati cancellati
Evita le query che ignorano i dati eliminati di recente. Una query potrebbe dover ignorare un numero elevato di voci di indice se i primi risultati della query sono stati eliminati di recente.
Un esempio di un carico di lavoro che potrebbe dover ignorare molti dati eliminati è quello che tenta di trovare gli elementi di lavoro in coda meno recenti. La query potrebbe essere simile a:
docs = db.collection('WorkItems').order_by('created').limit(100)
delete_batch = db.batch()
for doc in docs.stream():
finish_work(doc)
delete_batch.delete(doc.reference)
delete_batch.commit()
Ogni volta che questa query viene eseguita, esegue la scansione delle voci di indice per il campo created
su tutti i documenti eliminati di recente. Questo rallenta le query.
Per migliorare le prestazioni, utilizza il metodo start_at
per trovare il punto di partenza migliore. Per esempio:
completed_items = db.collection('CompletionStats').document('all stats').get()
docs = db.collection('WorkItems').start_at(
{'created': completed_items.get('last_completed')}).order_by(
'created').limit(100)
delete_batch = db.batch()
last_completed = None
for doc in docs.stream():
finish_work(doc)
delete_batch.delete(doc.reference)
last_completed = doc.get('created')
if last_completed:
delete_batch.update(completed_items.reference,
{'last_completed': last_completed})
delete_batch.commit()
NOTA: l'esempio precedente utilizza un campo in aumento monotono che è un anti-pattern per velocità di scrittura elevate.
Aumento del traffico
Dovresti aumentare gradualmente il traffico verso nuove raccolte o chiudere lessicograficamente i documenti per dare a Cloud Firestore il tempo sufficiente per preparare i documenti per l'aumento del traffico. Ti consigliamo di iniziare con un massimo di 500 operazioni al secondo per una nuova raccolta e quindi aumentare il traffico del 50% ogni 5 minuti. Allo stesso modo puoi aumentare il tuo traffico di scrittura, ma tieni a mente i limiti standard di Cloud Firestore . Assicurati che le operazioni siano distribuite in modo relativamente uniforme in tutta la gamma di tasti. Questa è chiamata la regola "500/50/5".
Migrazione del traffico verso una nuova raccolta
L'aumento graduale è particolarmente importante se esegui la migrazione del traffico delle app da una raccolta a un'altra. Un modo semplice per gestire questa migrazione è leggere dalla vecchia raccolta e, se il documento non esiste, leggere dalla nuova raccolta. Tuttavia, ciò potrebbe causare un improvviso aumento del traffico per chiudere lessicograficamente i documenti della nuova raccolta. Cloud Firestore potrebbe non essere in grado di preparare in modo efficiente la nuova raccolta per l'aumento del traffico, soprattutto quando contiene pochi documenti.
Un problema simile può verificarsi se si modificano gli ID documento di molti documenti all'interno della stessa raccolta.
La migliore strategia per la migrazione del traffico a una nuova raccolta dipende dal modello di dati. Di seguito è riportato un esempio di strategia nota come letture parallele . Dovrai determinare se questa strategia è efficace o meno per i tuoi dati e una considerazione importante sarà l'impatto sui costi delle operazioni parallele durante la migrazione.
Letture parallele
Per implementare letture parallele durante la migrazione del traffico a una nuova raccolta, leggi prima dalla vecchia raccolta. Se manca il documento, leggi dalla nuova raccolta. Un alto tasso di letture di documenti inesistenti può portare a hotspotting, quindi assicurati di aumentare gradualmente il carico della nuova raccolta. Una strategia migliore consiste nel copiare il vecchio documento nella nuova raccolta, quindi eliminare il vecchio documento. Aumenta gradualmente le letture parallele per assicurarti che Cloud Firestore sia in grado di gestire il traffico verso la nuova raccolta.
Una possibile strategia per aumentare gradualmente le letture o le scritture in una nuova raccolta consiste nell'usare un hash deterministico dell'ID utente per selezionare una percentuale casuale di utenti che tentano di scrivere nuovi documenti. Assicurati che il risultato dell'hash dell'ID utente non sia distorto dalla tua funzione o dal comportamento dell'utente.
Nel frattempo, esegui un lavoro batch che copia tutti i tuoi dati dai vecchi documenti alla nuova raccolta. Il tuo processo batch dovrebbe evitare le scritture su ID documento sequenziali per evitare hotspot. Al termine del processo batch, è possibile leggere solo dalla nuova raccolta.
Un perfezionamento di questa strategia consiste nel migrare piccoli batch di utenti alla volta. Aggiungi un campo al documento dell'utente che tenga traccia dello stato di migrazione di quell'utente. Seleziona un batch di utenti di cui eseguire la migrazione in base a un hash dell'ID utente. Utilizzare un processo batch per migrare i documenti per quel batch di utenti e utilizzare le letture parallele per gli utenti nel mezzo della migrazione.
Si noti che non è possibile eseguire facilmente il rollback a meno che non si eseguano doppie scritture sia della vecchia che della nuova entità durante la fase di migrazione. Ciò aumenterebbe i costi sostenuti per Cloud Firestore.
Impedire l'accesso non autorizzato
Impedisci le operazioni non autorizzate sul tuo database con le regole di sicurezza di Cloud Firestore. Ad esempio, l'utilizzo delle regole potrebbe evitare uno scenario in cui un utente malintenzionato scarica ripetutamente l'intero database.
Ulteriori informazioni sull'utilizzo delle regole di sicurezza di Cloud Firestore .
,Utilizza le best practice elencate qui come riferimento rapido durante la creazione di un'applicazione che utilizza Cloud Firestore.
Posizione della banca dati
Quando crei l'istanza del database, seleziona la posizione del database più vicina agli utenti e alle risorse di calcolo. Gli hop di rete di vasta portata sono più soggetti a errori e aumentano la latenza delle query.
Per massimizzare la disponibilità e la durabilità della tua applicazione, seleziona una posizione in più aree geografiche e colloca le risorse di calcolo critiche in almeno due aree geografiche.
Seleziona una località regionale per costi inferiori, per una minore latenza di scrittura se la tua applicazione è sensibile alla latenza o per la co-ubicazione con altre risorse GCP .
ID documento
- Evita gli ID documento
.
e..
- Evita di utilizzare
/
barre negli ID documento. Non utilizzare ID documento che aumentano in modo monotono come:
-
Customer1
,Customer2
,Customer3
, ... -
Product 1
,Product 2
,Product 3
, ...
Tali ID sequenziali possono portare a hotspot che influiscono sulla latenza.
-
Nomi di campo
Evita i seguenti caratteri nei nomi dei campi perché richiedono caratteri di escape aggiuntivi:
-
.
periodo -
[
parentesi sinistra -
]
parentesi quadra destra -
*
asterisco -
`
apice inverso
-
Indici
Evitare di utilizzare troppi indici. Un numero eccessivo di indici può aumentare la latenza di scrittura e aumentare i costi di archiviazione per le voci di indice.
Tieni presente che l'indicizzazione di campi con valori crescenti in modo monotono, come i timestamp, può portare a hotspot che influiscono sulla latenza per le applicazioni con velocità di lettura e scrittura elevate.
Esenzioni dall'indice
Per la maggior parte delle app, puoi fare affidamento sull'indicizzazione automatica e su eventuali collegamenti ai messaggi di errore per gestire i tuoi indici. Tuttavia, potresti voler aggiungere esenzioni a campo singolo nei seguenti casi:
Caso | Descrizione |
---|---|
Grandi campi stringa | Se disponi di un campo stringa che contiene spesso valori stringa lunghi che non utilizzi per le query, puoi ridurre i costi di archiviazione esentando il campo dall'indicizzazione. |
Velocità di scrittura elevate in una raccolta contenente documenti con valori sequenziali | Se indicizzi un campo che aumenta o diminuisce in sequenza tra i documenti in una raccolta, come un timestamp, la velocità massima di scrittura nella raccolta è di 500 scritture al secondo. Se non esegui query in base al campo con valori sequenziali, puoi esentare il campo dall'indicizzazione per ignorare questo limite. In un caso d'uso IoT con un'elevata velocità di scrittura, ad esempio, una raccolta contenente documenti con un campo timestamp potrebbe avvicinarsi al limite di 500 scritture al secondo. |
Campi TTL | Se utilizzi i criteri TTL (time-to-live) , tieni presente che il campo TTL deve essere un timestamp. L'indicizzazione sui campi TTL è abilitata per impostazione predefinita e può influire sulle prestazioni a tassi di traffico più elevati. Come best practice, aggiungi esenzioni a campo singolo per i tuoi campi TTL. |
Matrice di grandi dimensioni o campi mappa | I campi matrice o mappa di grandi dimensioni possono avvicinarsi al limite di 40.000 voci di indice per documento. Se non stai eseguendo una query in base a un campo di matrice o mappa di grandi dimensioni, dovresti esentarlo dall'indicizzazione. |
Operazioni di lettura e scrittura
La velocità massima esatta con cui un'app può aggiornare un singolo documento dipende fortemente dal carico di lavoro. Per ulteriori informazioni, vedere Aggiornamenti a un singolo documento .
Utilizzare le chiamate asincrone, ove disponibili, anziché le chiamate sincrone. Le chiamate asincrone riducono al minimo l'impatto della latenza. Ad esempio, si consideri un'applicazione che necessita del risultato di una ricerca di documenti e dei risultati di una query prima di eseguire il rendering di una risposta. Se la ricerca e la query non hanno una dipendenza dati, non è necessario attendere in modo sincrono il completamento della ricerca prima di avviare la query.
Non utilizzare offset. Invece, usa i cursori . L'utilizzo di un offset evita solo di restituire i documenti saltati all'applicazione, ma questi documenti vengono comunque recuperati internamente. I documenti ignorati influiscono sulla latenza della query e all'applicazione vengono fatturate le operazioni di lettura necessarie per recuperarli.
Tentativi di transazione
Gli SDK e le librerie client di Cloud Firestore ritentano automaticamente le transazioni non riuscite per gestire gli errori temporanei. Se la tua applicazione accede a Cloud Firestore direttamente tramite le API REST o RPC anziché tramite un SDK, la tua applicazione dovrebbe implementare tentativi di transazione per aumentare l'affidabilità.
Aggiornamenti in tempo reale
Per le migliori prestazioni dell'ascoltatore di istantanee , mantieni i tuoi documenti piccoli e controlla la velocità di lettura dei tuoi clienti. Le seguenti raccomandazioni forniscono linee guida per ottimizzare le prestazioni. Il superamento di questi consigli può comportare un aumento della latenza delle notifiche.
Raccomandazione | Dettagli |
---|---|
Riduci il tasso di abbandono dell'ascoltatore di istantanee | Evita ascoltatori che si agitano frequentemente, soprattutto quando il tuo database è sottoposto a un carico di scrittura significativo Idealmente, la tua applicazione dovrebbe configurare tutti i listener di snapshot richiesti subito dopo aver aperto una connessione a Cloud Firestore. Dopo aver configurato i listener di snapshot iniziali, dovresti evitare di aggiungere o rimuovere rapidamente listener di snapshot nella stessa connessione. Per garantire la coerenza dei dati, Cloud Firestore deve eseguire il priming di ogni nuovo snapshot listener dai dati di origine e quindi aggiornarsi sulle nuove modifiche. A seconda della velocità di scrittura del database, questa può essere un'operazione costosa. I tuoi snapshot listener possono riscontrare una maggiore latenza se aggiungi o rimuovi spesso snapshot listener ai riferimenti. In generale, un listener costantemente collegato offre prestazioni migliori rispetto al collegamento e allo scollegamento di un listener in quella posizione per la stessa quantità di dati. Per prestazioni ottimali, gli snapshot listener dovrebbero avere una durata di almeno 30 secondi. Se riscontri problemi di prestazioni dell'ascoltatore nella tua app, prova a monitorare gli ascolti e i non ascolti dell'app per determinare se potrebbero verificarsi troppo frequentemente. |
Limita i listener di snapshot per client | 100 Mantieni il numero di listener di istantanee per client al di sotto di 100. |
Limita la velocità di scrittura della raccolta | 1.000 operazioni/secondo Keep the rate of write operations for an individual collection under 1,000 operations/second. |
Limit the individual client push rate | 1 document/second Keep the rate of documents the database pushes to an individual client under 1 document/second. |
Limit the global client push rate | 1,000,000 documents/second Keep the rate of documents the database pushes to all clients under 1,000,000 documents/second. This is a soft limit. Cloud Firestore does not stop you from surpassing this threshold but it greatly affects performance. |
Limit the individual document payload | 10 KiB/second Keep the maximum document size downloaded by an individual client under 10 KiB/second. |
Limit the global document payload | 1 GiB/second Keep the maximum document size downloaded across all clients under 1 GiB/second. |
Limit the number of fields per document | 100 Your documents should have fewer than 100 fields. |
Understand the Cloud Firestore standard limits | Keep in mind the standard limits for Cloud Firestore . Pay special attention to the 1 write per second limit for documents and the limit of 1,000,000 concurrent connections per database. These are soft limits that Cloud Firestore does not stop you from exceeding. However, going over these limits might affect performance, depending on your total read and write rates. |
Designing for scale
The following best practices describe how to avoid situations that create contention issues.
Updates to a single document
As you design your app, consider how quickly your app updates single documents. The best way to characterize your workload's performance is to perform load testing. The exact maximum rate that an app can update a single document depends highly on the workload. Factors include the write rate, contention among requests, and the number affected indexes.
A document write operation updates the document and any associated indexes, and Cloud Firestore synchronously applies the write operation across a quorum of replicas. At high enough write rates, the database will start to encounter contention, higher latency, or other errors.
High read, write, and delete rates to a narrow document range
Avoid high read or write rates to lexicographically close documents, or your application will experience contention errors. This issue is known as hotspotting, and your application can experience hotspotting if it does any of the following:
Creates new documents at a very high rate and allocates its own monotonically increasing IDs.
Cloud Firestore allocates document IDs using a scatter algorithm. You should not encounter hotspotting on writes if you create new documents using automatic document IDs.
Creates new documents at a high rate in a collection with few documents.
Creates new documents with a monotonically increasing field, like a timestamp, at a very high rate.
Deletes documents in a collection at a high rate.
Writes to the database at a very high rate without gradually increasing traffic.
Avoid skipping over deleted data
Avoid queries that skip over recently deleted data. A query may have to skip over a large number of index entries if the early query results have recently been deleted.
An example of a workload that might have to skip over a lot of deleted data is one that tries to find the oldest queued work items. The query might look like:
docs = db.collection('WorkItems').order_by('created').limit(100)
delete_batch = db.batch()
for doc in docs.stream():
finish_work(doc)
delete_batch.delete(doc.reference)
delete_batch.commit()
Each time this query runs it scans over the index entries for the created
field on any recently deleted documents. This slows down queries.
To improve the performance, use the start_at
method to find the best place to start. For example:
completed_items = db.collection('CompletionStats').document('all stats').get()
docs = db.collection('WorkItems').start_at(
{'created': completed_items.get('last_completed')}).order_by(
'created').limit(100)
delete_batch = db.batch()
last_completed = None
for doc in docs.stream():
finish_work(doc)
delete_batch.delete(doc.reference)
last_completed = doc.get('created')
if last_completed:
delete_batch.update(completed_items.reference,
{'last_completed': last_completed})
delete_batch.commit()
NOTE: The example above uses a monotonically increasing field which is an anti-pattern for high write rates.
Ramping up traffic
You should gradually ramp up traffic to new collections or lexicographically close documents to give Cloud Firestore sufficient time to prepare documents for increased traffic. We recommend starting with a maximum of 500 operations per second to a new collection and then increasing traffic by 50% every 5 minutes. You can similarly ramp up your write traffic, but keep in mind the Cloud Firestore Standard Limits . Be sure that operations are distributed relatively evenly throughout the key range. This is called the "500/50/5" rule.
Migrating traffic to a new collection
Gradual ramp up is particularly important if you migrate app traffic from one collection to another. A simple way to handle this migration is to read from the old collection, and if the document does not exist, then read from the new collection. However, this could cause a sudden increase of traffic to lexicographically close documents in the new collection. Cloud Firestore may be unable to efficiently prepare the new collection for increased traffic, especially when it contains few documents.
A similar problem can occur if you change the document IDs of many documents within the same collection.
The best strategy for migrating traffic to a new collection depends on your data model. Below is an example strategy known as parallel reads . You will need to determine whether or not this strategy is effective for your data, and an important consideration will be the cost impact of parallel operations during the migration.
Parallel reads
To implement parallel reads as you migrate traffic to a new collection, read from the old collection first. If the document is missing, then read from the new collection. A high rate of reads of non-existent documents can lead to hotspotting, so be sure to gradually increase load to the new collection. A better strategy is to copy the old document to the new collection then delete the old document. Ramp up parallel reads gradually to ensure that Cloud Firestore can handle traffic to the new collection.
A possible strategy for gradually ramping up reads or writes to a new collection is to use a deterministic hash of the user ID to select a random percentage of users attempting to write new documents. Be sure that the result of the user ID hash is not skewed either by your function or by user behavior.
Meanwhile, run a batch job that copies all your data from the old documents to the new collection. Your batch job should avoid writes to sequential document IDs in order to prevent hotspots. When the batch job finishes, you can read only from the new collection.
A refinement of this strategy is to migrate small batches of users at a time. Add a field to the user document which tracks migration status of that user. Select a batch of users to migrate based on a hash of the user ID. Use a batch job to migrate documents for that batch of users, and use parallel reads for users in the middle of migration.
Note that you cannot easily roll back unless you do dual writes of both the old and new entities during the migration phase. This would increase Cloud Firestore costs incurred.
Prevent unauthorized access
Prevent unauthorized operations on your database with Cloud Firestore Security Rules. For example, using rules could avoid a scenario where a malicious user repeatedly downloads your entire database.
Learn more about using Cloud Firestore Security Rules .
,Use the best practices listed here as a quick reference when building an application that uses Cloud Firestore.
Database location
When you create your database instance, select the database location closest to your users and compute resources. Far-reaching network hops are more error-prone and increase query latency.
To maximize the availability and durability of your application, select a multi-region location and place critical compute resources in at least two regions.
Select a regional location for lower costs, for lower write latency if your application is sensitive to latency, or for co-location with other GCP resources .
Document IDs
- Avoid the document IDs
.
and..
. - Avoid using
/
forward slashes in document IDs. Do not use monotonically increasing document IDs such as:
-
Customer1
,Customer2
,Customer3
, ... -
Product 1
,Product 2
,Product 3
, ...
Such sequential IDs can lead to hotspots that impact latency.
-
Field Names
Avoid the following characters in field names because they require extra escaping:
-
.
period -
[
left bracket -
]
right bracket -
*
asterisk -
`
backtick
-
Indexes
Avoid using too many indexes. An excessive number of indexes can increase write latency and increases storage costs for index entries.
Be aware that indexing fields with monotonically increasing values, such as timestamps, can lead to hotspots which impact latency for applications with high read and write rates.
Index exemptions
For most apps, you can rely on automatic indexing as well as any error message links to manage your indexes. However, you may want to add single-field exemptions in the following cases:
Case | Description |
---|---|
Large string fields | If you have a string field that often holds long string values that you don't use for querying, you can cut storage costs by exempting the field from indexing. |
High write rates to a collection containing documents with sequential values | If you index a field that increases or decreases sequentially between documents in a collection, like a timestamp, then the maximum write rate to the collection is 500 writes per second. If you don't query based on the field with sequential values, you can exempt the field from indexing to bypass this limit. In an IoT use case with a high write rate, for example, a collection containing documents with a timestamp field might approach the 500 writes per second limit. |
TTL fields | If you use TTL (time-to-live) policies , note that the TTL field must be a timestamp. Indexing on TTL fields is enabled by default and can affect performance at higher traffic rates. As a best practice, add single-field exemptions for your TTL fields. |
Large array or map fields | Large array or map fields can approach the limit of 40,000 index entries per document. If you are not querying based on a large array or map field, you should exempt it from indexing. |
Read and write operations
The exact maximum rate that an app can update a single document depends highly on the workload. For more information, see Updates to a single document .
Use asynchronous calls where available instead of synchronous calls. Asynchronous calls minimize latency impact. For example, consider an application that needs the result of a document lookup and the results of a query before rendering a response. If the lookup and the query do not have a data dependency, there is no need to synchronously wait until the lookup completes before initiating the query.
Do not use offsets. Instead, use cursors . Using an offset only avoids returning the skipped documents to your application, but these documents are still retrieved internally. The skipped documents affect the latency of the query, and your application is billed for the read operations required to retrieve them.
Transactions retries
The Cloud Firestore SDKs and client libraries automatically retry failed transactions to deal with transient errors. If your application accesses Cloud Firestore through the REST or RPC APIs directly instead of through an SDK, your application should implement transaction retries to increase reliability.
Realtime updates
For the best snapshot listener performance, keep your documents small and control the read rate of your clients. The following recommendations provide guidelines for maximizing performance. Exceeding these recommendations can result in increased notification latency.
Recommendation | Details |
---|---|
Reduce snapshot listener churn rate | Avoid frequently churning listeners, especially when your database is under significant write load Ideally, your application should set up all the required snapshot listeners soon after opening a connection to Cloud Firestore. After setting up your initial snapshot listeners, you should avoid quickly adding or removing snapshot listeners in the same connection. To ensure data consistency, Cloud Firestore needs to prime each new snapshot listener from its source data and then catch up to new changes. Depending on your database's write rate, this can be an expensive operation. Your snapshot listeners can experience increased latency if you frequently add or remove snapshot listeners to references. In general, a constantly-attached listener performs better than attaching and detaching a listener at that location for the same amount of data. For best performance, snapshot listeners should have a lifetime of 30 seconds or longer. If you encounter listener performance issues in your app, try tracking your app's listens and unlistens to determine if they may be happening too frequently. |
Limit snapshot listeners per client | 100 Keep the number of snapshot listeners per client under 100. |
Limit the collection write rate | 1,000 operations/second Keep the rate of write operations for an individual collection under 1,000 operations/second. |
Limit the individual client push rate | 1 document/second Keep the rate of documents the database pushes to an individual client under 1 document/second. |
Limit the global client push rate | 1,000,000 documents/second Keep the rate of documents the database pushes to all clients under 1,000,000 documents/second. This is a soft limit. Cloud Firestore does not stop you from surpassing this threshold but it greatly affects performance. |
Limit the individual document payload | 10 KiB/second Keep the maximum document size downloaded by an individual client under 10 KiB/second. |
Limit the global document payload | 1 GiB/second Keep the maximum document size downloaded across all clients under 1 GiB/second. |
Limit the number of fields per document | 100 Your documents should have fewer than 100 fields. |
Understand the Cloud Firestore standard limits | Keep in mind the standard limits for Cloud Firestore . Pay special attention to the 1 write per second limit for documents and the limit of 1,000,000 concurrent connections per database. These are soft limits that Cloud Firestore does not stop you from exceeding. However, going over these limits might affect performance, depending on your total read and write rates. |
Designing for scale
The following best practices describe how to avoid situations that create contention issues.
Updates to a single document
As you design your app, consider how quickly your app updates single documents. The best way to characterize your workload's performance is to perform load testing. The exact maximum rate that an app can update a single document depends highly on the workload. Factors include the write rate, contention among requests, and the number affected indexes.
A document write operation updates the document and any associated indexes, and Cloud Firestore synchronously applies the write operation across a quorum of replicas. At high enough write rates, the database will start to encounter contention, higher latency, or other errors.
High read, write, and delete rates to a narrow document range
Avoid high read or write rates to lexicographically close documents, or your application will experience contention errors. This issue is known as hotspotting, and your application can experience hotspotting if it does any of the following:
Creates new documents at a very high rate and allocates its own monotonically increasing IDs.
Cloud Firestore allocates document IDs using a scatter algorithm. You should not encounter hotspotting on writes if you create new documents using automatic document IDs.
Creates new documents at a high rate in a collection with few documents.
Creates new documents with a monotonically increasing field, like a timestamp, at a very high rate.
Deletes documents in a collection at a high rate.
Writes to the database at a very high rate without gradually increasing traffic.
Avoid skipping over deleted data
Avoid queries that skip over recently deleted data. A query may have to skip over a large number of index entries if the early query results have recently been deleted.
An example of a workload that might have to skip over a lot of deleted data is one that tries to find the oldest queued work items. The query might look like:
docs = db.collection('WorkItems').order_by('created').limit(100)
delete_batch = db.batch()
for doc in docs.stream():
finish_work(doc)
delete_batch.delete(doc.reference)
delete_batch.commit()
Each time this query runs it scans over the index entries for the created
field on any recently deleted documents. This slows down queries.
To improve the performance, use the start_at
method to find the best place to start. For example:
completed_items = db.collection('CompletionStats').document('all stats').get()
docs = db.collection('WorkItems').start_at(
{'created': completed_items.get('last_completed')}).order_by(
'created').limit(100)
delete_batch = db.batch()
last_completed = None
for doc in docs.stream():
finish_work(doc)
delete_batch.delete(doc.reference)
last_completed = doc.get('created')
if last_completed:
delete_batch.update(completed_items.reference,
{'last_completed': last_completed})
delete_batch.commit()
NOTE: The example above uses a monotonically increasing field which is an anti-pattern for high write rates.
Ramping up traffic
You should gradually ramp up traffic to new collections or lexicographically close documents to give Cloud Firestore sufficient time to prepare documents for increased traffic. We recommend starting with a maximum of 500 operations per second to a new collection and then increasing traffic by 50% every 5 minutes. You can similarly ramp up your write traffic, but keep in mind the Cloud Firestore Standard Limits . Be sure that operations are distributed relatively evenly throughout the key range. This is called the "500/50/5" rule.
Migrating traffic to a new collection
Gradual ramp up is particularly important if you migrate app traffic from one collection to another. A simple way to handle this migration is to read from the old collection, and if the document does not exist, then read from the new collection. However, this could cause a sudden increase of traffic to lexicographically close documents in the new collection. Cloud Firestore may be unable to efficiently prepare the new collection for increased traffic, especially when it contains few documents.
A similar problem can occur if you change the document IDs of many documents within the same collection.
The best strategy for migrating traffic to a new collection depends on your data model. Below is an example strategy known as parallel reads . You will need to determine whether or not this strategy is effective for your data, and an important consideration will be the cost impact of parallel operations during the migration.
Parallel reads
To implement parallel reads as you migrate traffic to a new collection, read from the old collection first. If the document is missing, then read from the new collection. A high rate of reads of non-existent documents can lead to hotspotting, so be sure to gradually increase load to the new collection. A better strategy is to copy the old document to the new collection then delete the old document. Ramp up parallel reads gradually to ensure that Cloud Firestore can handle traffic to the new collection.
A possible strategy for gradually ramping up reads or writes to a new collection is to use a deterministic hash of the user ID to select a random percentage of users attempting to write new documents. Be sure that the result of the user ID hash is not skewed either by your function or by user behavior.
Meanwhile, run a batch job that copies all your data from the old documents to the new collection. Your batch job should avoid writes to sequential document IDs in order to prevent hotspots. When the batch job finishes, you can read only from the new collection.
A refinement of this strategy is to migrate small batches of users at a time. Add a field to the user document which tracks migration status of that user. Select a batch of users to migrate based on a hash of the user ID. Use a batch job to migrate documents for that batch of users, and use parallel reads for users in the middle of migration.
Note that you cannot easily roll back unless you do dual writes of both the old and new entities during the migration phase. This would increase Cloud Firestore costs incurred.
Prevent unauthorized access
Prevent unauthorized operations on your database with Cloud Firestore Security Rules. For example, using rules could avoid a scenario where a malicious user repeatedly downloads your entire database.
Learn more about using Cloud Firestore Security Rules .