Spanner リモート関数

Spanner リモート関数を使用すると、SQL 以外の言語で関数を実装できます。関数は、Cloud Run functions または Cloud Run でホストする必要があります。このように関数をホストすると、複雑なビジネス ロジックを個別のリモート関数に分割できます。

一般的なリモート関数のデプロイは、次の手順で行われます。

  1. Cloud Run functions または Cloud Run で HTTPS エンドポイントを作成します。

    • リモート関数を初めて使用する場合は、Cloud Run functions を使用することをおすすめします。
  2. その HTTPS エンドポイントを指す リモート関数を作成します。

  3. クエリでリモート関数を使用します。

必要なロール

Spanner エージェント サービス アカウント(service-PROJECT_ID@gcp-sa-spanner.iam.gserviceaccount.com)に Spanner リモート関数を使用するために必要な権限があることを確認するには、プロジェクトに対する Spanner API サービス エージェント roles/spanner.serviceAgent)IAM ロールを Spanner エージェント サービス アカウント(service-PROJECT_ID@gcp-sa-spanner.iam.gserviceaccount.com)に付与するよう管理者に依頼してください。

ロールの付与については、プロジェクト、フォルダ、組織へのアクセス権の管理をご覧ください。

管理者は、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 functions の作成、デプロイ、テスト、メンテナンスの方法については、Cloud Run functions のチュートリアルとその他の Cloud Run functions のドキュメントをご覧ください。

Cloud Run サービスの作成、デプロイ、テスト、メンテナンスの方法については、Cloud Run のクイックスタート Cloud Run のドキュメントをご覧ください。

Cloud Run 関数または Cloud Run サービスのデフォルトの認証を維持することをおすすめします。未認証の呼び出しを許可するようにサービスを構成することは避けてください。

入力形式

Spanner は、次の形式の JSON 本文を含む HTTPS POST リクエストを送信します。

フィールド名 説明 フィールド タイプ
requestId リクエスト ID。GoogleSQL クエリでエンドポイントに送信される複数のリクエスト全体で一意です。 常に提供されます。文字列。
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

関数が関数名 remote_add としてリージョン us-east1 のプロジェクト PROJECT_ID にデプロイされていると想定する場合、エンドポイント https://us-east1-PROJECT_ID.cloudfunctions.net/remote_add を使用してアクセスできます。

Cloud Run

次のサンプル Python コードは、同じ機能用に 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)ウェブアプリをビルドして Cloud Run にデプロイするをご覧ください。

Cloud Run サービスが、サービス名 remote_add としてリージョン us-east1 内のプロジェクト PROJECT_ID にデプロイされていることを前提とすると、そのサービスはエンドポイント 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 関数または 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 の料金ページをご覧ください。