Amplía Datastore con Cloud Functions (2nd gen)

Con Cloud Functions y Eventarc, puedes implementar código para controlar eventos activados por cambios en la base de datos de Firestore en modo Datastore. Esto te permite agregar funciones del servidor sin ejecutar tus propios servidores.

Activadores del modo Datastore

Eventarc admite los siguientes activadores de eventos de Firestore en modo Datastore para que puedas crear controladores de Cloud Functions (2nd gen) vinculados a los eventos de Firestore en modo Datastore:

Tipo de evento Activador
google.cloud.datastore.entity.v1.created Se activa cuando se escribe una entidad por primera vez.
google.cloud.datastore.entity.v1.updated Se activa cuando una entidad ya existe y se cambia un valor.
google.cloud.datastore.entity.v1.deleted Se activa cuando se borra una entidad.
google.cloud.datastore.entity.v1.written Se activa con created, updated o deleted.
google.cloud.datastore.entity.v1.created.withAuthContext Igual que created, pero agrega información de autenticación.
google.cloud.datastore.entity.v1.updated.withAuthContext Igual que updated, pero agrega información de autenticación.
google.cloud.datastore.entity.v1.deleted.withAuthContext Igual que deleted, pero agrega información de autenticación.
google.cloud.datastore.entity.v1.written.withAuthContext Igual que written, pero agrega información de autenticación.

Los activadores de eventos del modo Datastore solo responden a los cambios de entidad. La actualización de una entidad en modo Datastore en la que los datos no se modifican (una escritura no-op) no genera un evento de actualización ni de escritura. No puedes generar eventos solo para propiedades específicas.

Incluye el contexto de autenticación en el evento

Para incluir información de autenticación adicional sobre el evento, usa un activador de evento con la extensión withAuthContext. Esta extensión agrega información adicional sobre la principal que activó el evento. Agrega los atributos authtype y authid, además de la información que se muestra en el evento base. Consulta la referencia de authcontext para obtener más información sobre los valores de atributos.

Escribe una función activada por una entidad

Para escribir una función que responda a los eventos de Firestore en modo Datastore, prepárate para especificar lo siguiente durante la implementación:

  • un tipo de evento activador
  • un filtro de evento activador para seleccionar las entidades asociadas con la función
  • el código de la función para que se ejecute

Filtros del evento activador

Cuando especificas un filtro de evento, puedes especificar una coincidencia exacta de entidades o un patrón de ruta de acceso. Usa un patrón de ruta de acceso para hacer coincidir varias entidades con los comodines * o **.

Por ejemplo, puedes especificar una coincidencia de entidad exacta para responder a los cambios realizados en la siguiente entidad:

users/marie

Usa comodines, * o **, para responder a los cambios en entidades que coincidan con un patrón. El comodín * coincide con un solo segmento, y el comodín ** de varios segmentos coincide con cero o más segmentos en el patrón.

Para las coincidencias de un solo segmento (*), también puedes usar un grupo de captura con nombre, como users/{userId}.

En la siguiente tabla, se muestran los patrones de ruta de acceso válidos:

Patrón Descripción
users/* o users/{userId} Coincide con todas las entidades del tipo users. No coincide con el nivel de entidades subordinadas, como /users/marie/messages/33e2IxYBD9enzS50SJ68
users/** Coincide con todas las entidades del tipo users y todas las entidades subordinadas, como /users/marie/messages/33e2IxYBD9enzS50SJ68

Para obtener más información sobre los patrones de ruta, consulta Patrones de ruta de acceso de Eventarc.

Los activadores siempre deben apuntar a una entidad, incluso si usas un comodín. Consulta los ejemplos siguientes:

  • users/{userId=*}/{messages=*} no es válido porque {messages=*} es un ID de tipo.

  • users/{userId=*}/{messages}/{messageId=*} es válido porque {messageId=*} siempre apunta a una entidad.

Personajes fugados

En esta sección, se describen situaciones que requieren escapar caracteres en los IDs de categoría y los IDs de entidad. Esta acción permite que el filtro de eventos interprete el ID de forma correcta.

  • Si un ID de tipo o un ID de entidad incluye un carácter ~ o /, debes escapar el ID en el filtro de eventos. Para escapar un ID, usa el formato __escENCODED_ID__. Reemplaza ENCODED_ID por un ID de tipo o un ID de entidad que tenga todos los caracteres ~ y / reemplazados por sus IDs de codificación, que son los siguientes:

    • ~: ~0
    • /: ~1

    Por ejemplo, el ID de tipo user/profile se convierte en __escusers~1profile__. Un ejemplo de patrón de ruta de acceso con este ID de tipo es __escusers~1profile__/{userId}

  • Si usas el ID de tipo o el ID de entidad de . o .. en tu filtro de eventos, debes escapar el ID de la siguiente manera:

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

    Debes escapar el carácter . solo si el ID es exactamente . o ... Por ejemplo, el ID de tipo customers.info no requiere escape.

  • Si tu ID de tipo o entidad es un valor numérico en lugar de un valor de string, debes escapar el ID con __idNUMERIC_VALUE__. Por ejemplo, el patrón de ruta de acceso para una entidad del tipo 111 y el ID de entidad 222 es __id111__/__id222__.

  • Si migraste de Cloud Datastore heredado a Firestore en modo Datastore, tu base de datos puede contener ID heredados en una codificación que no sea UTF8. Debes escapar estos IDs con __bytesBASE64_ENCODING__. Reemplaza BASE64_ENCODING por la codificación en base64 del ID. Por ejemplo, el patrón de ruta de acceso Task/{task} con escape para el ID de tipo Task que no es UTF8 se convierte en __bytesVGFzaw==__/{task}.

Funciones de ejemplo

En el siguiente ejemplo, se muestra cómo recibir eventos del modo Datastore. Para trabajar con los datos involucrados en un evento, observa los campos value y old_value.

  • value: Es un objeto EntityResult que contiene una instantánea de la entidad posterior a la operación. Este campo no se propaga para borrar eventos.
  • old_value: Es un objeto EntityResult que contiene una instantánea de la entidad previa a la operación. Este campo solo se propaga para los eventos de actualización y eliminación.

Java

Para obtener información sobre cómo instalar y usar la biblioteca cliente del modo Datastore, consulta Bibliotecas cliente del modo Datastore. Si deseas obtener más información, consulta la documentación de referencia de la API Java del modo Datastore.

Para autenticarte en el modo Datastore, configura las credenciales predeterminadas de la aplicación. Si deseas obtener más información, consulta Configura la autenticación para un entorno de desarrollo 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());
  }
}

Incluye las dependencias de proto en tu fuente

Debes incluir el archivo data.proto del modo Datastore en el directorio del código fuente de tu función. Este archivo importa los siguientes .protos, que también debes incluir en tu directorio del código fuente:

Usa la misma estructura de directorio para las dependencias. Por ejemplo, coloca struct.proto dentro de google/protobuf.

Estos archivos son necesarios para decodificar los datos de eventos. Si la fuente de la función no incluye estos archivos, se mostrará un error cuando se ejecute.

Atributos del evento

Cada evento incluye atributos de datos que incluyen información sobre el evento, como la hora en que se activó. Firestore en modo Datastore agrega datos adicionales sobre la base de datos y la entidad involucrada en el evento. Puedes acceder a estos atributos de la siguiente manera:

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"));

Implementa una función

Los usuarios que implementan Cloud Functions deben tener el rol de IAM Cloud Functions Developer o una función que incluya los mismos permisos. Consulta también Configuración adicional para la implementación.

Puedes implementar una función con gcloud CLI o la consola de Google Cloud. En el siguiente ejemplo, se muestra la implementación con gcloud CLI. Para obtener detalles sobre la implementación con la consola de Google Cloud, consulta Implementa Cloud Functions.

  1. En la consola de Google Cloud, activa Cloud Shell.

    Activar Cloud Shell

    En la parte inferior de la consola de Google Cloud, se inicia una sesión de Cloud Shell en la que se muestra una ventana de línea de comandos. Cloud Shell es un entorno de shell con Google Cloud CLI ya instalada y con valores ya establecidos para el proyecto actual. La sesión puede tardar unos segundos en inicializarse.

  2. Usa el comando gcloud functions deploy para implementar una función:

    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" \
    

    El primer argumento, FUNCTION_NAME, es un nombre para tu función implementada. El nombre de la función debe comenzar con una letra seguida de un máximo de 62 letras, números, guiones o guiones bajos, y debe terminar con una letra o un número. Reemplaza FUNCTION_NAME por un nombre de función válido. Luego, agrega las siguientes marcas:

    • La marca --gen2 especifica que deseas implementar en Cloud Functions (2nd gen). Omitir esta marca da como resultado la implementación en Cloud Functions (1st gen).

    • La marca --region=FUNCTION_LOCATION especifica la región en la que se implementará la función.

      Para maximizar la proximidad, establece FUNCTION_LOCATION en una región cercana a la base de datos de Firestore. Si tu base de datos de Firestore está en una ubicación multirregional, configura el valor como us-central1 para las bases de datos en nam5 y como europe-west4 para las bases de datos en eur3. Para las ubicaciones regionales de Firestore, configúralas en la misma región.

    • La marca --trigger-location=TRIGGER_LOCATION especifica la ubicación del activador. Debes configurar TRIGGER_LOCATION en la ubicación de la base de datos en modo Datastore.

    • La marca --runtime=RUNTIME especifica el entorno de ejecución de lenguaje que usa tu función. Cloud Functions admite varios entornos de ejecución. Consulta Entornos de ejecución para obtener más información. Configura RUNTIME en un entorno de ejecución compatible.

    • La marca --source=SOURCE_LOCATION especifica la ubicación del código fuente de tu función. Consulta los siguientes artículos para obtener más detalles:

      Configura SOURCE_LOCATION en la ubicación del código fuente de la función.

    • La marca --entry-point=CODE_ENTRYPOINT especifica el punto de entrada a tu función en tu código fuente. Este es el código que ejecuta la función cuando se ejecuta. Debes establecer CODE_ENTRYPOINT en un nombre de función o de clase completamente calificado que exista en tu código fuente. Consulta Punto de entrada de la función para obtener más información.

    • Las marcas --trigger-event-filters definen el filtro de eventos que incluye el tipo de activador y la entidad o ruta que activa los eventos. Configura los siguientes valores de atributos para definir tu filtro de evento:

      • type=EVENT_FILTER_TYPE: Firestore admite los siguientes tipos de eventos:

        • google.cloud.datastore.entity.v1.created: El evento se envía cuando se escribe una entidad por primera vez.
        • google.cloud.datastore.entity.v1.updated: El evento se envía cuando una entidad ya existe y tiene algún valor cambiado.
        • google.cloud.datastore.entity.v1.deleted: El evento se envía cuando se borra una entidad.
        • google.cloud.datastore.entity.v1.written: El evento se envía cuando se crea, actualiza o borra una entidad.
        • google.cloud.datastore.entity.v1.created.withAuthContext: El evento se envía cuando se escribe en un documento por primera vez y el evento incluye información de autenticación adicional
        • google.cloud.datastore.entity.v1.updated.withAuthContext: El evento se envía cuando ya existe un documento y se cambia algún valor. Incluye información de autenticación adicional
        • google.cloud.datastore.entity.v1.deleted.withAuthContext: El evento se envía cuando se borra un documento. Incluye información de autenticación adicional
        • google.cloud.datastore.entity.v1.written.withAuthContext: El evento se envía cuando se crea, actualiza o borra un documento y un evento. Incluye información de autenticación adicional

        Establece EVENT_FILTER_TYPE en uno de estos tipos de eventos.

      • database=DATABASE: la base de datos de Firestore. Para el nombre predeterminado de la base de datos, establece DATABASE en (default).

      • namespace=NAMESPACE: Es el espacio de nombres de la base de datos. Para el nombre predeterminado de la base de datos, establece NAMESPACE en (default). Quita la marca para que coincida con cualquier espacio de nombres.

      • entity=ENTITY_OR_PATH: Es la ruta de la base de datos que activa eventos cuando se crean, actualizan o borran datos. Se aceptan los siguientes valores para ENTITY_OR_PATH:

        • Iguales; por ejemplo, --trigger-event-filters="entity='users/marie'"
        • Patrón de ruta de acceso; por ejemplo, --trigger-event-filters-path-pattern="entity='users/*'" Para obtener más información, consulta Información sobre los patrones de ruta de acceso.

      De manera opcional, puedes especificar opciones adicionales de configuración, herramientas de redes y seguridad cuando implementes una función.

      Para obtener una referencia completa del comando de implementación y sus marcas, consulta la documentación de gcloud functions deploy.

Implementaciones de ejemplo

En los siguientes ejemplos, se muestran implementaciones con Google Cloud CLI.

Implementa una función para una base de datos en la región 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}'"

Implementa una función para una base de datos en la multirregión 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}'"

Limitaciones

Ten en cuenta las siguientes limitaciones para los activadores de Firestore para Cloud Functions:

  • No se garantiza el ordenamiento. Los cambios rápidos pueden activar invocaciones de funciones en un orden inesperado.
  • Los eventos se entregan al menos una vez, pero un solo evento puede dar lugar a varias invocaciones de funciones. Evita depender de mecanismos solo una vez exactos y escribe funciones idempotentes.
  • Firestore en modo Datastore requiere Cloud Functions (2nd gen). Cloud Functions (1ª gen.) no es compatible con el modo Datastore.
  • Cloud Functions (1st gen) solo funciona con bases de datos “(predeterminadas)” y no es compatible con las bases de datos con nombre de Firestore. Usa Cloud Functions (2nd gen) para configurar eventos para las bases de datos con nombre.
  • Un activador se asocia con una sola base de datos. No puedes crear un activador que coincida con varias bases de datos.
  • Cuando se borra una base de datos, no se borra automáticamente ningún activador de la base de datos. El activador deja de entregar eventos, pero sigue existiendo hasta que lo borras.
  • Si un evento coincidente excede el tamaño máximo de la solicitud, es posible que el evento no se entregue a Cloud Functions (1st gen).
    • Los eventos que no se entregan debido al tamaño de la solicitud se registran en los registros de la plataforma y se tienen en cuenta para el uso de registros del proyecto.
    • Puedes encontrar estos registros en el Explorador de registros con el mensaje “El evento no se puede entregar a Cloud Function debido a que el tamaño supera el límite de 1ª gen... de gravedad error”. Puedes encontrar el nombre de la función en el campo functionName. Si el campo receiveTimestamp sigue dentro de una hora a partir de ahora, puedes inferir el contenido real del evento leyendo el documento en cuestión con una instantánea antes y después de la marca de tiempo.
    • Para evitar esa cadencia, puedes hacer lo siguiente:
      • Migra y actualiza a Cloud Functions (2nd gen)
      • Reducir el tamaño del documento
      • Borra la función de Cloud Functions en cuestión
    • Puedes desactivar el registro mediante las exclusiones, pero ten en cuenta que los eventos infractores aún no se entregarán.

Ubicaciones de Eventarc y Firestore en modo Datastore

Eventarc no admite multirregiones para activadores de eventos de Firestore, pero aún puedes crear activadores para las bases de datos de Firestore en ubicaciones multirregionales. Eventarc asigna ubicaciones multirregionales de Firestore a las siguientes regiones de Eventarc:

Firestore multirregional Región de Eventarc
nam5 us-central1
eur3 europe-west4

¿Qué sigue?