Spanner 远程函数

借助 Spanner 远程函数,您可以使用 SQL 以外的语言来实现函数。函数必须托管在 Cloud Run functions Cloud Run 中。以这种方式托管函数可将复杂的业务逻辑拆分为单独的远程函数。

典型的远程函数部署包含以下步骤:

  1. 在 Cloud Run functions 或 Cloud Run 中创建 HTTPS 端点

    • 如果您是远程函数的新手,建议您使用 Cloud Run functions。
  2. 在 Spanner 中创建指向该 HTTPS 端点的远程函数

  3. 在查询中使用远程函数

所需的角色

为确保您的 Spanner 代理服务账号 (service-PROJECT_ID@gcp-sa-spanner.iam.gserviceaccount.com) 拥有使用 Spanner 远程函数所需的权限,请让您的管理员向您的 Spanner 代理服务账号 (service-PROJECT_ID@gcp-sa-spanner.iam.gserviceaccount.com) 授予项目的 Spanner API Service Agent (roles/spanner.serviceAgent) IAM 角色。

如需详细了解如何授予角色,请参阅管理对项目、文件夹和组织的访问权限

您的管理员或许还可以为您的 Spanner 代理服务账号 (service-PROJECT_ID@gcp-sa-spanner.iam.gserviceaccount.com) 授予 通过自定义角色或其他预定义角色来获取所需的权限。

受支持的类型

远程函数支持以下数据类型作为实参类型或返回值类型:

  • ARRAY(属于以下任何受支持的类型)
  • BOOLEAN
  • BYTES
  • DATE
  • JSON
  • INTEGER
  • NUMERIC
  • STRING
  • TIMESTAMP

限制

  • 您无法创建表值型远程函数。

  • 生成的列表达式不支持远程函数。

  • 由于暂时性网络错误或 Spanner 内部重启,您可能会看到向端点发送的请求重复出现,有时甚至在收到成功响应后也是如此。

  • 不支持在分数 Spanner 实例中运行远程函数。

  • 对于自定义网域后面的 Cloud Run functions,不支持远程函数。

  • PostgreSQL 方言不支持远程函数。

创建端点

业务逻辑必须实现为 Cloud Run 函数或 Cloud Run。该端点必须能够在单个 HTTPS POST 请求中处理一批行,并将该批处理的结果作为 HTTPS 响应返回。

如果您已为 BigQuery 创建远程函数,则可以将其重复用于 Spanner。

如需了解如何编写、部署、测试和维护 Cloud Run 函数,请参阅 Cloud Run functions 教程及其他 Cloud Run functions 文档

请参阅 Cloud Run 快速入门和其他 Cloud Run 文档,了解如何编写、部署、测试和维护 Cloud Run 服务。

建议您为 Cloud Run 函数或 Cloud Run 服务保留默认身份验证。避免将服务配置为允许未经身份验证的调用。

输入格式

Spanner 会发送包含 JSON 正文的 HTTPS POST 请求,格式如下:

字段名称 说明 字段类型
requestId 请求标识符。在 GoogleSQL 查询中,该 ID 在发送到此端点的多个请求中必须唯一。 始终提供。字符串。
calls 一批输入数据。 始终提供。JSON 数组。

每个元素都是一个 JSON 数组,表示单个远程函数调用的 JSON 编码实参列表。

请求的示例:

// Sample request to a Cloud Run functions to calculate sum of two numbers. This request
// has two calls batched together into a single request.
{
 "requestId": "124ab1c",
 "calls": [
  [1, 2],
  [3, 4]
 ]
}

输出格式

Spanner 要求端点按以下格式返回 HTTPS 响应;否则,Spanner 将无法使用该响应,并且调用远程函数的查询将会失败。

字段名称 说明 值范围
replies 一批返回值。 JSON 数组。

每个元素对应于外部函数的 JSON 编码返回值。

该数组的大小必须与 HTTPS 请求中 calls 的 JSON 数组的大小相匹配。例如,如果 calls 中的 JSON 数组有 4 个元素,则此 JSON 数组也需要包含 4 个元素。如需成功响应,此字段是必需的。
errorMessage 返回除 200 之外的 HTTPS 响应代码时显示的错误消息。对于不可重试的错误,Spanner 会向用户返回此错误消息。在失败的响应中是必需的。通常小于 1 KB。 字符串。

成功响应的示例:

// Sample response from the Cloud Run functions which has the sum of the two numbers. Note
// that the order of the values within `replies` field matches the `calls` field from
// the request.
{
  "replies": [
    3,  // 1 + 2 = 3
    7   // 3 + 4 = 7
  ]
}

失败响应的示例:

{
  // The error message returned by your Cloud Run functions to indicate an error.
  // In this sample, the error message states that an overflow occurred when summing two numbers.
  "errorMessage": "Overflow detected when calculating sum of two numbers."
}

HTTPS 响应代码

对于成功的响应,您的端点会返回 200 HTTPS 代码。当 Spanner 收到任何其他值时,会将响应视为失败,并在 HTTPS 响应代码为 408、429、500、503 或 504 时重试,直到达到内部限制为止。

示例代码

Cloud Run 函数

以下 Python 代码示例会添加远程函数的所有整数实参。它使用批量调用的参数来处理请求,并在响应中返回所有结果。

"""
Python script which uses Flask framework to spin up a HTTP server to take
integers and return their sum. In case of overflow, it returns the error
as part of the response.
"""
import functions_framework

from flask import jsonify

# Max INT64 value encoded as a number in JSON by TO_JSON_STRING. Larger values are encoded as
# strings.
_MAX_LOSSLESS=9007199254740992

@functions_framework.http
def batch_add(request):
  try:
    return_value = []
    request_json = request.get_json()
    calls = request_json['calls']
    for call in calls:
      return_value.append(sum([int(x) if isinstance(x, str) else x for x in call if x is not None]))
    replies = [str(x) if x > _MAX_LOSSLESS or x < -_MAX_LOSSLESS else x for x in return_value]
    return_json = jsonify( { "replies":  replies } )
    return return_json
  except Exception as e:
    return jsonify( { "errorMessage": str(e) } ), 400

假设函数在位于 us-east1 区域的 PROJECT_ID 项目中以函数名称 remote_add 进行部署,则可以使用端点 https://us-east1-PROJECT_ID.cloudfunctions.net/remote_add 访问该函数。

Cloud Run

以下 Python 代码示例实现了一项 Web 服务,您可以构建该服务并部署到 Cloud Run 以实现相同的功能。

"""
Python script which uses Flask framework to spin up a HTTP server to take
integers and return their sum. In case of overflow, it returns the error
as part of the response.
"""
import os

from flask import Flask, request, jsonify

# Max INT64 value encoded as a number in JSON by TO_JSON_STRING. Larger values are encoded as
# strings.
_MAX_LOSSLESS=9007199254740992

app = Flask(__name__)

@app.route("/", methods=['POST'])
def batch_add():
  try:
    return_value = []
    request_json = request.get_json()
    calls = request_json['calls']
    for call in calls:
      return_value.append(sum([int(x) if isinstance(x, str) else x for x in call if x is not None]))
    replies = [str(x) if x > _MAX_LOSSLESS or x < -_MAX_LOSSLESS else x for x in return_value]
    return jsonify( { "replies" :  replies } )
  except Exception as e:
    return jsonify( { "errorMessage": str(e) } ), 400

if __name__ == "__main__":
    app.run(debug=True, host="0.0.0.0", port=int(os.environ.get("PORT", 8080)))

如需构建和部署代码,请参阅快速入门:构建 Python (Flask) Web 应用并将其部署到 Cloud Run

假设 Cloud Run 服务已部署在项目 PROJECT_ID 和区域 us-east1 中,服务名称为 remote_add,可以通过端点 https://remote_add-<project_id_hash>-ue.a.run.app 来访问它。

创建远程函数

如需创建远程函数,请执行以下操作:

SQL

在 Spanner 中运行以下 CREATE FUNCTION 语句:

CREATE FUNCTION REMOTE_FUNCTION_NAME(x INT64, y INT64) RETURNS INT64 NOT DETERMINISTIC LANGUAGE REMOTE OPTIONS (
  endpoint = `ENDPOINT_URL`,
  max_batching_rows = MAX_BATCHING_ROWS
);

替换以下内容:

  • REMOTE_FUNCTION_NAME:远程函数的名称。例如 sum_func
  • ENDPOINT_URL:在上一步中创建的 Cloud Run functions 或 Cloud Run 端点。
  • MAX_BATCHING_ROWS(可选):作为请求的一部分发送的最大行数。如果未指定,Spanner 会自动确定批次大小。

在查询中使用远程函数

如需在查询中调用上一步中的远程函数,请使用以下示例:

SELECT REMOTE_FUNCTION_NAME(1, 2); -- 1 + 2 = 3

价格

  • 您需要按标准 Spanner 价格付费。

  • Spanner 会针对发送到 Cloud Run functions 或 Cloud Run 以及从 Cloud Run functions 或 Cloud Run 接收的字节数收取费用。

  • 此功能可能会产生 Cloud Run functions 和 Cloud Run 费用。如需了解详情,请查看 Cloud Run functions Cloud Run 价格页面。