Funções remotas do Spanner

Uma função remota do Spanner permite implementar sua função em linguagens diferentes de SQL. As funções precisam ser hospedadas no Cloud Run functions ou no Cloud Run. A hospedagem de funções dessa maneira permite que a lógica de negócios complexa seja dividida em funções remotas separadas.

Uma implantação de função remota típica tem as seguintes etapas:

  1. Crie um endpoint HTTPS no Cloud Run functions ou Cloud Run.

    • Se você não conhece as funções remotas, recomendamos o uso do Cloud Run Functions.
  2. Crie uma função remota no Spanner apontando para esse endpoint HTTPS.

  3. Use a função remota em uma consulta.

Funções exigidas

Para garantir que a conta de serviço do agente do Spanner (service-PROJECT_ID@gcp-sa-spanner.iam.gserviceaccount.com) tenha as permissões necessárias para usar as funções remotas do Spanner, peça ao administrador para conceder o papel do IAM de agente de serviço da API Spanner (roles/spanner.serviceAgent) à conta de serviço do agente do Spanner (service-PROJECT_ID@gcp-sa-spanner.iam.gserviceaccount.com) no projeto.

Para mais informações sobre como conceder papéis, consulte Gerenciar o acesso a projetos, pastas e organizações.

O administrador também pode conceder à conta de serviço do agente do Spanner (service-PROJECT_ID@gcp-sa-spanner.iam.gserviceaccount.com) as permissões necessárias usando personalizados papéis ou outros predefinidos papéis.

Tipos compatíveis

As funções remotas oferecem suporte aos seguintes tipos de dados como argumentos ou tipos de retorno:

  • ARRAY (de qualquer um dos seguintes tipos compatíveis)
  • BOOLEAN
  • BYTES
  • DATE
  • JSON
  • INTEGER
  • NUMERIC
  • STRING
  • TIMESTAMP

Limitações

  • Não é possível criar funções remotas com valor de tabela.

  • As funções remotas estão indisponíveis em expressões de coluna geradas.

  • É possível ter solicitações repetidas com os mesmos dados para o endpoint, às vezes mesmo após respostas bem-sucedidas, devido a erros transitórios de rede ou reinicializações internas do Spanner.

  • Não é possível executar funções remotas em instâncias fracionárias do Spanner.

  • As funções remotas não são compatíveis com as funções do Cloud Run por trás de domínios personalizados.

  • As funções remotas não são compatíveis com o dialeto PostgreSQL.

Crie um endpoint

A lógica de negócios precisa ser implementada como uma função do Cloud Run ou do Cloud Run. O endpoint precisa processar um lote de linhas em uma única solicitação HTTPS POST e retornar o resultado do lote como uma resposta HTTPS.

Se você criou funções remotas para BigQuery, poderá reutilizá-las no Spanner.

Consulte o tutorial do Cloud Run functions e outras documentações do Cloud Run functions para saber como escrever, implantar, testar e manter uma função do Cloud Run.

Consulte o guia de início rápido do Cloud Run e outras documentações do Cloud Run sobre como escrever, implantar, testar e manter um serviço do Cloud Run.

Recomendamos manter a autenticação padrão para a função do Cloud Run ou o serviço do Cloud Run. Evite configurar o serviço para permitir a invocação não autenticada.

Formato da entrada

O Spanner envia solicitações HTTPS POST com corpos JSON no seguinte formato:

Nome do campo Descrição Tipo de campo
requestId Identificador da solicitação. Exclusivo em várias solicitações enviadas ao endpoint em a consulta do GoogleSQL. Sempre fornecido. String.
calls Um lote de dados de entrada. Sempre fornecido. Uma matriz JSON.

Cada elemento é uma matriz JSON que representa uma lista de argumentos codificada em JSON para uma única chamada de função remota.

Exemplo de solicitação:

// 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]
 ]
}

Formato da saída

O Spanner espera que o endpoint retorne uma resposta HTTPS no formato a seguir. Caso contrário, o Spanner não poderá consumir a resposta e a consulta falhará ao chamar a função remota.

Nome do campo Descrição Intervalo de valor
replies Um lote de valores de retorno. Matriz JSON.

Cada elemento corresponde a um valor de retorno codificado em JSON da função externa.

O tamanho da matriz precisa ser igual ao tamanho da matriz JSON de calls na solicitação HTTPS. Por exemplo, se a matriz JSON em calls tiver quatro elementos, esta matriz JSON precisará ter quatro elementos. Obrigatório para uma resposta bem-sucedida.
errorMessage Mensagem de erro quando o código de resposta HTTPS diferente de 200 é retornado. Para erros que não podem ser repetidos, o Spanner retorna essa mensagem de erro ao usuário. Obrigatório em respostas com falha. Normalmente, menos de 1 KB. String.

Exemplo de resposta bem-sucedida:

// 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
  ]
}

Exemplo de resposta com falha:

{
  // 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."
}

Código de resposta HTTPS

O endpoint retorna um código HTTPS 200 para uma resposta bem-sucedida. Quando o Spanner recebe qualquer outro valor, ele considera a resposta uma falha e tenta novamente quando o código de resposta HTTPS é 408, 429, 500, 503 ou 504 até que um limite interno seja atingido.

Exemplo de código

Função do Cloud Run

O exemplo de código Python a seguir implementa a adição de todos os argumentos inteiros da função remota. Ele processa uma solicitação com os argumentos para invocações em lote e retorna todos os resultados em uma resposta.

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

Supondo que a função seja implantada no projeto PROJECT_ID na região us-east1 como o nome da função remote_add, ela poderá ser acessada usando o endpoint https://us-east1-PROJECT_ID.cloudfunctions.net/remote_add.

Cloud Run

O exemplo de código Python a seguir implementa um serviço da Web, que pode ser criado e implantado no Cloud Run para a mesma funcionalidade.

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

Para criar e implantar o código, consulte Guia de início rápido: criar e implantar um app da Web em Python (Flask) no Cloud Run.

Supondo que o serviço do Cloud Run seja implantado no projeto PROJECT_ID na região us-east1 como o nome do serviço remote_add, ele poderá ser acessado usando o endpoint https://remote_add-<project_id_hash>-ue.a.run.app.

Criar uma função remota

Para criar uma função remota:

SQL

Execute a seguinte instrução CREATE FUNCTION no Spanner:

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

Substitua:

  • REMOTE_FUNCTION_NAME: o nome da função remota. Por exemplo, sum_func.
  • ENDPOINT_URL: o endpoint do Cloud Run functions ou do Cloud Run criado na etapa anterior.
  • MAX_BATCHING_ROWS (opcional): o número máximo de linhas a serem enviadas como parte de uma solicitação. Se não for especificado, o Spanner determinará o tamanho do lote automaticamente.

Usar a função remota em uma consulta

Para chamar a função remota da etapa anterior em uma consulta, use o exemplo a seguir:

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

Preços