使用 Cloud Functions (第 2 代) 扩展 Datastore

借助 Cloud Run functions 和 Eventarc,您可以部署代码来处理因 Datastore 模式 Firestore 数据库更改而触发的事件。这样您就可以添加服务器端功能,而无需运行自己的服务器。

Datastore 模式触发器

Eventarc 支持以下 Datastore 模式 Firestore 事件触发器,以便您创建与 Datastore 模式 Firestore 事件相关联的 Cloud Run Functions(第 2 代)处理脚本:

事件类型 触发器
google.cloud.datastore.entity.v1.created 首次写入实体时触发。
google.cloud.datastore.entity.v1.updated 当实体已存在并且其任何值发生了更改时触发。
google.cloud.datastore.entity.v1.deleted 在有实体被删除时触发。
google.cloud.datastore.entity.v1.written 在触发 createdupdateddeleted 时触发。
google.cloud.datastore.entity.v1.created.withAuthContext created 相同,但添加了身份验证信息。
google.cloud.datastore.entity.v1.updated.withAuthContext updated 相同,但添加了身份验证信息。
google.cloud.datastore.entity.v1.deleted.withAuthContext deleted 相同,但添加了身份验证信息。
google.cloud.datastore.entity.v1.written.withAuthContext written 相同,但添加了身份验证信息。

Datastore 模式事件触发器仅响应实体更改。数据未更改(无操作写入)的 Datastore 模式实体更新不会生成更新或写入事件。您无法仅为特定媒体资源生成事件。

在事件中添加身份验证上下文

如需包含与事件相关的其他身份验证信息,请将事件触发器与 withAuthContext 扩展程序搭配使用。此扩展程序会添加有关触发事件的主账号的其他信息。除了基本事件中返回的信息之外,它还会添加 authtypeauthid 属性。如需详细了解属性值,请参阅 authcontext 参考文档。

编写实体触发的函数

如需编写用于响应 Datastore 模式 Firestore 事件的函数,请准备在部署期间指定以下内容:

  • 触发事件类型
  • 触发器事件过滤条件,用于选择与函数关联的实体
  • 要运行的函数代码

触发器事件过滤条件

指定事件过滤条件时,您可以指定实体完全匹配或路径模式。使用路径模式,通过通配符 *** 匹配多个实体。

例如,您可以指定完全匹配的实体,以响应以下实体的更改:

users/marie

使用通配符 *** 响应与模式匹配的实体中的更改。* 通配符与单个分段匹配,** 多段通配符与模式中的零个或多个分段匹配。

对于单段匹配 (*),您还可以使用命名捕获组,例如 users/{userId}

下表展示了有效的路径模式:

