Module 6: Migrer de Cloud Datastore vers Cloud Firestore

1. Présentation

Cette série d'ateliers de programmation (tutoriels pratiques à suivre à votre rythme) est destinée à aider les développeurs Google App Engine (standard) à moderniser leurs applications en les guidant tout au long d'une série de migrations. La plupart de ces migrations consistent à délaisser les services groupés de l'environnement d'exécution d'origine, car les environnements d'exécution nouvelle génération sont plus souples et offrent aux utilisateurs davantage d'options de service. Une autre façon de moderniser une application consiste à passer à un produit plus récent. C'est le sujet de cet atelier de programmation.

Les utilisateurs d'App Engine qui accèdent à Datastore avec les bibliothèques clientes Cloud NDB ou Cloud Datastore n'ont pas besoin de migrer davantage. Toutefois, Cloud Firestore représente le dernier data store NoSQL évolutif et à haute disponibilité, avec des fonctionnalités issues de la base de données en temps réel Firebase.

Vous êtes au bon endroit si vous êtes un développeur qui se sent obligé d'utiliser Firestore pour profiter de ses fonctionnalités ou qui, au moins, est suffisamment intéressé pour explorer ce qu'implique la migration. Ce tutoriel explique comment migrer une application App Engine utilisant Cloud Datastore vers Cloud Firestore.

Vous apprendrez à effectuer les tâches suivantes :

  • Identifier les différences entre Datastore et Firestore
  • Migrer de Cloud Datastore vers Cloud Firestore

Prérequis

Enquête

Comment allez-vous utiliser cet atelier de programmation ?

Je vais le lire uniquement Je vais le lire et effectuer les exercices

2. Arrière-plan

En 2013, Datastore d'App Engine est devenu un produit à part entière, Google Cloud Datastore, et est désormais accessible aux développeurs en dehors d'App Engine. L'année suivante, Firebase a été racheté par Google. À l'époque, elle était connue pour sa base de données en temps réel.

Au cours des années suivantes, les équipes Firebase et Cloud Datastore ont travaillé à l'intégration de certaines fonctionnalités Firebase dans Datastore. Par conséquent, la nouvelle génération de Cloud Datastore a été lancée en 2017. Pour refléter l'héritage de certaines fonctionnalités Firebase, il a été renommé Cloud Firestore.

Cloud Firestore est devenu le mécanisme de stockage NoSQL par défaut pour les projets Google Cloud. Les nouvelles applications peuvent utiliser Cloud Firestore de manière native, tandis que les bases de données Datastore existantes ont été converties en Firestore en coulisses et fonctionnent désormais en tant que "Firestore en mode Datastore" pour préserver la compatibilité avec les opérations Datastore. Par conséquent, les applications ne peuvent utiliser Cloud Firestore que dans l'un de ces modes, et une fois défini, le mode ne peut plus être modifié.

Actuellement, lorsque les utilisateurs créent des projets et sélectionnent une solution NoSQL, ils sont invités à choisir entre Firestore en mode Datastore et Firestore en mode natif. Une fois que les utilisateurs ont ajouté des entités Datastore, ils ne peuvent pas passer à Firestore. De même, une fois le mode natif Firestore sélectionné, ils ne peuvent plus revenir à Datastore (ou plutôt à Firestore en mode Datastore). Pour en savoir plus, consultez la page Choisir entre Cloud Firestore en mode Datastore et le mode Firestore natif dans la documentation. Pour migrer une application vers Firestore, vous devez créer un projet, exporter Datastore, puis l'importer dans Firestore. L'objectif de ce tutoriel est de donner aux développeurs une idée des différences entre l'utilisation de Cloud Datastore et de Cloud Firestore.

