Ampliar Datastore con Cloud Functions (2.ª gen.)

Con las funciones de Cloud Run y Eventarc, puedes desplegar código para gestionar eventos activados por cambios en tu base de datos de Firestore en el modo de Datastore. De esta forma, puedes añadir funciones del lado del servidor sin tener que 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 funciones de Cloud Run (2.ª gen.) vinculados a 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 ha modificado algún valor.
google.cloud.datastore.entity.v1.deleted Se activa cuando se elimina una entidad.
google.cloud.datastore.entity.v1.written Se activa cuando se activa created, updated o deleted.
google.cloud.datastore.entity.v1.created.withAuthContext Es igual que created, pero añade información de autenticación.
google.cloud.datastore.entity.v1.updated.withAuthContext Es igual que updated, pero añade información de autenticación.
google.cloud.datastore.entity.v1.deleted.withAuthContext Es igual que deleted, pero añade información de autenticación.
google.cloud.datastore.entity.v1.written.withAuthContext Es igual que written, pero añade información de autenticación.

Los activadores de eventos del modo Datastore solo responden a los cambios de entidades. Si se actualiza una entidad del modo Datastore y los datos no cambian (una escritura sin operación), no se genera ningún evento de actualización ni de escritura. No puedes generar eventos solo para propiedades específicas.

Incluir el contexto de autenticación en el evento

Para incluir información de autenticación adicional sobre el evento, usa un activador de eventos con la extensión withAuthContext. Esta extensión añade información adicional sobre la entidad de seguridad que ha activado el evento. Añade los atributos authtype y authid, además de la información devuelta en el evento base. Consulta la referencia de authcontext para obtener más información sobre los valores de los atributos.

Escribir una función activada por entidades

Para escribir una función que responda a eventos de Firestore en modo Datastore, debes especificar lo siguiente durante la implementación:

  • un tipo de evento de activación
  • Un filtro de eventos de activador para seleccionar las entidades asociadas a la función
  • El código de la función que se va a ejecutar

Filtros de eventos de activación

Cuando especifica un filtro de eventos, puede indicar una coincidencia exacta de una entidad o un patrón de ruta. Usa un patrón de ruta para buscar coincidencias con varias entidades mediante los comodines * o **.

Por ejemplo, puede especificar una concordancia exacta de entidad para responder a los cambios de la siguiente entidad:

users/marie

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

En el caso de 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 patrones de ruta válidos:

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

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

El activador siempre debe apuntar a una entidad, aunque uses un comodín. Consulta los siguientes ejemplos:

  • 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.

Escape de caracteres

En esta sección se describen las situaciones en las que debe usar caracteres de escape en los IDs de tipo y los IDs de entidad. Si se escapa un carácter, el filtro de eventos podrá interpretar correctamente el ID.

  • Si un ID de tipo o un ID de entidad incluye un carácter ~ o /, debe escapar el ID en el filtro de eventos. Para escapar un ID, usa el formato __escENCODED_ID__. Sustituye ENCODED_ID por un ID de tipo o de entidad en el que se hayan sustituido todos los caracteres ~ y / 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 con este ID de tipo es __escusers~1profile__/{userId}.

  • Si usa el ID de tipo o el ID de entidad de . o .. en su filtro de eventos, debe escapar el ID de la siguiente manera:

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

    Solo tienes que usar el carácter de escape . si el ID es exactamente . o ... Por ejemplo, el ID de tipo customers.info no requiere formato de escape.

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

  • Si has migrado de Legacy Cloud Datastore a Firestore en el modo de Datastore, es posible que tu base de datos contenga IDs antiguos con una codificación que no sea UTF-8. Debe usar el carácter de escape __bytesBASE64_ENCODING__ para estos IDs. Sustituye BASE64_ENCODING por la codificación en base 64 del ID. Por ejemplo, el patrón de ruta Task/{task} con escape para el ID de tipo no UTF-8 Task 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 implicados en un evento, consulta los campos value y old_value.

  • value: objeto EntityResult que contiene una vista general de la entidad posterior a la operación. Este campo no se rellena en los eventos de eliminación.
  • old_value: objeto EntityResult que contiene una entidad de preoperación. Este campo solo se rellena en los eventos de actualización y eliminación.

Java

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

Para autenticarte en el modo Datastore, configura las credenciales predeterminadas de la aplicación. Para obtener más información, consulta el artículo Configurar la autenticación en 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

Debe incluir el archivo Datastore mode data.proto en el directorio de origen de su función. Este archivo importa los siguientes protos, que también debes incluir en tu directorio de origen:

Usa la misma estructura de directorios 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, devuelve un error cuando se ejecuta.

Atributos de evento

Cada evento incluye atributos de datos que contienen información sobre el evento, como la hora en la que se ha activado. Firestore en modo Datastore añade datos adicionales sobre la base de datos y la entidad implicadas 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"));

Desplegar una función

Los usuarios que implementen funciones de Cloud Run deben tener el rol de gestión de identidades y accesos Desarrollador de funciones de Cloud Run o un rol que incluya los mismos permisos. Consulta también la sección Configuración adicional para la implementación.

