É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 des modifications dans votre base de données Firestore en mode Datastore. Cela vous permet d'ajouter des fonctionnalités côté serveur sans passer par 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 à des é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éclenché lorsqu'une entité existe déjà et qu'une valeur est 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 du 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 ou d'écriture. Vous ne pouvez pas générer d'événements uniquement pour des propriétés spécifiques.

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. Il 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, vous devez vous préparer à 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 à exécuter

Filtres pour les événements déclencheurs

Lorsque vous spécifiez un filtre d'événement, vous pouvez spécifier une correspondance d'entité exacte ou un format de chemin d'accès. Utilisez un format de chemin d'accès pour mettre en correspondance plusieurs entités à l'aide des 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 d'entités correspondant à un modèle. Le caractère générique * correspond à un seul segment, et le caractère générique multisegment ** 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 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, consultez 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 des caractères dans les ID de genre et les 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 d'entité inclut un caractère ~ ou /, vous devez l'échapper dans votre filtre d'événement. Pour échapper un ID, utilisez le format __escENCODED_ID__. Remplacez ENCODED_ID par un ID de genre ou d'entité dans lequel tous les caractères ~ et / sont remplacés par leurs ID d'encodage, qui sont les suivants:

    • ~ : ~0
    • / : ~1

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

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

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

    Vous ne devez échapper le caractère . que si l'ID est exactement . ou ... Par exemple, l'ID de genre customers.info ne nécessite pas d'échappement.

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

  • Si vous avez migré de l'ancien Cloud Datastore vers Firestore en mode Datastore, votre base de données peut contenir d'anciens ID avec un encodage non UTF8. Vous devez échapper ces identifiants avec __bytesBASE64_ENCODING__. Remplacez BASE64_ENCODING par l'encodage en base64 de l'ID. Par exemple, le format de chemin 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 gérer les données associées à un événement, consultez les champs value et old_value.

  • value: objet EntityResult qui contient 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 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épertoires 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, elle renvoie une erreur 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 a été 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 avec la gcloud CLI. Pour en savoir plus sur le déploiement avec la console Google Cloud, consultez la page Déployer des fonctions 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 à proximité 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 de la région nam5 et sur europe-west4 pour celles de la région eur3. Pour les emplacements Firestore régionaux, définissez 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 page 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 que votre fonction exécute lors de son exécution. 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. Indiquez 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 a été 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: un é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 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. Comprend des informations d'authentification supplémentaires
        • google.cloud.datastore.entity.v1.deleted.withAuthContext: l'événement est envoyé lorsqu'un document est supprimé. Comprend des informations d'authentification supplémentaires
        • google.cloud.datastore.entity.v1.written.withAuthContext: un événement est envoyé lorsqu'un document est créé, mis à jour ou supprimé, ainsi qu'un événement. Comprend 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 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 qu'elle corresponde à 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. Les valeurs acceptées pour ENTITY_OR_PATH sont les suivantes:

        • É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 "(default)" existante en mode natif Firestore. La solution n'est pas compatible avec les bases de données nommées Firestore ni avec le mode Datastore. Veuillez utiliser Cloud Functions (2e génération) pour configurer des événements dans ce cas.
  • 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 un déclencheur qui correspond à 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 plate-forme et sont comptabilisés dans l'utilisation des journaux du projet.
    • Vous trouverez ces journaux dans l'explorateur de journaux avec le message "Event cannot deliver to Cloud function due to size exceeding the limit for 1st gen..." (l'événement ne peut pas être distribué à la fonction Cloud, car sa 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 date de moins d'une heure, 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 le code temporel.
    • 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 proprement dite à l'aide d'exclusions, mais notez que les événements mis en cause ne seront toujours pas distribués.

Emplacements Eventarc et Firestore en mode Datastore

Eventarc n'est pas compatible avec les zones multirégionales 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