Nous ne nous attendons pas à ce que les utilisateurs effectuent cette migration. C'est pourquoi elle est facultative. Bien que l'utilisation native de Cloud Firestore présente des avantages évidents, tels que l'authentification du client, l'intégration des règles Firebase et, bien sûr, la base de données Firebase en temps réel, les étapes de migration sont "inconvénientes" :

  • Vous devez utiliser un projet différent de celui de votre application actuelle.
  • Un projet dans lequel une application a ajouté des entités Datastore ne peut pas passer à Firestore en mode natif.
  • De même, un projet qui a sélectionné Firestore en mode natif ne peut pas revenir à Firestore en mode Datastore.
  • Il n'existe aucun outil de migration permettant de transférer des données d'un projet à un autre.
  • Certaines fonctionnalités critiques de Datastore, y compris les espaces de noms et un débit d'écriture plus élevé (> 10 000/s), ne sont pas disponibles dans Firestore.
  • Les outils d'exportation et d'importation sont "primitifs" et fonctionnent selon le principe du "tout ou rien".
    • Si votre application comporte de nombreuses entités Datastore, l'exportation et l'importation dans Firestore peuvent prendre plusieurs heures.
    • Pendant cette période, votre application ou service ne pourra pas écrire ni mettre à jour de données.
    • Les activités de migration sont comptabilisées dans l'utilisation normale. Vous pouvez les répartir (dans les quotas quotidiens si possible) pour minimiser les coûts.
    • Comme votre nouveau service s'exécute dans un autre projet, vous devez prévoir un délai pour que les mises à jour DNS se propagent.
  • Datastore et Firestore ont des modèles de données similaires, mais différents. La migration nécessite donc de modifier le fonctionnement de l'application/du service.
    • Les requêtes ancêtres de Datastore sont désormais des requêtes de collection Firestore (par défaut).
    • Les requêtes de type large de Datastore sont des requêtes de groupe de collections Firestore.
    • Les index et la gestion sont différents, etc.

Cela dit, si vous avez une application assez simple à migrer, si vous vous préparez à simuler une telle migration ou si vous êtes simplement là pour en savoir plus sur Datastore et Firestore, n'hésitez pas à continuer !

Utilisateurs de Python 2 : cet atelier de programmation de migration facultatif n'est présenté qu'en Python 3. Toutefois, comme Cloud Firestore est également compatible avec la version 2.x, les utilisateurs peuvent interpoler les différences d'utilisation. Par exemple, les enregistrements Firestore utilisent des chaînes Unicode (au lieu de chaînes d'octets). Un indicateur de début u'' est donc requis pour les littéraux de chaîne Python 2. Cela signifie qu'une fonction store_visit() 2.x se présentera comme suit :

def store_visit(remote_addr, user_agent):
    doc_ref = fs_client.collection(u'Visit')
    doc_ref.add({
        u'timestamp': datetime.now(),
        u'visitor': u'{}: {}'.format(remote_addr, user_agent),
    })

À part cela, la bibliothèque cliente devrait fonctionner de la même manière. Le seul autre problème à prendre en compte est que la bibliothèque Cloud Firestore 2.x est "figée" en termes de développement. Par conséquent, de plus en plus de fonctionnalités plus récentes ne seront disponibles que dans la bibliothèque cliente Firestore 3.x.

Les principales étapes de ce tutoriel sont les suivantes :

  1. Configuration/Préparation
  2. Ajouter la bibliothèque Cloud Firestore
  3. Mettre à jour les fichiers de l'application

3. Configuration/Préparation

Avant de passer à la partie principale de ce tutoriel, nous allons configurer notre projet, obtenir le code et déployer l'application de base pour nous assurer de bien utiliser du code fonctionnel.

1. Configurer le projet

Nous vous conseillons de réutiliser le même projet que celui utilisé pour l'atelier de programmation du module 3. Vous pouvez également créer un projet ou réutiliser un autre projet existant. Assurez-vous que le projet dispose d'un compte de facturation actif et que App Engine (app) est activé.

2. Obtenir un exemple d'application de référence

L'une des conditions préalables à cet atelier de programmation est de disposer d'un exemple d'application fonctionnel du Module 3. Si vous n'en avez pas, suivez le tutoriel du module 3 (lien ci-dessus) avant de continuer. Si vous connaissez déjà le contenu de ce tutoriel, vous pouvez récupérer le code du module 3 ci-dessous.

Que vous utilisiez votre application ou la nôtre, le code de départ ("START") est celui du module 3. Le présent atelier de programmation du module 6 vous guide pour chaque étape jusqu'à obtenir un code similaire au code final ("FINISH"). (Ce tutoriel n'est disponible que pour Python 3.)

Le répertoire des fichiers du module 3 (qu'il s'agisse de votre code ou du nôtre) doit se présenter comme suit :

$ ls
README.md               main.py                 templates
app.yaml                requirements.txt

3. (Re)Déployer l'application du module 3

Étapes préliminaires restantes :

  1. Familiarisez-vous avec l'outil de ligne de commande gcloud (si nécessaire).
  2. (Re)déployez le code du module 3 sur App Engine (si nécessaire).

Une fois que vous avez exécuté ces étapes et vérifié le bon fonctionnement du code, nous pouvons passer à la suite de ce tutoriel en commençant par les fichiers de configuration.

