Étendre Datastore avec Cloud Functions (2nd gen)

Avec Cloud Functions et Eventarc, vous pouvez déployer du code pour gérer les événements déclenchés par les modifications apportées à votre base de données Firestore en mode Datastore. Cela vous permet d'ajouter des fonctionnalités côté serveur sans avoir à exécuter vos propres serveurs.

Déclencheurs du mode Datastore

Eventarc est compatible avec les déclencheurs d'événements Firestore en mode Datastore suivants pour vous permettre de créer des gestionnaires Cloud Functions (2nd gen) liés aux événements Firestore en mode Datastore:

Event Type Déclencheur
google.cloud.datastore.entity.v1.created Déclenché lorsqu'une entité est écrite pour la première fois.
google.cloud.datastore.entity.v1.updated Déclenchement lorsqu'une entité existe déjà et qu'une valeur a été modifiée.
google.cloud.datastore.entity.v1.deleted Déclenché lorsqu'une entité est supprimée.
google.cloud.datastore.entity.v1.written Déclenché lorsque created, updated ou deleted est déclenché.
google.cloud.datastore.entity.v1.created.withAuthContext Identique à created, mais ajoute des informations d'authentification.
google.cloud.datastore.entity.v1.updated.withAuthContext Identique à updated, mais ajoute des informations d'authentification.
google.cloud.datastore.entity.v1.deleted.withAuthContext Identique à deleted, mais ajoute des informations d'authentification.
google.cloud.datastore.entity.v1.written.withAuthContext Identique à written, mais ajoute des informations d'authentification.

Les déclencheurs d'événements en mode Datastore ne répondent qu'aux modifications d'entités. Une mise à jour d'une entité en mode Datastore dans laquelle les données ne sont pas modifiées (écriture no-op) ne génère pas d'événement de mise à jour ni d'écriture. Vous ne pouvez pas générer d'événements pour certaines propriétés seulement.

Inclure le contexte d'authentification dans l'événement

Pour inclure des informations d'authentification supplémentaires sur l'événement, utilisez un déclencheur d'événement avec l'extension withAuthContext. Cette extension ajoute des informations supplémentaires sur le compte principal qui a déclenché l'événement. Elle ajoute les attributs authtype et authid en plus des informations renvoyées dans l'événement de base. Pour en savoir plus sur les valeurs d'attribut, consultez la documentation de référence sur authcontext.

Écrire une fonction déclenchée par une entité

Pour écrire une fonction qui répond aux événements Firestore en mode Datastore, préparez-vous à spécifier les éléments suivants lors du déploiement:

  • un type d'événement déclencheur
  • Un filtre d'événement déclencheur pour sélectionner les entités associées à la fonction
  • le code de la fonction pour exécuter

Filtres d'événements déclencheurs

Lorsque vous spécifiez un filtre d'événement, vous pouvez spécifier une correspondance d'entité exacte ou un modèle de chemin d'accès. Utilisez un format de chemin d'accès pour faire correspondre plusieurs entités avec les caractères génériques * ou **.

Par exemple, vous pouvez spécifier une correspondance d'entité exacte pour répondre aux modifications apportées à l'entité suivante:

users/marie

Utilisez des caractères génériques, * ou **, pour répondre aux modifications des entités correspondant à un format. Le caractère générique * correspond à un seul segment, tandis que le caractère générique à plusieurs segments ** correspond à zéro, un ou plusieurs segments du modèle.

Pour les correspondances de segment unique (*), vous pouvez également utiliser un groupe de capture nommé, tel que users/{userId}.

Le tableau suivant illustre des formats de chemin d'accès valides:

Modèle Description
users/* ou users/{userId} Correspond à toutes les entités du genre users. Ne correspond pas au niveau des entités descendantes comme /users/marie/messages/33e2IxYBD9enzS50SJ68
users/** Correspond à toutes les entités de genre users et à toutes les entités descendantes telles que /users/marie/messages/33e2IxYBD9enzS50SJ68

Pour en savoir plus sur les modèles de chemin d'accès, consultez la section Modèles de chemin Eventarc.

Votre déclencheur doit toujours pointer vers une entité, même si vous utilisez un caractère générique. Consultez les exemples suivants :

  • users/{userId=*}/{messages=*} n'est pas valide, car {messages=*} est un ID de genre.

  • users/{userId=*}/{messages}/{messageId=*} est valide, car {messageId=*} pointe toujours vers une entité.

Échappement des caractères

Cette section décrit les situations dans lesquelles vous devez échapper les caractères des ID de genre et des ID d'entité. L'échappement d'un caractère permet au filtre d'événement d'interpréter correctement l'ID.

  • Si un ID de genre ou un ID d'entité comprend un caractère ~ ou /, vous devez échapper l'ID dans votre filtre d'événement. Pour échapper un ID, utilisez le format __escENCODED_ID__. Remplacez ENCODED_ID par un ID de genre ou un ID d'entité dont tous les caractères ~ et / sont remplacés par leurs ID d'encodage, à savoir:

    • ~ : ~0
    • / : ~1

    Par exemple, l'ID de genre user/profile devient __escusers~1profile__. Voici un exemple de modèle de chemin d'accès avec cet ID de genre : __escusers~1profile__/{userId}

  • Si vous utilisez l'ID de genre ou l'ID d'entité . ou .. dans votre filtre d'événement, vous devez échapper l'ID comme suit:

    • . : __esc~2__
    • .. : __esc~2~2__

    L'échappement du caractère . n'est nécessaire que si l'ID correspond exactement à . ou ... Par exemple, l'ID de genre customers.info ne nécessite pas d'échappement.

  • Si votre ID de genre ou d'entité est une valeur numérique et non une valeur de chaîne, vous devez échapper l'ID avec __idNUMERIC_VALUE__. Par exemple, le modèle de chemin d'accès d'une entité de genre 111 et d'ID d'entité 222 est __id111__/__id222__.

  • Si vous avez migré de l'ancien service Cloud Datastore vers Firestore en mode Datastore, votre base de données peut contenir d'anciens ID dans un encodage non UTF8. Vous devez échapper ces ID avec __bytesBASE64_ENCODING__. Remplacez BASE64_ENCODING par l'encodage en base64 de l'ID. Par exemple, le modèle de chemin d'accès Task/{task} avec échappement pour l'ID de genre non UTF8 Task devient __bytesVGFzaw==__/{task}.

Exemples de fonctions

L'exemple suivant montre comment recevoir des événements en mode Datastore. Pour utiliser les données impliquées dans un événement, consultez les champs value et old_value.

  • value: objet EntityResult contenant un instantané d'entité post-opération. Ce champ n'est pas renseigné pour les événements de suppression.
  • old_value: objet EntityResult contenant un instantané d'entité de pré-opération. Ce champ n'est renseigné que pour les événements de mise à jour et de suppression.

Java

Pour savoir comment installer et utiliser la bibliothèque cliente pour le mode Datastore, consultez la page Bibliothèques clientes en mode Datastore. Pour en savoir plus, consultez la documentation de référence de l'API Java en mode Datastore.

Pour vous authentifier auprès du mode Datastore, configurez les Identifiants par défaut de l'application. Pour en savoir plus, consultez Configurer l'authentification pour un environnement de développement local.

import com.google.cloud.functions.CloudEventsFunction;
import com.google.events.cloud.datastore.v1.EntityEventData;
import com.google.protobuf.InvalidProtocolBufferException;
import io.cloudevents.CloudEvent;
import java.util.logging.Logger;

public class Datastore implements CloudEventsFunction {
  private static final Logger logger = Logger.getLogger(Datastore.class.getName());

  @Override
  public void accept(CloudEvent event) throws InvalidProtocolBufferException {
    EntityEventData datastoreEventData = EntityEventData.parseFrom(event.getData().toBytes());

    logger.info("Function triggered by event on: " + event.getSource());
    logger.info("Event type: " + event.getType());

    logger.info("Old value:");
    logger.info(datastoreEventData.getOldValue().toString());

    logger.info("New value:");
    logger.info(datastoreEventData.getValue().toString());
  }
}

Inclure les dépendances proto dans votre code source

Vous devez inclure le fichier data.proto en mode Datastore dans le répertoire source de votre fonction. Ce fichier importe les fichiers proto suivants, que vous devez également inclure dans votre répertoire source:

Utilisez la même structure de répertoire pour les dépendances. Par exemple, placez struct.proto dans google/protobuf.

Ces fichiers sont nécessaires pour décoder les données d'événement. Si la source de votre fonction n'inclut pas ces fichiers, une erreur est renvoyée lors de son exécution.

Attributs d'événement

Chaque événement inclut des attributs de données qui incluent des informations sur l'événement, telles que l'heure à laquelle il s'est déclenché. Firestore en mode Datastore ajoute des données supplémentaires sur la base de données et l'entité impliquées dans l'événement. Vous pouvez accéder à ces attributs comme suit:

Java
logger.info("Event time " + event.getTime());
logger.info("Event project: " + event.getExtension("project"));
logger.info("Event location: " + event.getExtension("location"));
logger.info("Database name: " + event.getExtension("database"));
logger.info("Database namespace: " + event.getExtension("namespace"));
logger.info("Database entity: " + event.getExtension("entity"));
// For withAuthContext events
logger.info("Auth information: " + event.getExtension("authid"));
logger.info("Auth information: " + event.getExtension("authtype"));

Déployer une fonction

Les utilisateurs qui déploient Cloud Functions doivent disposer du rôle IAM de développeur Cloud Functions ou d'un rôle comprenant les mêmes autorisations. Consultez également la section Configuration supplémentaire pour le déploiement.

Vous pouvez déployer une fonction à l'aide de gcloud CLI ou de la console Google Cloud. L'exemple ci-dessous illustre le déploiement à l'aide de gcloud CLI. Pour en savoir plus sur le déploiement avec la console Google Cloud, consultez la section Déployer Cloud Functions.

  1. Dans la console Google Cloud, activez Cloud Shell.

    Activer Cloud Shell

    En bas de la fenêtre de la console Google Cloud, une session Cloud Shell démarre et affiche une invite de ligne de commande. Cloud Shell est un environnement shell dans lequel Google Cloud CLI est déjà installé, et dans lequel des valeurs sont déjà définies pour votre projet actuel. L'initialisation de la session peut prendre quelques secondes.

  2. Exécutez la commande gcloud functions deploy pour déployer une fonction :

    gcloud functions deploy FUNCTION_NAME \
    --gen2 \
    --region=FUNCTION_LOCATION \
    --trigger-location=TRIGGER_LOCATION \
    --runtime=RUNTIME \
    --source=SOURCE_LOCATION \
    --entry-point=CODE_ENTRYPOINT \
    --trigger-event-filters="type=EVENT_FILTER_TYPE" \
    --trigger-event-filters="database=DATABASE" \
    --trigger-event-filters="namespace=NAMESPACE" \
    --trigger-event-filters-path-pattern="entity=ENTITY_OR_PATH" \
    

    Le premier argument, FUNCTION_NAME, est le nom de la fonction déployée. Le nom de la fonction doit commencer par une lettre suivie de 62 caractères au maximum (lettres, chiffres, traits d'union ou traits de soulignement) et doit se terminer par une lettre ou un chiffre. Remplacez FUNCTION_NAME par un nom de fonction valide. Ajoutez ensuite les indicateurs suivants:

    • L'option --gen2 spécifie que vous souhaitez déployer sur Cloud Functions (2nd gen). Si vous omettez cette option, le déploiement sur Cloud Functions (1st gen) sera effectué.

    • L'option --region=FUNCTION_LOCATION spécifie la région dans laquelle déployer votre fonction.

      Pour maximiser la proximité, définissez FUNCTION_LOCATION sur une région proche de votre base de données Firestore. Si votre base de données Firestore se trouve dans une zone multirégionale, définissez la valeur sur us-central1 pour les bases de données dans nam5 et sur europe-west4 pour les bases de données dans eur3. Pour les emplacements Firestore régionaux, définissez-les sur la même région.

    • L'option --trigger-location=TRIGGER_LOCATION spécifie l'emplacement du déclencheur. Vous devez définir TRIGGER_LOCATION sur l'emplacement de votre base de données en mode Datastore.

    • L'option --runtime=RUNTIME spécifie l'environnement d'exécution de langage qui est utilisé par votre fonction. Cloud Functions est compatible avec plusieurs environnements d'exécution. Pour en savoir plus, consultez la section Environnements d'exécution. Définissez RUNTIME sur un environnement d'exécution compatible.

    • L'option --source=SOURCE_LOCATION spécifie l'emplacement du code source de votre fonction. Pour en savoir plus, consultez les articles suivants:

      Définissez SOURCE_LOCATION sur l'emplacement du code source de votre fonction.

    • L'option --entry-point=CODE_ENTRYPOINT spécifie le point d'entrée de votre fonction dans votre code source. Il s'agit du code exécuté par votre fonction. Vous devez définir CODE_ENTRYPOINT sur un nom de fonction ou un nom de classe complet existant dans votre code source. Pour en savoir plus, consultez la section Point d'entrée de la fonction.

    • Les options --trigger-event-filters définissent le filtre d'événement, qui inclut le type de déclencheur et l'entité ou le chemin d'accès qui déclenche les événements. Définissez les valeurs d'attribut suivantes pour définir votre filtre d'événement:

      • type=EVENT_FILTER_TYPE: Firestore accepte les types d'événements suivants:

        • google.cloud.datastore.entity.v1.created: l'événement est envoyé lorsqu'une entité est écrite pour la première fois.
        • google.cloud.datastore.entity.v1.updated: l'événement est envoyé lorsqu'une entité existe déjà et qu'une valeur est modifiée.
        • google.cloud.datastore.entity.v1.deleted: l'événement est envoyé lorsqu'une entité est supprimée.
        • google.cloud.datastore.entity.v1.written: l'événement est envoyé lorsqu'une entité est créée, mise à jour ou supprimée.
        • google.cloud.datastore.entity.v1.created.withAuthContext: l'événement est envoyé lorsqu'un document est écrit pour la première fois et qu'il inclut des informations d'authentification supplémentaires.
        • google.cloud.datastore.entity.v1.updated.withAuthContext: l'événement est envoyé lorsqu'un document existe déjà et qu'une valeur est modifiée. Inclut des informations d'authentification supplémentaires
        • google.cloud.datastore.entity.v1.deleted.withAuthContext: l'événement est envoyé lorsqu'un document est supprimé. Inclut des informations d'authentification supplémentaires
        • google.cloud.datastore.entity.v1.written.withAuthContext: l'événement est envoyé lorsqu'un document est créé, mis à jour ou supprimé. Inclut des informations d'authentification supplémentaires

        Définissez EVENT_FILTER_TYPE sur l'un de ces types d'événements.

      • database=DATABASE : base de données Firestore. Pour le nom de la base de données par défaut, définissez DATABASE sur (default).

      • namespace=NAMESPACE: espace de noms de la base de données Pour le nom de base de données par défaut, définissez NAMESPACE sur (default). Supprimez l'option pour la faire correspondre à n'importe quel espace de noms.

      • entity=ENTITY_OR_PATH: chemin d'accès à la base de données qui déclenche des événements lorsque des données sont créées, mises à jour ou supprimées. Valeurs acceptées pour ENTITY_OR_PATH:

        • Égal à. Par exemple : --trigger-event-filters="entity='users/marie'"
        • Format de chemin d'accès. Exemple : --trigger-event-filters-path-pattern="entity='users/*'" Pour en savoir plus, consultez la page Comprendre les formats de chemin d'accès.

      Vous pouvez éventuellement spécifier des options supplémentaires de configuration, de mise en réseau et de sécurité lorsque vous déployez une fonction.

      Pour en savoir plus sur la commande de déploiement et ses options, consultez la documentation sur gcloud functions deploy.

Exemples de déploiements

Les exemples suivants illustrent les déploiements avec la Google Cloud CLI.

Déployez une fonction pour une base de données dans la région us-west2:

gcloud functions deploy gcfv2-trigger-datastore-node \
--gen2 \
--region=us-west2 \
--trigger-location=us-west2 \
--runtime=nodejs18 \
--source=gs://example_bucket-1/datastoreEventFunction.zip \
--entry-point=makeUpperCase \
--trigger-event-filters=type=google.cloud.datastore.entity.v1.written \
--trigger-event-filters=database='(default)' \
--trigger-event-filters-path-pattern="entity='messages/{pushId}'"

Déployez une fonction pour une base de données dans l'emplacement multirégional nam5:

gcloud functions deploy gcfv2-trigger-datastore-python \
--gen2 \
--region=us-central1 \
--trigger-location=nam5 \
--runtime=python311 \
--source=gs://example_bucket-1/datastoreEventFunction.zip \
--entry-point=make_upper_case \
--trigger-event-filters=type=google.cloud.datastore.entity.v1.written.withAuthContext \
--trigger-event-filters=database='(default)' \
--trigger-event-filters-path-pattern="entity='messages/{pushId}'"

Limites

Notez les limitations suivantes concernant les déclencheurs Firestore pour Cloud Functions :

  • Cloud Functions (1re génération) nécessite une base de données "(par défaut)" existante dans Firestore en mode natif. Il n'est pas compatible avec les bases de données nommées Firestore, ni avec le mode Datastore. Dans ce cas, veuillez utiliser Cloud Functions (2nd gen) pour configurer des événements.
  • L'ordre n'est pas garanti. Les modifications rapides peuvent déclencher des appels de fonctions dans un ordre inattendu.
  • Bien que les événements soient diffusés une fois au moins, un même événement peut produire plusieurs appels de fonction. Évitez de dépendre de procédés dits "exactement une fois" et écrivez des fonctions idempotentes.
  • Firestore en mode Datastore nécessite Cloud Functions (2e génération). Cloud Functions (1re génération) n'est pas compatible avec le mode Datastore.
  • Un déclencheur est associé à une seule base de données. Vous ne pouvez pas créer de déclencheur correspondant à plusieurs bases de données.
  • La suppression d'une base de données ne supprime pas automatiquement les déclencheurs de cette base de données. Le déclencheur cesse de diffuser des événements, mais continue d'exister jusqu'à ce que vous le supprimiez.
  • Si un événement correspondant dépasse la taille maximale de requête, il risque de ne pas être distribué à Cloud Functions (1re génération).
    • Les événements non diffusés en raison de la taille de la requête sont consignés dans les journaux de la plate-forme et sont comptabilisés dans l'utilisation des journaux pour le projet.
    • Vous trouverez ces journaux dans l'explorateur de journaux avec le message "Impossible de distribuer l'événement à la fonction Cloud, car la taille dépasse la limite pour la 1re génération..." de gravité error. Vous trouverez le nom de la fonction dans le champ functionName. Si le champ receiveTimestamp se trouve toujours dans l'heure qui suit, vous pouvez déduire le contenu réel de l'événement en lisant le document en question avec un instantané avant et après l'horodatage.
    • Pour éviter une telle cadence, vous pouvez :
      • Migrer et passer à Cloud Functions (2e génération)
      • Réduire la taille du document
      • Supprimer les fonctions Cloud Functions en question
    • Vous pouvez désactiver la journalisation elle-même à l'aide d'exclusions, mais sachez que les événements incriminés ne seront toujours pas transmis.

Emplacements Eventarc et Firestore en mode Datastore

Eventarc n'est pas compatible avec les emplacements multirégionaux pour les déclencheurs d'événements Firestore, mais vous pouvez toujours créer des déclencheurs pour les bases de données Firestore dans des emplacements multirégionaux. Eventarc mappe les emplacements multirégionaux Firestore sur les régions Eventarc suivantes:

Firestore – Plusieurs régions Région Eventarc
nam5 us-central1
eur3 europe-west4

Étapes suivantes