Bonnes pratiques pour Cloud Firestore

Utilisez les bonnes pratiques listées ici comme référence rapide lorsque vous créez une application qui utilise Cloud Firestore.

Zone de la base de données

Lorsque vous créez votre instance de base de données, sélectionnez la zone de base de données la plus proche de vos utilisateurs et des ressources de calcul. Les sauts de réseau importants sont plus sujets aux erreurs et augmentent la latence des requêtes.

Pour optimiser la disponibilité et la durabilité de votre application, sélectionnez une zone multirégionale et placez les ressources de calcul critiques dans au moins deux régions.

Sélectionnez une zone régionale si vous souhaitez réduire vos coûts, réduire la latence d'écriture si votre application est sensible à la latence, ou co-localiser votre application avec d'autres ressources GCP.

ID de document

  • Évitez les ID de document . et ...
  • Évitez d'utiliser des barres obliques / dans les ID de document.
  • N'utilisez pas des ID de document monotones croissants, tels que :

    • Customer1, Customer2, Customer3, ...
    • Product 1, Product 2, Product 3, ...

    Ces ID séquentiels peuvent générer des hotspots qui ont un impact sur la latence.

Noms des champs

  • Évitez les caractères suivants dans les noms de champs, car ils nécessitent un échappement supplémentaire :

    • . point
    • [ crochet ouvrant
    • ] crochet fermant
    • * astérisque
    • ` accent grave

Index

Réduire la latence d'écriture

Le principal facteur de latence d'écriture est la distribution ramifiée de l'index. Voici les bonnes pratiques à suivre pour réduire la distribution d'index :

  • Définissez des exceptions d'index au niveau de la collection. Une solution simple par défaut consiste à désactiver l'indexation décroissante et l'indexation de tableau. La suppression des valeurs indexées inutilisées permet également de réduire les coûts de stockage.

  • Réduisez le nombre de documents dans une transaction. Pour écrire un grand nombre de documents, envisagez d'utiliser un rédacteur groupé plutôt qu'un rédacteur par lot atomique.

Exclusions d'index

Pour la plupart des applications, vous pouvez utiliser l'indexation automatique et les liens des messages d'erreur pour gérer vos index. Toutefois, vous pouvez ajouter des exclusions de champs uniques dans les cas suivants :

Cas Description
Champs de chaîne volumineux

S'il existe un champ de chaîne contenant souvent des chaînes longues que vous n'utilisez pas pour les requêtes, vous pouvez réduire les coûts de stockage en excluant le champ de l'indexation.

Taux d'écriture élevés dans une collection contenant des documents avec des valeurs séquentielles

Si vous indexez un champ qui augmente ou diminue séquentiellement entre les documents d'une collection, comme un horodatage, le taux d'écriture maximal pour la collection est de 500 écritures par seconde. Si vous ne faites pas de requête en fonction du champ avec des valeurs séquentielles, vous pouvez exclure le champ de l'indexation pour contourner cette limite.

Par exemple, dans le cas d'un IoT avec un taux d'écriture élevé, une collection contenant des documents avec un champ d'horodatage peut approcher la limite des 500 écritures par seconde.

Champs TTL

Si vous utilisez des règles TTL (Time To Live), notez que le champ TTL doit être un code temporel. L'indexation sur les champs TTL est activée par défaut et peut affecter les performances à des taux de trafic plus élevés. Une bonne pratique consiste à ajouter des exceptions d'indexation automatique pour vos champs TTL.

Champs de tableau ou de carte volumineux

Les champs de tableau ou de mappage volumineux peuvent approcher la limite de 40 000 entrées d'index par document. Si vous ne faites pas de requête en fonction d'un champ de tableau ou de mappage volumineux, vous devez exclure ce champ de l'indexation.

Opérations de lecture et d'écriture

  • Le taux maximal exact auquel une application peut mettre à jour un seul document dépend fortement de la charge de travail. Pour plus d'informations, consultez la section Mises à jour d'un seul document.

  • Utilisez des appels asynchrones, le cas échéant, au lieu d'appels synchrones. Les appels asynchrones minimisent l'impact de la latence. Par exemple, prenons une application qui a besoin du résultat d'une recherche de document et des résultats d'une requête pour pouvoir afficher une réponse. Si la recherche et la requête n'ont pas de dépendance aux données, il n'est pas nécessaire d'attendre de manière synchrone que la recherche se termine avant de lancer la requête.

  • N'utilisez pas de décalages. Utilisez plutôt des curseurs. L'utilisation d'un décalage permet uniquement d'éviter de renvoyer les documents ignorés à l'application, mais ces derniers sont tout de même récupérés en interne. Les documents ignorés ont une incidence sur la latence de la requête, et l'application est facturée pour les opérations de lecture nécessaires à leur récupération.

Nouvelles tentatives de transactions

Les SDK et bibliothèques clientes Cloud Firestore relancent automatiquement les transactions ayant échoué pour gérer les erreurs temporaires. Si votre application accède à Cloud Firestore directement via les API REST ou RPC au lieu d'un SDK, elle doit implémenter de nouvelles tentatives de transaction pour améliorer la fiabilité.

Mises à jour en temps réel

Pour connaître les bonnes pratiques liées aux mises à jour en temps réel, consultez Comprendre les requêtes en temps réel à grande échelle.

Concevoir des solutions évolutives

Les bonnes pratiques suivantes décrivent comment éviter les situations qui créent des conflits.

Mises à jour d'un seul document

Lorsque vous concevez votre application, tenez compte de la rapidité avec laquelle elle met à jour des documents uniques. La meilleure façon de caractériser les performances de votre charge de travail consiste à effectuer des tests de charge. Le taux maximal exact auquel une application peut mettre à jour un seul document dépend fortement de la charge de travail. Les facteurs incluent le taux d'écriture, les conflits entre les requêtes et le nombre d'index concernés.

Une opération d'écriture de document met à jour le document et tous les index associés, et Cloud Firestore applique de manière synchrone l'opération d'écriture sur un quorum d'instances répliquées. Si les taux d'écriture sont suffisamment élevés, la base de données commencera à rencontrer des conflits, une latence plus élevée ou d'autres erreurs.

Taux de lecture, d'écriture et de suppression élevés pour une plage de documents restreinte

Évitez les taux de lecture ou d'écriture élevés sur les documents qui sont proches d'un point de vue lexicographique, ou votre application rencontrera des erreurs liées à un conflit. Ce problème, appelé "hotspotting", peut se produire si votre application effectue l'une des opérations suivantes :

  • Crée de nouveaux documents à un taux très élevé et attribue ses propres ID augmentant de façon monotone.

    Cloud Firestore attribue des ID de document à l'aide d'un algorithme de dispersion. Vous ne devriez pas rencontrer de problème de hotspotting sur les opérations d'écriture si vous créez vos documents à l'aide d'ID de document automatiques.

  • Elle crée des documents à un taux élevé dans une collection contenant peu de documents.

  • Elle crée des documents avec un champ monotone croissant, comme un horodatage, à un taux très élevé.

  • Elle supprime les documents d'une collection à un taux élevé.

  • Elle effectue des opérations d'écriture dans la base de données à un taux très élevé sans augmenter progressivement le trafic.

Éviter d'ignorer les données supprimées

Évitez les requêtes qui ignorent les données récemment supprimées. Une requête peut devoir ignorer un grand nombre d'entrées d'index si les premiers résultats de la requête ont été récemment supprimés.

Un exemple de charge de travail qui peut devoir ignorer de nombreuses données supprimées est une charge de travail qui tente de trouver les éléments de travail les plus anciens dans la file d'attente. La requête peut ressembler à ceci :

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()

Chaque fois que cette requête est exécutée, elle analyse les entrées d'index du champ created sur tous les documents récemment supprimés. Cela ralentit les requêtes.

Pour améliorer les performances, utilisez la méthode start_at afin de trouver le meilleur point de départ. Exemple :

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()

REMARQUE : L'exemple ci-dessus utilise un champ qui augmente de façon monotone, ce qui est un antimodèle pour les taux d'écriture élevés.

Augmenter le trafic

Vous devez augmenter progressivement le trafic vers les nouvelles collections ou les documents lexicographiquement proches pour donner à Cloud Firestore suffisamment de temps pour préparer les documents à l'augmentation du trafic. Nous recommandons de commencer par un maximum de 500 opérations par seconde dans une nouvelle collection, puis d'augmenter le trafic de 50 % toutes les 5 minutes. Vous pouvez également augmenter votre trafic d'écriture, mais gardez à l'esprit les limites standards de Cloud Firestore. Assurez-vous que les opérations sont réparties de manière relativement uniforme sur la plage de clés. Nous appelons cette méthode "règle 500/50/5".

Migrer le trafic vers une nouvelle collection

La montée en puissance progressive est particulièrement importante si vous migrez le trafic de votre application d'une collection vers une autre. Une façon simple de gérer cette migration consiste à lire l'ancienne collection et, si le document n'existe pas, à lire la nouvelle collection. Toutefois, cela peut entraîner une augmentation soudaine du trafic vers les documents qui sont proches d'un point de vue lexicographique dans la nouvelle collection. Cloud Firestore peut ne pas être en mesure de préparer efficacement la nouvelle collection pour l'augmentation du trafic, en particulier lorsqu'elle contient peu de documents.

Un problème similaire peut se produire si vous modifiez les ID de nombreux documents dans la même collection.

La stratégie à adopter pour migrer le trafic vers une nouvelle collection dépend de votre modèle de données. Vous trouverez ci-dessous un exemple de stratégie connue sous le nom de lectures parallèles. Vous devrez déterminer si cette stratégie est efficace pour vos données. L'impact financier des opérations parallèles pendant la migration sera un aspect important à prendre en compte.

Lectures parallèles

Pour mettre en œuvre des lectures parallèles lors de la migration du trafic vers une nouvelle collection, commencez par lire l'ancienne collection. Si le document est manquant, lisez la nouvelle collection. Un taux élevé de lectures de documents inexistants peut entraîner un problème de hotspotting. Vous devez faire en sorte d'augmenter progressivement la charge vers la nouvelle collection. Une meilleure stratégie consiste à copier l'ancien document dans la nouvelle collection, puis à supprimer l'ancien document. Augmentez progressivement les lectures parallèles pour vous assurer que Cloud Firestore peut gérer le trafic vers la nouvelle collection.

Une stratégie possible pour l'augmentation progressive des opérations de lecture ou d'écriture vers une nouvelle collection consiste à utiliser un hachage déterministe de l'ID utilisateur afin de sélectionner un pourcentage aléatoire d'utilisateurs qui tentent d'écrire de nouveaux documents. Assurez-vous que le résultat du hachage de l'ID utilisateur n'est pas faussé par votre fonction ou par le comportement des utilisateurs.

En parallèle, exécutez un job par lot qui copie toutes vos données des anciens documents vers la nouvelle collection. Votre job par lot doit éviter d'écrire dans des ID de document séquentiels afin d'éviter les hotspots. Une fois le job par lot terminé, vous ne pouvez lire que la nouvelle collection.

Vous pouvez peaufiner cette stratégie en faisant migrer simultanément des petits lots d'utilisateurs. Ajoutez un champ au document de l'utilisateur, qui suit l'état de la migration de cet utilisateur. Sélectionnez un lot d'utilisateurs à migrer en fonction du hachage de l'ID utilisateur. Utilisez un job par lot pour migrer les documents de ce lot d'utilisateurs, et utilisez des lectures parallèles pour les utilisateurs en cours de migration.

Veuillez noter qu'il n'est pas facile de revenir à la configuration précédente sans effectuer d'opérations d'écriture en double des anciennes et des nouvelles entités pendant la phase de migration. Cela augmenterait les coûts Cloud Firestore encourus.

Confidentialité

  • Évitez de stocker des informations sensibles dans un ID de projet Cloud. car celui-ci peut être conservé au-delà de la durée de vie de votre projet.
  • En tant que bonne pratique de conformité des données, nous vous recommandons de ne pas stocker d'informations sensibles dans les noms de documents et de champs de document.

Empêcher les accès non autorisés

Empêchez les opérations non autorisées sur votre base de données avec Cloud Firestore Security Rules. Par exemple, l'utilisation de règles peut éviter qu'un utilisateur malveillant télécharge de manière répétée l'intégralité de votre base de données.

En savoir plus sur l'utilisation de Cloud Firestore Security Rules