Trabaja con funciones remotas
Una función remota de BigQuery te permite implementar tu función en otros lenguajes que no sean SQL y JavaScript, o con bibliotecas o servicios no permitidos en las funciones definidas por el usuario de BigQuery.
Descripción general
Una función remota de BigQuery te permite incorporar la funcionalidad de Google SQL con software fuera de BigQuery; para ello, proporciona una integración directa con Cloud Functions y Cloud Run. Con las funciones remotas de BigQuery, puedes implementar las funciones en Cloud Functions o Cloud Run implementadas con cualquier lenguaje compatible y, luego, invocarlas desde las consultas de Google SQL.
Flujo de trabajo
- Crea el extremo HTTP en Cloud Functions o Cloud Run.
- Crea una función remota en BigQuery.
- Crea una conexión del tipo
CLOUD_RESOURCE
. - Crea una función remota.
- Crea una conexión del tipo
- Usa la función remota en una consulta como cualquier otra función definida por el usuario.
Limitaciones
Las funciones remotas solo admiten uno de los siguientes tipos de datos como tipo de argumento o tipo de datos que se muestra:
- Booleano
- Bytes
- Numérica
- String
- Fecha
- Fecha y hora
- Tiempo
- Marca de tiempo
- JSON
Las funciones remotas no son compatibles con los tipos
ARRAY
,STRUCT
,INTERVAL
niGEOGRAPHY
.No puedes crear funciones remotas temporales.
No puedes crear funciones remotas con valores de tabla.
No puedes usar funciones remotas cuando creas vistas materializadas.
Siempre se supone que el valor de retorno de una función remota no es determinista, por lo que el resultado de una consulta que llama a una función remota no se almacena en caché.
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 errores internos de BigQuery.
Cuando se omite una evaluación de función remota para algunas filas debido a un cortocircuito, por ejemplo, en expresiones condicionales o en una declaración
MERGE
conWHEN [NOT] MATCHED
, el procesamiento por lotes no se usa con la función remota. En este caso, el campocalls
en el cuerpo de la solicitud HTTP tiene exactamente un elemento.Si el conjunto de datos asociado con la función remota se replica en una región de destino mediante la replicación de conjunto de datos entre regiones, la función remota solo se puede consultar en la región en la que se creó.
Crear un extremo
Para crear una función remota que pueda implementar la lógica empresarial, debes crear un extremo HTTP mediante Cloud Functions o Cloud Run. El extremo debe poder procesar un lote de filas en una sola solicitud POST HTTP y mostrar los resultados para el lote como una respuesta HTTP.
Si creas la función remota a través de BigQuery Dataframes, no es necesario que crees el extremo HTTP de forma manual; el servicio lo hace automáticamente.
Consulta el instructivo de Cloud Functions y otra documentación de Cloud Functions sobre cómo escribir, implementar, probar y mantener una Cloud Function.
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.
Se recomienda mantener la autenticación predeterminada en lugar de permitir la invocación no autenticada de tu servicio de Cloud Functions o Cloud Run.
Formato de entrada
BigQuery envía solicitudes POST HTTP con cuerpo JSON en el siguiente formato:
Nombre del campo | Descripción | Tipo de campo |
---|---|---|
requestId | Es el ID de la solicitud. Es único cuando se envían varias solicitudes a este extremo en una consulta de Google SQL. | Siempre proporcionado. String. |
emisor | Es el nombre completo del recurso del trabajo para la consulta en GoogleSQL que llama a la función remota. | Siempre proporcionado. String. |
sessionUser | Es el correo electrónico del usuario que ejecuta la consulta de GoogleSQL. | Siempre proporcionado. String. |
userDefinedContext | Es el contexto definido por el usuario que se usó cuando se creó la función remota en BigQuery. | Opcional. Un objeto JSON con pares clave-valor. |
llamadas | Un lote de datos de entrada. | Siempre proporcionado. Un array JSON.
Cada elemento en sí es un array JSON, que es una lista de argumentos codificados en JSON de una llamada a función remota. |
Ejemplo de una solicitud:
{
"requestId": "124ab1c",
"caller": "//bigquery.googleapis.com/projects/myproject/jobs/myproject:US.bquxjob_5b4c112c_17961fafeaf",
"sessionUser": "[email protected]",
"userDefinedContext": {
"key1": "value1",
"key2": "v2"
},
"calls": [
[null, 1, "", "abc"],
["abc", "9007199254740993", null, null]
]
}
Formato de salida
BigQuery espera que el extremo muestre una respuesta HTTP en el siguiente formato; de lo contrario, BigQuery no podrá consumirlo 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. | Obligatorio para obtener una respuesta correcta. 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 |
errorMessage | Mensaje de error cuando se muestra el código de respuesta HTTP distinto de 200. En el caso de los errores que no se pueden reintentar, se muestra como parte del mensaje de error del trabajo de BigQuery al usuario. | Opcional. String. El tamaño debe ser inferior a 1 KB. |
Ejemplo de una respuesta correcta:
{
"replies": [
1,
0
]
}
Ejemplo de una respuesta con errores:
{
"errorMessage": "Received but not expected that the argument 0 be null".
}
Código de respuesta HTTP
El extremo debe mostrar el código de respuesta HTTP 200 para que sea una respuesta correcta. Cuando BigQuery recibe cualquier otro valor, considera la respuesta como una falla y vuelve a intentarlo cuando el código de respuesta HTTP es 408, 429, 500, 503 o 504 hasta algún límite interno.
Codificación JSON de tipo de datos SQL
La codificación JSON en la solicitud o respuesta HTTP sigue la codificación JSON de BigQuery existente para la función TO_JSON_STRING.
Código de Cloud Function de muestra
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.
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.
# See https://cloud.google.com/bigquery/docs/reference/standard-sql/json_functions#json_encodings
_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 my_gcf_project
en la región us-east1
como el nombre de la función remote_add
, se puede acceder a ella mediante el extremo https://us-east1-my_gcf_project.cloudfunctions.net/remote_add
.
Código de Cloud Run de muestra
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.
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.
# See https://cloud.google.com/bigquery/docs/reference/standard-sql/json_functions#json_encodings
_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)))
Consulta la guía sobre cómo compilar y, luego, implementar el código.
Si suponemos que el servicio de Cloud Run se implementa en el proyecto my_gcf_project
en la región us-east1
como el nombre del servicio remote_add
, se puede acceder a él mediante el extremo https://remote_add-<project_id_hash>-ue.a.run.app
.
Crea una función remota
BigQuery usa una conexión CLOUD_RESOURCE
para interactuar con tu Cloud Function. Para crear una función remota, debes
crear una conexión CLOUD_RESOURCE
. Si creas la función remota a través de
BigQuery Dataframes
y se te otorgó el
rol de administrador de IAM del proyecto (roles/resourcemanager.projectIamAdmin
), entonces
no tienes que crear la conexión y otorgarle acceso de forma manual; el servicio
lo hace automáticamente.
Crear una conexión
Debes tener una conexión de recursos de Cloud para conectarte a Cloud Functions y Cloud Run.
Selecciona una de las opciones siguientes:
Console
Ve a la página de BigQuery.
Para crear una conexión, haz clic en
Agregar y, luego, en Conexiones a fuentes de datos externas.En la lista Tipo de conexión, selecciona Modelos remotos de Vertex AI, funciones remotas y BigLake (Cloud Resource).
En el campo ID de conexión, escribe un nombre para tu conexión.
Haga clic en Crear conexión.
Haz clic en Ir a la conexión.
En el panel Información de conexión, copia el ID de la cuenta de servicio para usarlo en un paso posterior.
bq
En un entorno de línea de comandos, crea una conexión:
bq mk --connection --location=REGION --project_id=PROJECT_ID \ --connection_type=CLOUD_RESOURCE CONNECTION_ID
El parámetro
--project_id
anula el proyecto predeterminado.Reemplaza lo siguiente:
REGION
: tu región de conexiónPROJECT_ID
: El ID del proyecto de Google Cloud.CONNECTION_ID
: Es un ID para tu conexión.
Cuando creas un recurso de conexión, BigQuery crea una cuenta de servicio del sistema única y la asocia con la conexión.
Solución de problemas: Si recibes el siguiente error de conexión, actualiza el SDK de Google Cloud:
Flags parsing error: flag --connection_type=CLOUD_RESOURCE: value should be one of...
Recupera y copia el ID de cuenta de servicio para usarlo en un paso posterior:
bq show --connection PROJECT_ID.REGION.CONNECTION_ID
El resultado es similar al siguiente:
name properties 1234.REGION.CONNECTION_ID {"serviceAccountId": "connection-1234-9u56h9@gcp-sa-bigquery-condel.iam.gserviceaccount.com"}
Terraform
Agrega la siguiente sección a tu archivo main.tf
.
## This creates a cloud resource connection. ## Note: The cloud resource nested object has only one output only field - serviceAccountId. resource "google_bigquery_connection" "connection" { connection_id = "CONNECTION_ID" project = "PROJECT_ID" location = "REGION" cloud_resource {} }Reemplaza lo siguiente:
CONNECTION_ID
: Es un ID para tu conexión.PROJECT_ID
: El ID del proyecto de Google Cloud.REGION
: tu región de conexión
Configura el acceso
Debes otorgar a la conexión nueva acceso de solo lectura a tu servicio de Cloud Functions o Cloud Run. No se recomienda permitir la invocación no autenticada para el servicio de Cloud Functions o Cloud Run.
Para otorgar roles, sigue estos pasos:
Ir a la página IAM y administración
Haga clic en
Agregar.Se abre el cuadro de diálogo Agregar principales.
En el campo Principales nuevas (New principals), ingresa el ID de la cuenta de servicio que copiaste antes.
En el campo Seleccionar un rol, selecciona una de las siguientes opciones:
- Si usas una Cloud Function de 1ª gen., elige Cloud Function y, luego, selecciona Rol de invocador de Cloud Functions.
- Si usas una Cloud Function de 2ª gen., elige Cloud Run y, luego, selecciona Rol de invocador de Cloud Run.
- Si usas un servicio de Cloud Run, elige Cloud Run y, luego, selecciona Rol de invocador de Cloud Run.
Haz clic en Guardar.
Crea una función remota
Para crear una función remota, sigue estos pasos:
SQL
Ejecuta la siguiente declaración CREATE FUNCTION
en BigQuery:
En la consola de Google Cloud, ve a la página de BigQuery.
En el editor de consultas, escribe la siguiente oración:
CREATE FUNCTION
PROJECT_ID.DATASET_ID
.remote_add(x INT64, y INT64) RETURNS INT64 REMOTE WITH CONNECTIONPROJECT_ID.LOCATION.CONNECTION_NAME
OPTIONS ( endpoint = 'ENDPOINT_URL' )Reemplaza lo siguiente:
DATASET_ID
: el ID del conjunto de datos de BigQuery.ENDPOINT_URL
: la URL de tu extremo de función remota de Cloud Function o Cloud Run.
Haz clic en
Ejecutar.
Para obtener más información sobre cómo ejecutar consultas, visita Ejecuta una consulta interactiva.
BigQuery DataFrames
BigQuery Dataframes está en vista previa.
- Habilita las APIs necesarias y asegúrate de que se te hayan otorgado los roles necesarios, como se describe en la sección Requisitos de Funciones remotas.
Usa el decorador
remote_function
.import bigframes.pandas as bpd # Set BigQuery DataFrames options bpd.options.bigquery.project = your_gcp_project_id bpd.options.bigquery.location = "us" # BigQuery DataFrames gives you the ability to turn your custom scalar # functions into a BigQuery remote function. It requires the GCP project to # be set up appropriately and the user having sufficient privileges to use # them. One can find more details about the usage and the requirements via # `help` command. help(bpd.remote_function) # Read a table and inspect the column of interest. df = bpd.read_gbq("bigquery-public-data.ml_datasets.penguins") df["body_mass_g"].head(10) # Define a custom function, and specify the intent to turn it into a remote # function. It requires a BigQuery connection. If the connection is not # already created, BigQuery DataFrames will attempt to create one assuming # the necessary APIs and IAM permissions are setup in the project. In our # examples we would be using a pre-created connection named # `bigframes-rf-conn`. We will also set `reuse=False` to make sure we don't # step over someone else creating remote function in the same project from # the exact same source code at the same time. Let's try a `pandas`-like use # case in which we want to apply a user defined scalar function to every # value in a `Series`, more specifically bucketize the `body_mass_g` value # of the penguins, which is a real number, into a category, which is a # string. @bpd.remote_function( [float], str, bigquery_connection="bigframes-rf-conn", reuse=False, ) def get_bucket(num): if not num: return "NA" boundary = 4000 return "at_or_above_4000" if num >= boundary else "below_4000" # Then we can apply the remote function on the `Series`` of interest via # `apply` API and store the result in a new column in the DataFrame. df = df.assign(body_mass_bucket=df["body_mass_g"].apply(get_bucket)) # This will add a new column `body_mass_bucket` in the DataFrame. You can # preview the original value and the bucketized value side by side. df[["body_mass_g", "body_mass_bucket"]].head(10) # The above operation was possible by doing all the computation on the # cloud. For that, there is a google cloud function deployed by serializing # the user code, and a BigQuery remote function created to call the cloud # function via the latter's http endpoint on the data in the DataFrame. # The BigQuery remote function created to support the BigQuery DataFrames # remote function can be located via a property `bigframes_remote_function` # set in the remote function object. print(f"Created BQ remote function: {get_bucket.bigframes_remote_function}") # The cloud function can be located via another property # `bigframes_cloud_function` set in the remote function object. print(f"Created cloud function: {get_bucket.bigframes_cloud_function}") # Warning: The deployed cloud function may be visible to other users with # sufficient privilege in the project, so the user should be careful about # having any sensitive data in the code that will be deployed as a remote # function. # Let's continue trying other potential use cases of remote functions. Let's # say we consider the `species`, `island` and `sex` of the penguins # sensitive information and want to redact that by replacing with their hash # code instead. Let's define another scalar custom function and decorate it # as a remote function @bpd.remote_function( [str], str, bigquery_connection="bigframes-rf-conn", reuse=False ) def get_hash(input): import hashlib # handle missing value if input is None: input = "" encoded_input = input.encode() hash = hashlib.md5(encoded_input) return hash.hexdigest() # We can use this remote function in another `pandas`-like API `map` that # can be applied on a DataFrame df_redacted = df[["species", "island", "sex"]].map(get_hash) df_redacted.head(10)
Debes tener el permiso bigquery.routines.create
en el conjunto de datos en el que creas la función remota y el permiso bigquery.connections.delegate
(disponible a través del rol Connection Admin de BigQuery) en la conexión que usa la función remota.
Proporciona contexto definido por el usuario
Puedes especificar user_defined_context
en OPTIONS
como una forma de pares clave-valor, que serán parte de cada solicitud HTTP al extremo. Con el contexto definido por el usuario, puedes crear varias funciones remotas, pero reutilizar un solo extremo, el cual proporciona diferentes comportamientos según el contexto que se le pasa.
En los siguientes ejemplos, se crean dos funciones remotas para encriptar y desencriptar datos BYTES
mediante el mismo extremo.
CREATE FUNCTION `PROJECT_ID.DATASET_ID`.encrypt(x BYTES)
RETURNS BYTES
REMOTE WITH CONNECTION `PROJECT_ID.LOCATION.CONNECTION_NAME`
OPTIONS (
endpoint = 'ENDPOINT_URL',
user_defined_context = [("mode", "encryption")]
)
CREATE FUNCTION `PROJECT_ID.DATASET_ID`.decrypt(x BYTES)
RETURNS BYTES
REMOTE WITH CONNECTION `PROJECT_ID.LOCATION.CONNECTION_NAME`
OPTIONS (
endpoint = 'ENDPOINT_URL',
user_defined_context = [("mode", "decryption")]
)
Limita la cantidad de filas en una solicitud por lotes
Puedes especificar max_batching_rows
en OPTIONS
como la cantidad máxima de filas en cada solicitud HTTP para evitar el tiempo de espera de Cloud Functions. Si no se especifica, BigQuery decidirá cuántas filas se incluyen en un lote.
Usa una función remota en una consulta
Asegúrate de haber otorgado el permiso en tu Cloud Function para que pueda acceder la cuenta de servicio de BigQuery asociada con la conexión de la función remota.
También debes tener el permiso bigquery.routines.get
en el conjunto de datos donde se encuentra el rol remota, y el permiso bigquery.connections.use
, que puedes obtenerBigQuery Connection User
en la conexión que usa la función remota.
Puedes usar una función remota en una consulta como una función definida por el usuario.
Por ejemplo, puedes usar la función remote_add
en la consulta de ejemplo:
SELECT
val,
`PROJECT_ID.DATASET_ID`.remote_add(val, 2)
FROM
UNNEST([NULL,2,3,5,8]) AS val;
En este ejemplo, se produce el siguiente resultado:
+------+-----+
| val | f0_ |
+------+-----+
| NULL | 2 |
| 2 | 4 |
| 3 | 5 |
| 5 | 7 |
| 8 | 10 |
+------+-----+
Regiones admitidas
Hay dos tipos de ubicaciones en BigQuery:
Una región es un lugar geográfico específico, como Londres.
Una multirregión es un área geográfica grande, como Estados Unidos, que contiene dos o más lugares geográficos.
Regiones individuales
En un conjunto de datos de una sola región de BigQuery, solo puedes crear una función remota que use una Cloud Function implementada en la misma región. Por ejemplo:
- Una función remota en la región individual
us-east4
de BigQuery solo puede usar una Cloud Function deus-east4
.
Por lo tanto, para regiones individuales, las funciones remotas solo son compatibles en regiones que admiten Cloud Functions y BigQuery.
Multirregiones
En un conjunto de datos multirregión de BigQuery (US
, EU
), solo puedes crear una función remota que use una función de Cloud Functions implementada en una región dentro de la misma área geográfica grande (US, EU). Por ejemplo:
- Una función remota en la multirregión
US
de BigQuery solo puede usar una Cloud Function implementada en una sola región del área geográfica US, comous-central1
,us-east4
,us-west2
, etcétera. - Una función remota en la multirregión
EU
de BigQuery solo puede usar una Cloud Function implementada en cualquier región individual de los estados miembros de la Unión Europea, comoeurope-north1
,europe-west3
, etcétera.
Para obtener más información sobre regiones y multirregiones de BigQuery, consulta la página Ubicaciones de conjuntos de datos. Para obtener más información sobre las regiones de Cloud Functions, consulta la página Ubicaciones de Cloud Functions.
Conexiones
Para una ubicación de una sola región o multirregional, solo puedes crear una función remota en la misma ubicación que la conexión que usas. Por ejemplo, para crear una función remota en la multirregión US
, usa una conexión ubicada en la multirregión US
.
Precios
Se aplican los precios estándar de BigQuery.
Además, con esta función se pueden generar costos por Cloud Functions y Cloud Run. Revisa las páginas de precios de Cloud Functions y Cloud Run para obtener más detalles.
Usa los Controles del servicio de VPC
Controles del servicio de VPC es una función de Google Cloud que te permite configurar un perímetro seguro para protegerte del robo de datos. Si deseas usar los Controles del servicio de VPC con funciones remotas para obtener seguridad adicional o usar extremos con
configuración de entrada internal traffic
, sigue la Guía de los Controles del servicio de VPC para:
Crear un perímetro de servicio
Agrega el proyecto de BigQuery de la consulta con la función remota en el perímetro.
Agrega el proyecto de extremo al perímetro y configura
Cloud Functions API
oCloud Run API
en los servicios restringidos según el tipo de extremo. Para obtener más detalles, consulta Controles del servicio de VPC de Cloud Functions y Controles del servicio de VPC de Cloud Run.
Prácticas recomendadas para las funciones remotas
Filtra previamente tu entrada: si tu entrada se puede filtrar con facilidad antes de pasarla a una función remota, es probable que tu consulta sea más rápida y económica.
Mantén la escalabilidad de tu Cloud Function. La escalabilidad es una función de instancias mínimas, instancias máximas y simultaneidad.
- Cuando sea posible, usa el valor predeterminado para la cantidad máxima de instancias de tu Cloud Function.
- Ten en cuenta que no hay un límite predeterminado para las funciones de HTTP de Cloud Functions de 1ª gen. Para evitar eventos de escalamiento ilimitados con Cloud Functions HTTP de 1a gen. durante la prueba o en producción, recomendamos configurar un límite, por ejemplo, 3,000.
Sigue otras sugerencias de Cloud Functions para obtener un mejor rendimiento. Las consultas de funciones remotas que interactúan con una Cloud Function de latencia alta pueden fallar debido al tiempo de espera.
Implementa tu extremo a fin de que muestre un código de respuesta HTTP correcto y una carga útil para una respuesta con errores.
Para minimizar los reintentos de BigQuery, usa códigos de respuesta HTTP distintos de 408, 429, 500, 503 y 504 para una respuesta con errores, y asegúrate de detectar todas las excepciones en el código de la función. De lo contrario, el marco de trabajo del servicio HTTP puede mostrar automáticamente el código 500 para cualquier excepción que no se detecte. Es posible que veas solicitudes HTTP que se reintentaron cuando BigQuery vuelva a intentar una partición de datos o una consulta con errores.
El extremo debe mostrar una carga útil de JSON en el formato definido para una respuesta con errores. Aunque no es estrictamente obligatorio, ayuda a BigQuery a distinguir si la respuesta con errores es de la implementación de la función o de la infraestructura de Cloud Functions o Cloud Run. Para este último, BigQuery puede volver a intentarlo con un límite interno diferente.
Cuotas
Para obtener información sobre las cuotas de las funciones remotas, consulta Cuotas y límites.