Ampliar o Datastore com o Cloud Functions (2nd gen)

Com as funções do Cloud Run e o Eventarc, é possível implantar código para processar eventos acionados por mudanças no banco de dados do Firestore no modo Datastore. Isso permite adicionar funcionalidade do lado do servidor sem executar seus próprios servidores.

Gatilhos do modo Datastore

O Eventarc é compatível com os seguintes gatilhos de evento do Firestore no modo Datastore para que você possa criar manipuladores de funções do Cloud Run (2ª geração) vinculados a eventos do Firestore no modo Datastore:

Tipo de evento Gatilho
google.cloud.datastore.entity.v1.created Acionado quando uma entidade é gravada pela primeira vez.
google.cloud.datastore.entity.v1.updated Acionado quando uma entidade já existe e tem algum valor alterado.
google.cloud.datastore.entity.v1.deleted Acionado quando uma entidade é excluída.
google.cloud.datastore.entity.v1.written Acionado quando created, updated ou deleted é acionado.
google.cloud.datastore.entity.v1.created.withAuthContext Igual a created, mas adiciona informações de autenticação.
google.cloud.datastore.entity.v1.updated.withAuthContext Igual a updated, mas adiciona informações de autenticação.
google.cloud.datastore.entity.v1.deleted.withAuthContext Igual a deleted, mas adiciona informações de autenticação.
google.cloud.datastore.entity.v1.written.withAuthContext Igual a written, mas adiciona informações de autenticação.

Os gatilhos de eventos do modo Datastore respondem apenas a mudanças de entidade. Uma atualização em uma entidade do modo Datastore, em que os dados permanecem inalterados (uma gravação em ambiente autônomo), não gera um evento de atualização ou gravação. Não é possível gerar eventos apenas para propriedades específicas.

Incluir contexto de autenticação no evento

Para incluir mais informações de autenticação sobre o evento, use um gatilho de evento com a extensão withAuthContext. Essa extensão adiciona mais informações sobre o principal que acionou o evento. Ele adiciona os atributos authtype e authid às informações retornadas no evento base. Consulte a referência authcontext para mais informações sobre valores de atributos.

Escrever uma função acionada por entidade

Para gravar uma função que responda a eventos do Firestore no modo Datastore, prepare-se para especificar o seguinte durante a implantação:

  • um tipo de evento de acionador
  • um filtro de evento de acionamento para selecionar as entidades associadas à função
  • o código da função a ser executado

Filtros de eventos de acionamento

Ao especificar um filtro de evento, você pode especificar uma correspondência exata de entidade ou um padrão de caminho. Use um padrão de caminho para corresponder a várias entidades com os caracteres curinga * ou **.

Por exemplo, é possível especificar uma correspondência exata de entidade para responder a mudanças na seguinte entidade:

users/marie

Use caracteres curinga, * ou **, para responder a mudanças em entidades que correspondam a um padrão. O caractere curinga * corresponde a um único segmento, e o caractere curinga de vários segmentos ** corresponde a zero ou mais segmentos no padrão.

Para correspondências de segmento único (*), também é possível usar um grupo de captura nomeado, como users/{userId}.

A tabela a seguir mostra exemplos de padrões de caminho válidos:

Padrão Descrição
users/* ou users/{userId} Corresponde a todas as entidades do tipo users. Não corresponde a entidades descendentes, como /users/marie/messages/33e2IxYBD9enzS50SJ68
users/** Corresponde a todas as entidades do tipo users e a todas as entidades descendentes, como /users/marie/messages/33e2IxYBD9enzS50SJ68

Para saber mais sobre padrões de caminho, consulte Padrões de caminho do Eventarc.

É preciso que seu gatilho aponte sempre para uma entidade, mesmo que você esteja usando um caractere curinga. Veja os exemplos a seguir:

  • users/{userId=*}/{messages=*} não é válido porque {messages=*} é um ID de tipo.

  • users/{userId=*}/{messages}/{messageId=*} é válido porque {messageId=*} sempre aponta para uma entidade.

Escape de caracteres

A seção descreve situações em que é necessário usar caracteres de escape em IDs de tipo e de entidade. O escape de um caractere permite que o filtro de evento interprete o ID corretamente.

  • Se um ID de tipo ou de entidade incluir um caractere ~ ou /, faça o escape do ID no filtro de evento. Para escapar um ID, use o formato __escENCODED_ID__. Substitua ENCODED_ID por um ID de tipo ou de entidade que tenha todos os caracteres ~ e / substituídos pelos IDs de codificação, que são os seguintes:

    • ~: ~0
    • /: ~1

    Por exemplo, o ID do tipo user/profile se torna __escusers~1profile__. Um exemplo de padrão de caminho com esse ID de tipo é __escusers~1profile__/{userId}

  • Se você usar o ID do tipo ou da entidade de . ou .. no filtro de eventos, é necessário fazer o escape do ID da seguinte maneira:

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

    Você só precisa usar o escape do caractere . se o ID for exatamente . ou ... Por exemplo, o ID do tipo customers.info não precisa de escape.

  • Se o tipo ou ID da entidade for um valor numérico em vez de uma string, use o escape __idNUMERIC_VALUE__. Por exemplo, o padrão de caminho para uma entidade do tipo 111 e ID 222 é __id111__/__id222__.

  • Se você migrou do Cloud Datastore legado para o Firestore no modo Datastore, seu banco de dados pode conter IDs legados em uma codificação não UTF-8. É necessário usar o caractere de escape __bytesBASE64_ENCODING__ nesses IDs. Substitua BASE64_ENCODING pela codificação base-64 do ID. Por exemplo, o padrão de caminho Task/{task} com escape para ID de tipo não UTF8 Task se torna __bytesVGFzaw==__/{task}.

Exemplos de funções

O exemplo a seguir mostra como receber eventos do modo Datastore. Para trabalhar com os dados envolvidos em um evento, consulte os campos value e old_value.

  • value: um objeto EntityResult que contém um snapshot de entidade pós-operação. Esse campo não é preenchido para eventos de exclusão.
  • old_value: um objeto EntityResult que contém um instantâneo de entidade pré-operação. Esse campo é preenchido apenas para eventos de atualização e exclusão.

Java

Para saber como instalar e usar a biblioteca de cliente do modo Datastore, consulte Bibliotecas de cliente do modo Datastore. Para mais informações, consulte a documentação de referência da API do modo Datastore Java.

Para autenticar no modo Datastore, configure o Application Default Credentials. Para mais informações, consulte Configurar a autenticação para um ambiente de desenvolvimento 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());
  }
}

Inclua as dependências do proto na sua origem

Você precisa incluir o arquivo modo Datastore data.proto no diretório de origem da função. Esse arquivo importa os seguintes protos, que também precisam ser incluídos no diretório de origem:

Use a mesma estrutura de diretório para as dependências. Por exemplo, coloque struct.proto em google/protobuf.

Esses arquivos são necessários para decodificar dados de eventos. Se a origem da função não incluir esses arquivos, ela vai retornar um erro quando for executada.

Atributos do evento

Cada evento inclui atributos de dados com informações sobre o evento, como o horário em que ele foi acionado. O Firestore no modo Datastore adiciona mais dados sobre o banco de dados e a entidade envolvidos no evento. Você pode acessar esses atributos da seguinte forma:

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

Implantar uma função

Os usuários que implantam funções do Cloud Run precisam ter o papel do IAM de Desenvolvedor de funções do Cloud Run ou um papel que inclua as mesmas permissões. Consulte também Outras configurações para implantação.

É possível implantar uma função usando a CLI gcloud ou o console Google Cloud . O exemplo abaixo demonstra a implantação com a CLI gcloud. Para detalhes sobre a implantação com o console do Google Cloud , consulte Implantar funções do 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. Use o comando gcloud functions deploy para implantar uma função:

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

    O primeiro argumento, FUNCTION_NAME, é um nome para a função implantada. O nome da função deve começar com uma letra, seguida por até 62 letras, números, hífens e sublinhados, e terminar com uma letra ou um número Substitua FUNCTION_NAME por um nome de função válido. Em seguida, adicione as seguintes flags:

    • A flag --gen2 especifica que você quer implantar nas funções do Cloud Run (2ª geração). A omissão dessa flag resulta na implantação no Cloud Run functions (1ª geração).

    • A sinalização --region=FUNCTION_LOCATION especifica a região em que a função será implantada.

      Para maximizar a proximidade, defina FUNCTION_LOCATION como uma região perto do seu banco de dados do Firestore. Se o banco de dados do Firestore estiver em um local multirregional, defina o valor como us-central1 para bancos de dados emnam5 e como europe-west4 para bancos de dados em eur3. Para locais regionais do Firestore, defina como a mesma região.

    • A flag --trigger-location=TRIGGER_LOCATION especifica o local do acionador. Defina TRIGGER_LOCATION como o local do banco de dados do modo Datastore.

    • A sinalização --runtime=RUNTIME especifica o ambiente de execução da linguagem que a função usa. O Cloud Run functions aceita vários ambientes de execução. Consulte Ambientes de execução para mais informações. Defina RUNTIME como um ambiente de execução compatível.

    • A sinalização --source=SOURCE_LOCATION especifica o local do código-fonte da função. Consulte as seções a seguir para mais detalhes:

      Defina SOURCE_LOCATION como o local do código-fonte da função.

    • A sinalização --entry-point=CODE_ENTRYPOINT especifica o ponto de entrada da função no código-fonte. Este é o código que a função executa quando é executada. Você precisa definir CODE_ENTRYPOINT como um nome de função ou de classe totalmente qualificada no código-fonte. Consulte Ponto de entrada de função para mais informações.

    • As flags --trigger-event-filters definem o filtro de eventos, que inclui o tipo de acionador e a entidade ou o caminho que aciona os eventos. Defina os seguintes valores de atributo para definir seu filtro de evento:

      • type=EVENT_FILTER_TYPE: o Firestore aceita os seguintes tipos de eventos:

        • google.cloud.datastore.entity.v1.created: o evento é enviado quando uma entidade é gravada pela primeira vez.
        • google.cloud.datastore.entity.v1.updated: o evento é enviado quando uma entidade já existe e algum valor é alterado.
        • google.cloud.datastore.entity.v1.deleted: o evento é enviado quando uma entidade é excluída.
        • google.cloud.datastore.entity.v1.written: o evento é enviado quando uma entidade é criada, atualizada ou excluída.
        • google.cloud.datastore.entity.v1.created.withAuthContext: o evento é enviado quando um documento é gravado pela primeira vez e inclui informações de autenticação adicionais.
        • google.cloud.datastore.entity.v1.updated.withAuthContext: o evento é enviado quando um documento já existe e tem algum valor alterado. Inclui informações de autenticação adicionais
        • google.cloud.datastore.entity.v1.deleted.withAuthContext: o evento é enviado quando um documento é excluído. Inclui informações de autenticação adicionais
        • google.cloud.datastore.entity.v1.written.withAuthContext: o evento é enviado quando um documento é criado, atualizado ou excluído. Inclui informações de autenticação adicionais

        Defina EVENT_FILTER_TYPE como um destes tipos de evento.

      • database=DATABASE: o banco de dados do Firestore. Para o nome padrão do banco de dados, defina DATABASE como (default).

      • namespace=NAMESPACE: o namespace do banco de dados. Para o nome padrão do banco de dados, defina NAMESPACE como (default). Remova a flag para corresponder a qualquer namespace.

      • entity=ENTITY_OR_PATH: o caminho do banco de dados que aciona eventos quando os dados são criados, atualizados ou excluídos. Os valores aceitos para ENTITY_OR_PATH são:

        • Igual por exemplo, --trigger-event-filters="entity='users/marie'"
        • Padrão do caminho por exemplo, --trigger-event-filters-path-pattern="entity='users/*'". Para mais informações, consulte Entender os padrões de caminho.

      Também é possível especificar opções adicionais de configuração, rede e segurança ao implantar uma função.

      Para ver uma referência completa sobre o comando de implantação e as sinalizações dele, consulte a documentação gcloud functions deploy.

Exemplos de implantações

Os exemplos a seguir demonstram implantações com a Google Cloud CLI.

Implante uma função para um banco de dados na região 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}'"

Implante uma função para um banco de dados na multirregião 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}'"

Limitações

As seguintes limitações para gatilhos do Firestore do Cloud Run functions:

  • O pré-requisito do Cloud Run functions (1ª geração) é um banco de dados "(padrão)" no modo nativo do Firestore. Ele não é compatível com bancos de dados nomeados do Firestore ou com o modo Datastore. Use o Cloud Run functions (2ª geração) para configurar eventos nesses casos.
  • Não garantimos acionamentos em ordem. Alterações rápidas podem acionar invocações de função em uma ordem inesperada.
  • Os eventos são entregues pelo menos uma vez, mas um único evento pode resultar em invocações de várias funções. Evite depender de mecanismos do tipo "apenas uma vez" e escreva funções idempotentes.
  • O Firestore no modo Datastore requer o Cloud Run functions (2ª geração). O Cloud Run functions (1ª geração) não é compatível com o modo Datastore.
  • Um gatilho está associado a um único banco de dados. Não é possível criar um gatilho que corresponda a vários bancos de dados.
  • A exclusão de um banco de dados não remove automaticamente nenhum gatilho dele. O gatilho deixa de entregar eventos, mas continua existindo até que você o exclua.
  • Se um evento correspondente exceder o tamanho máximo da solicitação, ele pode não ser entregue ao Cloud Run functions (1ª geração).
    • Os eventos não entregues devido ao tamanho da solicitação são registrados nos registros da plataforma e contabilizados no uso de registros do projeto.
    • É possível encontrar esses registros na Análise de registros com a mensagem "O evento não pode ser entregue à função do Cloud devido ao tamanho excedido em relação ao limite para a 1ª geração..." da gravidade de error. Encontre o nome da função no campo functionName. Se o campo receiveTimestamp ainda estiver dentro de uma hora, será possível inferir o conteúdo real do evento lendo o documento em questão com um snapshot antes e depois do carimbo de data/hora.
    • Para evitar isso, faça o seguinte:
      • Migre e faça upgrade para o Cloud Run functions (2ª geração)
      • Reduza o tamanho do documento
      • Exclua o Cloud Run functions em questão
    • É possível desativar a geração de registros usando exclusões, mas os eventos ofensivos ainda não serão entregues.

Locais do Eventarc e do Firestore no modo Datastore

O Eventarc não é compatível com multirregiões para gatilhos de eventos do Firestore, mas ainda é possível criar gatilhos para bancos de dados do Firestore em locais multirregionais. O Eventarc mapeia os locais multirregionais do Firestore para as seguintes regiões do Eventarc:

Multirregião do Firestore Região do Eventarc
nam5 us-central1
eur3 europe-west4

A seguir