使用 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=*} 是一个 kind 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 恰好为 ... 时,才需要对 . 字符进行转义。 例如,kind ID customers.info 不需要转义。

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

  • 如果您从旧版 Cloud Datastore 迁移到 Datastore 模式的 Firestore,则您的数据库可能包含采用非 UTF-8 编码的旧版 ID。您必须使用 __bytesBASE64_ENCODING__ 转义这些 ID。 将 BASE64_ENCODING 替换为相应 ID 的 base-64 编码。例如,具有非 UTF-8 类 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,您还必须将其包含在源目录中:

为依赖项使用相同的目录结构。例如,将 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 functions 的用户必须具有 Cloud Run functions Developer IAM 角色或具有提供相同权限的其他角色。另请参阅其他部署配置

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

  1. 在 Google Cloud 控制台中,激活 Cloud Shell。

    激活 Cloud Shell

    Cloud Shell 会话随即会在 Google Cloud 控制台的底部启动,并显示命令行提示符。Cloud Shell 是一个已安装 Google Cloud CLI 且已为当前项目设置值的 Shell 环境。该会话可能需要几秒钟时间来完成初始化。

  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 数据库位于多区域位置,请将该值设置为 us-central1(对于位于 nam5 中的数据库)和 europe-west4(对于位于 eur3 中的数据库)。对于区域级 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 代) 来配置事件。
  • 对 Cloud Run functions 和 Firestore 触发器进行跨项目设置存在限制。如需设置 Firestore 触发器,Cloud Run functions 必须位于同一项目中。
  • 无法保证顺序。快速更改可能会以意想不到的顺序触发函数调用。
  • 事件至少会被传送一次,但单个事件可能会导致多次调用函数。应该避免依赖“正好一次”机制,并编写幂等函数
  • 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 函数
    • 您可以使用排除功能关闭日志记录功能本身,但请注意,违规事件仍然不会传送。

Eventarc 和 Datastore 模式 Firestore 位置

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

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

后续步骤