Exigences Python 2

  • Assurez-vous que app.yaml référence (toujours) les packages tiers groupés : grpcio et setuptools.
  • Assurez-vous que appengine_config.py utilise toujours pkg_resources et google.appengine.ext.vendor pour orienter l'application vers des ressources tierces.
  • Dans la section suivante sur la mise à jour de requirements.txt, vous devez utiliser google-cloud-firestore==1.9.0, car il s'agit de la dernière version de la bibliothèque cliente Python Firestore compatible avec la version 2.x.
    • Si votre requirements.txt comporte une entrée pour google-cloud-core, laissez-la telle quelle.
    • Supprimez lib et réinstallez-le avec pip install -t lib -r requirements.txt.

4. Mettre à jour les fichiers de configuration (ajouter la bibliothèque Cloud Firestore)

Après la configuration, les prochaines étapes consistent à mettre à jour la configuration, puis les fichiers d'application. Pour le premier, la seule modification de configuration est un petit échange de package dans votre fichier requirements.txt. Faisons-le maintenant.

Remplacez la ligne google-cloud-datastore par google-cloud-firestore dans requirements.txt pour qu'elle ressemble à ceci :

Flask==1.1.2
google-cloud-firestore==2.0.2

Nous vous recommandons d'utiliser les versions les plus récentes de chaque bibliothèque. Les numéros de version ci-dessus correspondent aux dernières versions au moment de la rédaction de cet article. Le code du dépôt (code final "FINISH") est mis à jour plus fréquemment et peut contenir une version plus récente.

Aucune autre modification de configuration n'est requise. app.yaml et templates/index.html restent donc inchangés.

5. Mettre à jour les fichiers de l'application

Il n'y a qu'un fichier d'application : main.py. Par conséquent, toutes les modifications de la présente section s'appliquent uniquement à ce fichier.

1. Importations

Le changement d'importation du package est mineur, de datastore à firestore :

  • AVANT :
from google.cloud import datastore
  • APRÈS :
from google.cloud import firestore

2. Accès à Firestore

Après avoir initialisé Flask, créez votre client Firestore. Effectuez une modification similaire à celle ci-dessus, mais pour l'initialisation du client :

  • AVANT :
app = Flask(__name__)
ds_client = datastore.Client()
  • APRÈS :
app = Flask(__name__)
fs_client = firestore.Client()

En effectuant la migration de Cloud NDB vers Cloud Datastore, vous avez déjà fait le plus dur pour passer à Cloud Firestore. Avec Datastore, vous créez des enregistrements de données sous la forme d'entités composées de propriétés communes et vous les regroupez par clés. Dans Firestore, les enregistrements de données sont des documents composés de paires clé/valeur et regroupés dans des collections. Lorsque vous migrez depuis Datastore, vous devez tenir compte de ces différences, car elles se manifesteront lorsque vous créerez des enregistrements de données et lorsque vous les interrogerez. Vos résultats peuvent varier en fonction de la complexité de votre code Datastore.

Pour Datastore, vous créez des requêtes basées sur le type d'entité, ainsi que des critères de filtrage et de tri. Pour Firestore, l'interrogation des données est similaire. Prenons un exemple rapide, en supposant les valeurs de requête, les clients (ds_client ou fs_client, respectivement) et les importations suivants :

from datetime import datetime
from firestore.Query import DESCENDING

OCT1 = datetime(2020, 10, 1)
LIMIT = 10

Pour Datastore, interrogeons les dix entités Visit les plus récentes, postérieures au 1er octobre 2020, par ordre décroissant :

query = ds_client.query(kind='Visit')
query.add_filter('timestamp', '>=', datetime(2020, 10, 1))
query.order = ['-timestamp']
return query.fetch(limit=LIMIT)

Faites de même pour Firestore, à partir de la collection Visit :

query = fs_client.collection('Visit')
query.where('timestamp', '>=', datetime(2020, 10, 1))
query.order_by('timestamp', direction=DESCENDING)
return query.limit(LIMIT).stream()

La requête de l'exemple d'application est plus simple (pas de clause "WHERE"). Pour rappel, voici le code Cloud Datastore :

  • AVANT :
def store_visit(remote_addr, user_agent):
    entity = datastore.Entity(key=ds_client.key('Visit'))
    entity.update({
        'timestamp': datetime.now(),
        'visitor': '{}: {}'.format(remote_addr, user_agent),
    })
    ds_client.put(entity)

def fetch_visits(limit):
    query = ds_client.query(kind='Visit')
    query.order = ['-timestamp']
    return query.fetch(limit=limit)