Puedes desplegar una función con la CLI de gcloud o con la consola Google Cloud . En el ejemplo que aparece más abajo se muestra una implementación con la CLI de gcloud. Para obtener información sobre la implementación con la Google Cloud consola, consulta Implementar funciones de Cloud Run.

  1. In the Google Cloud console, activate Cloud Shell.

    Activate Cloud Shell

    At the bottom of the Google Cloud console, a Cloud Shell session starts and displays a command-line prompt. Cloud Shell is a shell environment with the Google Cloud CLI already installed and with values already set for your current project. It can take a few seconds for the session to initialize.

  2. Usa el comando gcloud functions deploy para desplegar 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 el nombre de la función desplegada. El nombre de la función debe empezar por 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. Sustituye FUNCTION_NAME por un nombre de función válido. A continuación, añade las siguientes marcas:

    • La marca --gen2 especifica que quieres desplegar en Cloud Run Functions (2.ª gen.). Si se omite esta marca, la implementación se realizará en Cloud Run Functions (1.ª gen.).

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

      Para maximizar la proximidad, asigna a FUNCTION_LOCATION una región cercana a tu base de datos de Firestore. Si tu base de datos de Firestore está en una ubicación multirregional, asigna el valor us-central1 a las bases de datos de nam5 y el valor europe-west4 a las bases de datos de eur3. En el caso de las ubicaciones regionales de Firestore, debe ser la misma región.

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

    • La marca --runtime=RUNTIME especifica qué tiempo de ejecución de lenguaje usa tu función. Cloud Run Functions admite varios tiempos de ejecución. Consulta Tiempos de ejecución para obtener más información. Asigna a RUNTIME un tiempo de ejecución compatible.

    • La marca --source=SOURCE_LOCATION especifica la ubicación del código fuente de tu función. Consulta la siguiente sección para obtener más información:

      Asigna a SOURCE_LOCATION la ubicación del código fuente de tu función.

    • La marca --entry-point=CODE_ENTRYPOINT especifica el punto de entrada de la función en el código fuente. Este es el código que ejecuta tu función cuando se pone en marcha. Debes asignar a CODE_ENTRYPOINT el nombre de una función o el nombre completo de una clase 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 la ruta que activa los eventos. Asigne los siguientes valores de atributo para definir su filtro de eventos:

      • 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 ya existe una entidad y se ha cambiado algún valor.
        • google.cloud.datastore.entity.v1.deleted: el evento se envía cuando se elimina una entidad.
        • google.cloud.datastore.entity.v1.written: el evento se envía cuando se crea, actualiza o elimina una entidad.
        • google.cloud.datastore.entity.v1.created.withAuthContext: el evento se envía cuando se escribe en un documento por primera vez e 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 ha cambiado algún valor. Incluye información de autenticación adicional
        • google.cloud.datastore.entity.v1.deleted.withAuthContext: event is sent when a document is deleted. Incluye información de autenticación adicional.
        • google.cloud.datastore.entity.v1.written.withAuthContext: el evento se envía cuando se crea, actualiza o elimina un documento y event. Incluye información de autenticación adicional

        Asigna EVENT_FILTER_TYPE a uno de estos tipos de eventos.

      • database=DATABASE: la base de datos de Firestore. En el nombre de la base de datos predeterminada, asigna el valor DATABASE a (default).

      • namespace=NAMESPACE: el espacio de nombres de la base de datos. En el nombre de la base de datos predeterminado, asigna el valor NAMESPACE a (default). Quita la marca para que coincida con cualquier espacio de nombres.

      • entity=ENTITY_OR_PATH: la ruta de la base de datos que activa eventos cuando se crean, actualizan o eliminan datos. Los valores aceptados para ENTITY_OR_PATH son:

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

      Cuando despliegues una función, puedes especificar opciones adicionales de configuración, redes y seguridad.

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

  3. Ejemplos de implementaciones

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

    Despliega 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 nam5 multirregión:

    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 de los activadores de Firestore para las funciones de Cloud Run:

    • Las funciones de Cloud Run (1.ª gen.) requieren una base de datos "(default)" en el modo nativo de Firestore. No admite bases de datos con nombre de Firestore ni el modo Datastore. En estos casos, utiliza funciones de Cloud Run (2.ª gen.) para configurar los eventos.
    • No se garantiza la realización del pedido. 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. No dependas de los mecanismos de entrega exactamente una vez y escribe funciones idempotentes.
    • Firestore en el modo de Datastore requiere funciones de Cloud Run (2.ª gen.). Cloud Run Functions (1.ª gen.) no admite el modo Datastore.
    • Un activador está asociado a una sola base de datos. No puedes crear un activador que coincida con varias bases de datos.
    • Si eliminas una base de datos, no se eliminarán automáticamente los activadores de esa base de datos. El activador deja de enviar eventos, pero sigue existiendo hasta que lo eliminas.
    • Si un evento coincidente supera el tamaño máximo de solicitud, es posible que no se envíe a las funciones de Cloud Run (1.ª 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 en el uso de registros del proyecto.
      • Puedes encontrar estos registros en el Explorador de registros con el mensaje "Event cannot deliver to Cloud function due to size exceeding the limit for 1st gen..." (No se puede enviar el evento a la función de Cloud porque el tamaño supera el límite de la primera generación...) de error gravedad. Puedes encontrar el nombre de la función en el campo functionName. Si el campo receiveTimestamp sigue estando a menos de una hora, puedes inferir el contenido del evento leyendo el documento en cuestión con una instantánea antes y después de la marca de tiempo.
      • Para evitar este tipo de cadencia, puedes hacer lo siguiente:
        • Migrar y actualizar a Cloud Run Functions (2.ª gen.)
        • Reducir el tamaño del documento
        • Elimina las funciones de Cloud Run en cuestión
      • Puedes desactivar el registro mediante exclusiones, pero ten en cuenta que los eventos infractores seguirán sin enviarse.

    Ubicaciones de Eventarc y Firestore en modo Datastore

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

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

    Siguientes pasos