模式 说明
users/*users/{userId} 匹配类型为 users 的所有实体。与子实体级别(如 /users/marie/messages/33e2IxYBD9enzS50SJ68)不匹配
users/** users 类型的所有实体以及 /users/marie/messages/33e2IxYBD9enzS50SJ68 等所有后代实体匹配

如需详细了解路径模式,请参阅 Eventarc 路径模式

即使您使用的是通配符,触发器也必须始终指向某个实体。请参见以下示例:

  • users/{userId=*}/{messages=*} 无效,因为 {messages=*} 是类型 ID。

  • users/{userId=*}/{messages}/{messageId=*}有效的,因为 {messageId=*} 始终指向某个实体。

字符转义

该部分介绍了需要对种类 ID 和实体 ID 中的字符进行转义的情况。对字符进行转义可让事件过滤条件正确解释 ID。

  • 如果类型 ID 或实体 ID 包含 ~/ 字符,您必须在事件过滤条件中对该 ID 进行转义。如需转义 ID,请使用 __escENCODED_ID__ 格式。将 ENCODED_ID 替换为将所有 ~/ 字符替换为其编码 ID 的类型 ID 或实体 ID,如下所示:

    • ~~0
    • /~1

    例如,类型 ID user/profile 会变为 __escusers~1profile__。具有此类型 ID 的示例路径模式为 __escusers~1profile__/{userId}

  • 如果您在事件过滤条件中使用 ... 的类型 ID 或实体 ID,则必须按如下方式对 ID 进行转义:

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

    只有当 ID 恰好为 ... 时,您才需要对 . 字符进行转义。例如,类型 ID customers.info 不需要转义。

  • 如果您的类型或实体 ID 是数字值(而非字符串值),您必须使用 __idNUMERIC_VALUE__ 对 ID 进行转义。例如,种类为 111 且实体 ID 为 222 的实体的路径模式为 __id111__/__id222__

  • 如果您从旧版 Cloud Datastore 迁移到了 Datastore 模式 Firestore,您的数据库可能包含采用非 UTF8 编码的旧版 ID。您必须使用 __bytesBASE64_ENCODING__ 转义这些 ID。 将 BASE64_ENCODING 替换为 ID 的 base-64 编码。例如,对于非 UTF8 类型 ID Task 的路径模式 Task/{task},转义后的路径模式为 __bytesVGFzaw==__/{task}

示例函数

以下示例演示了如何接收 Datastore 模式事件。如需处理事件涉及的数据,请查看 valueold_value 字段。

  • value:一个 EntityResult 对象,其中包含操作后实体快照。系统不会为删除事件填充此字段。
  • old_value:一个 EntityResult 对象,其中包含操作前实体快照。系统仅为更新和删除事件填充此字段。

Java

如需了解如何安装和使用 Datastore 模式的客户端库,请参阅 Datastore 模式客户端库。 如需了解详情,请参阅 Datastore 模式 Java API 参考文档

如需向 Datastore 模式进行身份验证,请设置应用默认凭据。 如需了解详情,请参阅为本地开发环境设置身份验证

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());
  }
}

在源代码中添加 proto 依赖项

您必须在函数的源目录中添加 Datastore 模式 data.proto 文件。此文件会导入以下 proto,您也必须将这些 proto 添加到源目录中:

为依赖项使用相同的目录结构。例如,将 struct.proto 放置在 google/protobuf 中。

这些文件是解码事件数据的必需文件。如果函数源代码不包含这些文件,则在运行时会返回错误。

事件属性

每个事件都包含数据属性,其中包含与事件相关的信息,例如事件触发的时间。Datastore 模式 Firestore 会添加与事件中涉及的数据库和实体相关的其他数据。您可以按如下方式访问这些属性:

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

部署函数

部署 Cloud Run 函数的用户必须具有 Cloud Run functions Developer IAM 角色或具有提供相同权限的其他角色。另请参阅其他部署配置

您可以使用 gcloud CLI 或 Google Cloud 控制台部署函数。以下示例演示了使用 gcloud CLI 进行部署。如需详细了解如何使用 Google Cloud 控制台进行部署,请参阅部署 Cloud Run functions 函数

  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. 使用 gcloud functions deploy 命令部署函数:

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

    第一个参数 FUNCTION_NAME 是已部署函数的名称。函数名称必须以字母开头,后面最多可跟 62 个字母、数字、连字符或下划线,但必须以字母或数字结尾。 将 FUNCTION_NAME 替换为有效的函数名称。然后,添加以下标志:

    • --gen2 标志用于指定要部署到 Cloud Run functions(第 2 代)。如果省略此标志,则会部署到 Cloud Run functions(第 1 代)。

    • --region=FUNCTION_LOCATION 标志指定要在其中部署函数的区域。

      如需最大限度地缩短距离,请将 FUNCTION_LOCATION 设置为靠近 Firestore 数据库的区域。如果您的 Firestore 数据库位于多区域位置,请将 nam5 中的数据库的值设置为 us-central1,将 eur3 中的数据库的值设置为 europe-west4。对于地区性 Firestore 位置,请设置为同一区域。

    • --trigger-location=TRIGGER_LOCATION 标志用于指定触发器的位置。您必须将 TRIGGER_LOCATION 设置为 Datastore 模式数据库的位置。

    • --runtime=RUNTIME 标志指定函数使用的语言运行时。Cloud Run functions 函数支持多种运行时。如需了解详情,请参阅运行时。将 RUNTIME 设置为受支持的运行时。

    • --source=SOURCE_LOCATION 标志用于指定函数源代码的位置。如需了解详情,请参阅以下内容:

      SOURCE_LOCATION 设置为函数源代码的位置。

    • --entry-point=CODE_ENTRYPOINT 标志指定源代码中函数的入口点。这是您的函数在运行时执行的代码。您必须将 CODE_ENTRYPOINT 设置为源代码中存在的函数名称或完全限定类名称。如需了解详情,请参阅函数入口点

    • --trigger-event-filters 标志用于定义事件过滤条件,其中包括触发器类型以及触发事件的实体或路径。设置以下属性值以定义事件过滤条件:

      • type=EVENT_FILTER_TYPE:Firestore 支持以下事件类型:

        • google.cloud.datastore.entity.v1.created:首次写入实体时发送事件。
        • google.cloud.datastore.entity.v1.updated:在实体已存在并且值已更改时发送事件。
        • google.cloud.datastore.entity.v1.deleted:在删除实体时发送事件。
        • google.cloud.datastore.entity.v1.written:在创建、更新或删除实体时发送事件。
        • google.cloud.datastore.entity.v1.created.withAuthContext:首次写入文档时发送事件,并且该事件包含额外的身份验证信息
        • google.cloud.datastore.entity.v1.updated.withAuthContext:在文档已存在并且值已更改时发送事件。包含额外的身份验证信息
        • google.cloud.datastore.entity.v1.deleted.withAuthContext:在删除文档时发送事件。包含额外的身份验证信息
        • google.cloud.datastore.entity.v1.written.withAuthContext:在创建、更新或删除文档时发送事件。包含额外的身份验证信息

        EVENT_FILTER_TYPE 设置为以下某种事件类型。

      • database=DATABASE:Firestore 数据库。对于默认数据库名称,请将 DATABASE 设置为 (default)

      • namespace=NAMESPACE:数据库命名空间。对于默认数据库名称,请将 NAMESPACE 设置为 (default)。移除该标志以匹配任何命名空间。

      • entity=ENTITY_OR_PATH:在有数据被创建、更新或删除时触发事件的数据库路径。ENTITY_OR_PATH 的接受值包括:

        • 等于;例如 --trigger-event-filters="entity='users/marie'"
        • 路径模式;例如 --trigger-event-filters-path-pattern="entity='users/*'"。如需了解详情,请参阅了解路径模式

      您可以视需要在部署函数时指定其他配置网络安全选项。

      如需查看有关部署命令及其标志的完整参考信息,请参阅 gcloud functions deploy 文档。

部署示例

以下示例演示了如何使用 Google Cloud CLI 进行部署。

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}'"

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}'"

限制

请注意适用于 Cloud Run functions 的 Firestore 触发器的以下限制:

  • Cloud Run functions (第 1 代) 前提条件是 Firestore 原生模式的现有“(默认)”数据库。它不支持 Firestore 命名数据库或 Datastore 模式。在这种情况下,请使用 Cloud Run functions (第 2 代) 来配置事件。
  • 无法保证顺序。快速更改可能会以意想不到的顺序触发函数调用。
  • 事件至少会被传送一次,但单个事件可能会导致多次调用函数。应该避免依赖“正好一次”机制,并编写幂等函数
  • Datastore 模式 Firestore 需要 Cloud Run functions(第 2 代)。Cloud Run functions(第 1 代)不支持 Datastore 模式。
  • 一个触发器与单一数据库相关联。您无法创建与多个数据库匹配的触发器。
  • 删除数据库不会自动删除该数据库的任何触发器。触发器会停止传送事件,但会继续存在,直到您删除触发器
  • 如果匹配的事件超过请求大小上限,该事件可能不会传送到 Cloud Run functions (第 1 代)。
    • 因请求大小而未传送的事件会记录在平台日志中,并计入项目的日志使用量。
    • 您可以在 Logs Explorer 中找到这些日志,其严重性为 error 且内容为“由于大小超出第 1 代的限制,因此事件无法传送到 Cloud Functions 函数”消息。您可以在 functionName 字段下方找到函数名称。如果 receiveTimestamp 字段仍在从现在起的一小时内,您可以利用该时间戳之前和之后的快照来读取相关文档,从而推断实际事件内容。
    • 为避免这种情况发生,您可以:
      • 迁移和升级到 Cloud Run functions (第 2 代)
      • 缩小文档
      • 删除相关的 Cloud Run functions 函数
    • 您可以使用排除功能关闭日志记录功能本身,但请注意,违规事件仍然不会传送。

Datastore 模式 Eventarc 和 Firestore 的位置

Eventarc 不支持 Firestore 事件触发器的多区域,但您仍然可以在多区域位置为 Firestore 数据库创建触发器。Eventarc 会将 Firestore 多区域位置映射到以下 Eventarc 区域:

Firestore 多区域 Eventarc 区域
nam5 us-central1
eur3 europe-west4

后续步骤