Lorsque vous migrez vers Firestore, vous constaterez que la création de documents est semblable à celle des entités, et que les requêtes sont identiques à celles présentées précédemment.

  • APRÈS :
def store_visit(remote_addr, user_agent):
    doc_ref = fs_client.collection('Visit')
    doc_ref.add({
        'timestamp': datetime.now(),
        'visitor': '{}: {}'.format(remote_addr, user_agent),
    })

def fetch_visits(limit):
    visits_ref = fs_client.collection('Visit')
    visits = (v.to_dict() for v in visits_ref.order_by('timestamp',
            direction=firestore.Query.DESCENDING).limit(limit).stream())
    return visits

La fonction principale root() reste la même, tout comme le fichier de modèle index.html. Vérifiez vos modifications, enregistrez-les, déployez-les et validez-les.

6. Résumé/Nettoyage

Déployer l'application

Redéployez votre application avec gcloud app deploy et confirmez qu'elle fonctionne correctement. Votre code doit maintenant correspondre à celui du dépôt du module 6 (ou à une version 2.x si vous préférez).

Si vous avez consulté cette série sans suivre les ateliers de programmation précédents, l'application elle-même ne change pas. Elle enregistre toutes les visites sur la page Web principale (/) et ressemble à ceci une fois que vous avez consulté le site suffisamment de fois :

Application Visitme

Bravo ! Vous avez terminé la migration optionnelle du module 6. Il s'agit probablement de l'une des dernières migrations que vous pouvez effectuer en ce qui concerne le stockage de données App Engine. Si vous ne l'avez pas encore fait, vous pouvez envisager de conteneuriser votre application pour Cloud Run (voir les ateliers de programmation des modules 4 et 5, dont les liens sont fournis ci-dessous).

Facultatif : Effectuer un nettoyage

Que diriez-vous d'un bon nettoyage pour éviter que des frais vous soient facturés tant que vous n'êtes pas prêt à passer à l'atelier de programmation suivant sur la migration ? En tant que développeur existant, vous connaissez probablement déjà les informations de tarification d'App Engine.

Facultatif : Désactiver l'application

Si vous n'êtes pas encore prêt à passer au tutoriel suivant, désactivez votre application pour éviter les frais. Lorsque vous serez prêt à passer au prochain atelier de programmation, vous pourrez le réactiver. Tant que votre application est désactivée, elle ne génère aucun trafic, donc aucuns frais. Toutefois, si vous dépassez le quota sans frais, votre utilisation de Firestore vous sera facturée alors supprimez suffisamment d'éléments pour rester dans la limite.

En revanche, si vous ne souhaitez pas poursuivre vos migrations et souhaitez supprimer tous les éléments, vous pouvez arrêter le projet.

Étapes suivantes

Après ce tutoriel, vous pouvez envisager de suivre d'autres ateliers de programmation sur les modules de migration :

  • Module 7 : files d'attente d'envoi de tâches App Engine (obligatoire si vous utilisez des files d'attente de tâches d'envoi de tâches [push])
    • Ajoutez des tâches push taskqueue App Engine à l'application du module 1.
    • Préparez les utilisateurs à migrer vers Cloud Tasks dans le module 8.
  • Module 4 : Migrer vers Cloud Run avec Docker
    • Conteneurisez votre application pour l'exécuter sur Cloud Run avec Docker.
    • Cette migration vous permet de rester sur Python 2.
  • Module 5 : Migrer vers Cloud Run avec les packs de création Cloud
    • Conteneurisez votre application pour qu'elle s'exécute sur Cloud Run avec les packs de création Cloud.
    • Il n'est pas nécessaire de déjà connaître Docker, les conteneurs ou les Dockerfile.
    • Nécessite que votre application ait déjà été migrée vers Python 3 (les packs de création ne sont pas compatibles avec Python 2)

7. Ressources supplémentaires

Problèmes/commentaires concernant le module de migration App Engine en atelier de programmation

Si vous rencontrez des problèmes avec cet atelier de programmation, commencez par faire une recherche avant de les signaler. Liens vers la recherche et la création d'un signalement :

Ressources de migration

Vous trouverez dans le tableau ci-dessous des liens vers les dossiers de dépôt correspondant au module 3 ("START") et au module 6 ("FINISH"). Vous pouvez également y accéder depuis le dépôt pour toutes les migrations App Engine que vous pouvez cloner ou télécharger sous forme de fichier ZIP.

Atelier de programmation

Python 2

Python 3

Module 3

(code)

code

Module 6

(n/a)

code

Ressources App Engine

Vous trouverez ci-dessous d'autres ressources concernant cette migration spécifique :