Funciones remotas de Spanner

Una función remota de Spanner te permite implementar tu función en lenguajes que no sean SQL. Las funciones deben alojarse en Cloud Run Functions o Cloud Run. Alojar funciones de esta manera permite dividir la lógica empresarial compleja en funciones remotas separadas.

Una implementación típica de una función remota tiene los siguientes pasos:

  1. Crea un extremo HTTPS en las funciones de Cloud Run o en Cloud Run.

    • Si es la primera vez que usas funciones remotas, te recomendamos que uses Cloud Run Functions.
  2. Crea una función remota en Spanner que apunte a ese extremo HTTPS.

  3. Usa la función remota en una consulta.

Roles obligatorios

Para asegurarte de que tu cuenta de servicio del agente de Spanner (service-PROJECT_ID@gcp-sa-spanner.iam.gserviceaccount.com) tenga los permisos necesarios para usar las funciones remotas de Spanner, pídele a tu administrador que le otorgue a tu cuenta de servicio del agente de Spanner (service-PROJECT_ID@gcp-sa-spanner.iam.gserviceaccount.com) el rol de IAM de Agente de servicio de la API de Spanner (roles/spanner.serviceAgent) en el proyecto.

Para obtener más información sobre cómo otorgar roles, consulta Administra el acceso a proyectos, carpetas y organizaciones.

Es posible que tu administrador también pueda otorgar permisos a tu cuenta de servicio del agente de Spanner (service-PROJECT_ID@gcp-sa-spanner.iam.gserviceaccount.com). los permisos necesarios a través de roles personalizados o cualquier otro rol predefinido.

Tipos compatibles

Las funciones remotas admiten los siguientes tipos de datos como argumentos o tipos de datos que se muestran:

  • ARRAY (de cualquiera de los siguientes tipos admitidos)
  • BOOLEAN
  • BYTES
  • DATE
  • JSON
  • INTEGER
  • NUMERIC
  • STRING
  • TIMESTAMP

Limitaciones

  • No puedes crear funciones remotas con valores de tabla.

  • No se admiten las funciones remotas en las expresiones de columnas generadas.

  • Es posible que veas solicitudes repetidas con los mismos datos a tu extremo, incluso después de obtener respuestas exitosas, debido a errores de red transitorios o reinicios internos de Spanner.

  • No se admite la ejecución de funciones remotas en instancias fraccionarias de Spanner.

  • Las funciones remotas no son compatibles con las funciones de Cloud Run detrás de dominios personalizados.

  • Las funciones remotas no son compatibles con el dialecto de PostgreSQL.

Crear un extremo

La lógica empresarial se debe implementar como una función de Cloud Run o Cloud Run. El extremo debe poder procesar un lote de filas en una sola solicitud POST HTTPS y mostrar el resultado del lote como una respuesta HTTPS.

Si creaste funciones remotas para BigQuery, puedes reutilizarlas para Spanner.

Consulta el instructivo de Cloud Run Functions y otra documentación de Cloud Run Functions sobre cómo escribir, implementar, probar y mantener una función de Cloud Run.

Consulta la guía de inicio rápido de Cloud Run y otra documentación de Cloud Run sobre cómo escribir, implementar, probar y mantener un servicio de Cloud Run.

Te recomendamos que mantengas la autenticación predeterminada para tu función o servicio de Cloud Run. Evita configurar el servicio para permitir invocaciones sin autenticación.

Formato de entrada

Spanner envía solicitudes POST HTTPS con cuerpos JSON en el siguiente formato:

Nombre del campo Descripción Tipo de campo
requestId Es el identificador de la solicitud. Es único cuando se envían varias solicitudes al extremo en una consulta de GoogleSQL. Siempre proporcionado. String.
calls Un lote de datos de entrada. Siempre proporcionado. Un array JSON.

Cada elemento es un array JSON que representa una lista de argumentos codificados en JSON para una sola llamada a función remota.

Ejemplo de una solicitud:

// 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 de salida

Spanner espera que el extremo muestre una respuesta HTTPS en el siguiente formato; de lo contrario, Spanner no podrá consumir la respuesta y fallará la consulta que llama a la función remota.

Nombre del campo Descripción Rango de valores
replies Es un lote de valores de retorno. Es un array JSON.

Cada elemento corresponde a un valor de retorno codificado en JSON de la función externa.

El tamaño del array debe coincidir con el tamaño del array JSON de calls en la solicitud HTTPS. Por ejemplo, si el array JSON en calls tiene 4 elementos, este array JSON también debe tener 4 elementos. Obligatorio para obtener una respuesta correcta.
errorMessage Mensaje de error cuando se muestra el código de respuesta HTTPS distinto de 200. En el caso de los errores que no se pueden reintentar, Spanner devuelve este mensaje de error al usuario. Se requiere en las respuestas con errores. Por lo general, menos de 1 KB String.

Ejemplo de una respuesta correcta:

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

Ejemplo de una respuesta con errores:

{
  // 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 respuesta HTTPS

Tu extremo devuelve un código HTTPS 200 para una respuesta correcta. Cuando Spanner recibe cualquier otro valor, considera la respuesta como una falla y vuelve a intentarlo cuando el código de respuesta HTTPS es 408, 429, 500, 503 o 504 hasta que se alcanza un límite interno.

Código de muestra

Función de Cloud Run

El siguiente código de muestra de Python implementa la adición de todos los argumentos de números enteros de la función remota. Controla una solicitud con los argumentos para invocaciones por lotes y muestra todos los resultados en una respuesta.

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

Si suponemos que la función se implementa en el proyecto PROJECT_ID en la región us-east1 como el nombre de la función remote_add, se puede acceder a ella con el extremo https://us-east1-PROJECT_ID.cloudfunctions.net/remote_add.

Cloud Run

En el siguiente código de muestra de Python, se implementa un servicio web, que se puede compilar e implementar en Cloud Run para la misma funcionalidad.

"""
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 compilar e implementar el código, consulta la Guía de inicio rápido: Compila y, luego, implementa una app web de Python (Flask) en Cloud Run.

Si suponemos que el servicio de Cloud Run se implementa en el proyecto PROJECT_ID en la región us-east1 como el nombre del servicio remote_add, se puede acceder a él con el extremo https://remote_add-<project_id_hash>-ue.a.run.app.

Crea una función remota

Para crear una función remota, sigue estos pasos:

SQL

Ejecuta la siguiente instrucción CREATE FUNCTION en 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
);

Reemplaza lo siguiente:

  • REMOTE_FUNCTION_NAME: Es el nombre de tu función remota. Por ejemplo, sum_func.
  • ENDPOINT_URL: Es el extremo de Cloud Run o Cloud Run Functions que se creó en el paso anterior.
  • MAX_BATCHING_ROWS (opcional): Es la cantidad máxima de filas que se enviarán como parte de una solicitud. Si no se especifica, Spanner determina el tamaño del lote automáticamente.

Usa una función remota en una consulta

Para llamar a la función remota del paso anterior dentro de una consulta, usa el siguiente ejemplo:

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

Precios

  • Se aplican los precios estándar de Spanner.

  • Spanner factura los bytes enviados a las funciones de Cloud Run o Cloud Run, y los bytes recibidos de estos.

  • Esta función puede generar costos de Cloud Run Functions y Cloud Run. Revisa las páginas de precios de Cloud Run Functions y Cloud Run para obtener más detalles.