Trabalhar com funções remotas
Uma função remota do BigQuery permite implementar a função em outras linguagens além do SQL e do JavaScript ou com bibliotecas ou serviços que não são permitidos nas funções definidas pelo usuário do BigQuery.
Visão geral
Uma função remota do BigQuery permite incorporar a funcionalidade do SQL do BigQuery com software fora do BigQuery fornecendo uma integração direta com o Cloud Functions e o Cloud Run. Com as funções remotas do BigQuery, é possível implantar as funções no Cloud Functions ou no Cloud Run implementadas com qualquer linguagem compatível e, em seguida, invocá-las em consultas do GoogleSQL.
Fluxo de trabalho
- Crie o endpoint HTTP no Cloud Functions ou no Cloud Run.
- Crie uma função remota no BigQuery.
- Crie uma conexão do tipo
CLOUD_RESOURCE
. - Crie uma função remota.
- Crie uma conexão do tipo
- Use a função remota em uma consulta como qualquer outra função definida pelo usuário.
Limitações
As funções remotas são compatíveis apenas com um dos seguintes tipos de dados como tipo de argumento ou de retorno:
- Booleano
- Bytes
- Numérico
- String
- Data
- Datetime
- Momento
- Carimbo de data/hora
- JSON
As funções remotas não são compatíveis com os tipos
ARRAY
,STRUCT
,INTERVAL
ouGEOGRAPHY
.Não é possível criar funções remotas temporárias.
Não é possível criar funções remotas com valor de tabela.
Não é possível usar funções remotas ao criar visualizações materializadas.
O valor de retorno de uma função remota é sempre considerado não determinístico. Portanto, o resultado de uma consulta que chama uma função remota não é armazenado em cache.
É possível ter solicitações repetidas com os mesmos dados para o endpoint, mesmo após respostas bem-sucedidas, devido a erros transitórios de rede ou internos do BigQuery.
Quando uma avaliação de função remota é ignorada para algumas linhas devido a curto-circuito, por exemplo, emexpressões condicionais ou a
MERGE
declaração comWHEN [NOT] MATCHED
, os lotes não serão usados com a função remota. Nesse caso, o campocalls
no corpo da solicitação HTTP tem exatamente um elemento.Se o conjunto de dados associado à função remota for replicado para uma região de destino por meio da replicação do conjunto de dados entre regiões, a função remota só poderá ser consultada na região em que foi criado.
Crie um endpoint
Para criar uma função remota que possa implementar a lógica de negócios, é preciso criar um endpoint HTTP usando o Cloud Functions ou o Cloud Run. O endpoint precisa processar um lote de linhas em uma única solicitação POST HTTP e retornar os resultados em lote como uma resposta HTTP.
Se estiver criando a função remota usando o BigQuery DataFrames, não será necessário criar manualmente o endpoint HTTP. O serviço faz isso para você automaticamente.
Consulte o tutorial do Cloud Functions e outras documentações do Cloud Functions para saber como escrever, implantar, testar e manter uma função do Cloud.
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.
É recomendável manter a autenticação padrão, e não permitir a invocação não autenticada da função do Cloud ou do serviço do Cloud Run.
Formato da entrada
O BigQuery envia solicitações HTTP POST com corpo JSON no seguinte formato:
Nome do campo | Descrição | Tipo de campo |
---|---|---|
requestId | ID da solicitação. Exclusivo em várias solicitações enviadas para esse endpoint em uma consulta GoogleSQL. | Sempre fornecido. String. |
autor da chamada | Nome completo do recurso do job para a consulta GoogleSQL que chama a função remota. | Sempre fornecido. String. |
sessionUser | E-mail do usuário que está executando a consulta GoogleSQL. | Sempre fornecido. String. |
userDefinedContext | O contexto definido pelo usuário que foi usado ao criar a função remota no BigQuery. | Opcional. Um objeto JSON com pares de chave-valor. |
chamadas | Um lote de dados de entrada. | Sempre fornecido. Uma matriz JSON.
Cada elemento é uma matriz JSON, que é uma lista de argumentos codificada em JSON de uma chamada de função remota. |
Exemplo de solicitação:
{
"requestId": "124ab1c",
"caller": "//bigquery.googleapis.com/projects/myproject/jobs/myproject:US.bquxjob_5b4c112c_17961fafeaf",
"sessionUser": "test-user@test-company.com",
"userDefinedContext": {
"key1": "value1",
"key2": "v2"
},
"calls": [
[null, 1, "", "abc"],
["abc", "9007199254740993", null, null]
]
}
Formato da saída
O BigQuery espera que o endpoint retorne uma resposta HTTP no formato a seguir. Caso contrário, o BigQuery não poderá consumir o endpoint e falhará na consulta que chama a função remota.
Nome do campo | Descrição | Intervalo de valor |
respostas | Um lote de valores de retorno. | Obrigatório para uma resposta bem-sucedida. Uma 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 |
errorMessage | Mensagem de erro quando o código de resposta HTTP diferente de 200 é retornado. Para erros que não podem ser repetidos, retornamos como parte da mensagem de erro do job do BigQuery para o usuário. | Opcional. String. O tamanho precisa ser inferior a 1 KB. |
Exemplo de resposta bem-sucedida:
{
"replies": [
1,
0
]
}
Exemplo de resposta com falha:
{
"errorMessage": "Received but not expected that the argument 0 be null".
}
Código de resposta HTTP
Seu endpoint retornará o código de resposta HTTP 200 para uma resposta bem-sucedida. Quando o BigQuery recebe qualquer outro valor, ele considera a resposta como uma falha e tenta novamente quando o código de resposta HTTP é 408, 429, 500, 503 ou 504 até algum limite interno.
Codificação JSON de tipos de dados SQL
A codificação JSON em solicitações/respostas HTTP segue a atual codificação JSON do BigQuery para a função TO_JSON_STRING.
Exemplo de código de função do Cloud
O código Python de amostra 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 todo o resultado em uma resposta.
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
Supondo que a função seja implantada no projeto my_gcf_project
na região
us-east1
como o nome da função remote_add
, ela pode ser acessada pelo
endpoint https://us-east1-my_gcf_project.cloudfunctions.net/remote_add
.
Exemplo de código do 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.
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)))
Consulte o guia sobre como criar e implantar o código.
Supondo que o serviço do Cloud Run seja implantado no projeto
my_gcf_project
na região us-east1
como o nome do serviço remote_add
, ele pode
ser acessado pelo endpoint
https://remote_add-<project_id_hash>-ue.a.run.app
.
Criar uma função remota
O BigQuery usa uma conexão CLOUD_RESOURCE
para interagir com a
função do Cloud. Para criar uma função remota, você precisa
criar uma conexão CLOUD_RESOURCE
. Se estiver criando
a função remota usando o BigQuery DataFrames
e você recebeu o administrador
do IAM do projeto (roles/resourcemanager.projectIamAdmin
), você não precisará criar manualmente a conexão e conceder acesso a ela. O serviço faz isso para você automaticamente.
Criar uma conexão
Você precisa ter uma conexão de recursos do Cloud para se conectar ao Cloud Function e ao Cloud Run.
Selecione uma das seguintes opções:
Console
Acessar a página do BigQuery.
Para criar uma conexão, clique em
Adicionar e em Conexões com fontes de dados externas.Na lista Tipo de conexão, selecione Modelos remotos da Vertex AI, funções remotas e BigLake (Cloud Resource).
No campo ID da conexão, insira um nome para a conexão.
Clique em Criar conexão.
Clique em Ir para conexão.
No painel Informações da conexão, copie o ID da conta de serviço para uso em uma etapa posterior.
bq
Em um ambiente de linha de comando, crie uma conexão:
bq mk --connection --location=REGION --project_id=PROJECT_ID \ --connection_type=CLOUD_RESOURCE CONNECTION_ID
O parâmetro
--project_id
substitui o projeto padrão.Substitua:
REGION
: sua região de conexãoPROJECT_ID
: o ID do projeto do Google CloudCONNECTION_ID
: um ID para sua conexão
Quando você cria um recurso de conexão, o BigQuery cria uma conta de serviço do sistema exclusiva e a associa à conexão.
Solução de problemas: se você receber o seguinte erro de conexão, atualize o SDK Google Cloud:
Flags parsing error: flag --connection_type=CLOUD_RESOURCE: value should be one of...
Recupere e copie o ID da conta de serviço para uso em uma etapa posterior:
bq show --connection PROJECT_ID.REGION.CONNECTION_ID
O resultado será assim:
name properties 1234.REGION.CONNECTION_ID {"serviceAccountId": "connection-1234-9u56h9@gcp-sa-bigquery-condel.iam.gserviceaccount.com"}
Terraform
Anexe a seguinte seção ao seu arquivo 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 {} }
CONNECTION_ID
: um ID para sua conexãoPROJECT_ID
: o ID do projeto do Google CloudREGION
: sua região de conexão
Configurar o acesso
Conceda à nova conexão acesso somente leitura à função do Cloud ou do Cloud Run. Não recomendamos invocações não autenticadas para seu serviço do Cloud Functions ou do Cloud Run.
Para conceder papéis, siga estas etapas:
Acessar a página AM e administrador
Clique em
Adicionar.A caixa de diálogo Adicionar principais é aberta.
No campo Novos principais, digite o ID da conta de serviço que você copiou anteriormente.
No campo Selecionar uma função, escolha uma das seguintes opções:
- Se você estiver usando uma Função do Cloud de primeira geração, escolha Função do Cloud e selecione Papel de invocador da Função do Cloud.
- Se você estiver usando uma Função do Cloud de segunda geração, escolha Cloud Run e selecione Papel de invocador do Cloud Run.
- Se você estiver usando um serviço do Cloud Run, escolha Cloud Run e selecione Papel de invocador do Cloud Run.
Clique em Save.
Criar uma função remota
Para criar uma função remota:
SQL
Execute a seguinte
instrução CREATE FUNCTION
no BigQuery:
No Console do Google Cloud, acesse a página BigQuery.
No editor de consultas, digite a seguinte instrução:
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' )Substitua:
DATASET_ID
: o ID do conjunto de dados do BigQuery.ENDPOINT_URL
: o URL da função do Cloud ou do endpoint da função remota do Cloud Run.
Clique em
Executar.
Para mais informações sobre como executar consultas, acesse Executar uma consulta interativa.
BigQuery DataFrames
O BigQuery DataFrames está em visualização.
- Ative as APIs necessárias e verifique se você recebeu os papéis necessários, conforme descrito na seção Requisitos de Funções remotas.
Use o 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)
Na conexão usada pela função remota, você precisa ter as permissões
bigquery.routines.create
no conjunto de dados em que
a função remota foi criada e a permissão bigquery.connections.delegate
(disponível no papel Administrador de conexões do BigQuery) na conexão
usada para a função remota.
Como fornecer contexto definido pelo usuário
É possível especificar user_defined_context
em OPTIONS
como uma forma de pares de
chave-valor, que vão estar em todas as solicitações HTTP para o endpoint. Com o contexto
definido pelo usuário, é possível criar várias funções remotas, mas reutilizar um único
endpoint, que oferece vários comportamentos com base no contexto passado para ele.
Os exemplos a seguir criam duas funções remotas para criptografar e descriptografar dados de
BYTES
usando o mesmo endpoint.
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")]
)
Como limitar o número de linhas em uma solicitação em lote
É possível especificar max_batching_rows
em OPTIONS
como o número máximo de linhas
em cada solicitação HTTP para evitar o
tempo limite do Cloud Functions. Se não for
especificado, o BigQuery vai decidir quantas linhas serão incluídas em um lote.
Usar uma função remota em uma consulta
Verifique se você concedeu a permissão na função do Cloud para que fique acessível à conta de serviço do BigQuery associada à conexão da função remota.
Você também precisa ter a permissão bigquery.routines.get
no conjunto de dados
em que a função está e a permissão bigquery.connections.use
,
que você pode receber pelo papel BigQuery Connection User
na conexão
usada pela função remota.
Use uma função remota em uma consulta da mesma forma que uma função definida pelo usuário.
É possível usar a função remote_add
na consulta de
exemplo:
SELECT
val,
`PROJECT_ID.DATASET_ID`.remote_add(val, 2)
FROM
UNNEST([NULL,2,3,5,8]) AS val;
Este exemplo produz a saída a seguir:
+------+-----+
| val | f0_ |
+------+-----+
| NULL | 2 |
| 2 | 4 |
| 3 | 5 |
| 5 | 7 |
| 8 | 10 |
+------+-----+
Regiões compatíveis
Há dois tipos de local no BigQuery:
Uma região é um lugar geográfico específico, como Londres.
Um local multirregional é uma área geográfica grande, como Estados Unidos, que contém dois ou mais lugares geográficos.
Regiões únicas
Em um conjunto de dados de região única do BigQuery, só é possível criar uma função remota que use uma função do Cloud implantada na mesma região. Por exemplo:
- Uma função remota em uma região única
us-east4
do BigQuery só pode usar uma função do Cloud emus-east4
.
Portanto, para regiões únicas, as funções remotas têm suporte apenas em regiões compatíveis com o Cloud Functions e o BigQuery.
Multirregiões
Em um conjunto de dados multirregional (US
, EU
) do BigQuery, só é possível criar uma função
remota que use uma função do Cloud implantada em uma região dentro da
mesma grande área geográfica (EUA, UE). Exemplo:
- Uma função remota na multirregião
US
só pode usar uma função do Cloud implantada em qualquer região da área geográfica dos EUA, comous-central1
,us-east4
,us-west2
etc. - Uma função remota na multirregião
EU
do BigQuery só pode usar uma função do Cloud implantada em qualquer região única nos estados-membros da União Europeia, comoeurope-north1
,europe-west3
etc.
Saiba mais sobre regiões e multirregiões na página Locais dos conjuntos de dados. Saiba mais sobre as regiões do Cloud Functions na página Locais do Cloud Functions.
Conexões
Para os locais de região única ou multirregiões, só é possível
criar uma função remota no mesmo local que a conexão usada. Por exemplo,
para criar uma função remota na multirregião
US
, use uma conexão localizada na multirregião US
.
Preços
São aplicados os preços padrão do BigQuery.
Além disso, os custos do Cloud Functions e do Cloud Run podem ser gerados com o uso desse recurso. Consulte as páginas de preços do Cloud Functions e do Cloud Run para mais detalhes.
Como usar VPC Service Controls
O VPC Service Controls é um recurso do Google Cloud em que é possível configurar um perímetro seguro para evitar a exfiltração de dados. Para usar
o VPC Service Controls com funções remotas para aumentar a segurança ou para usar
endpoints com
configurações de entrada internal traffic
,
siga a
Guia do VPC Service Controls para:
criar um perímetro de serviço;
Adicione o projeto do BigQuery da consulta usando a função remota no perímetro.
Adicione o projeto de endpoint ao perímetro e defina
Cloud Functions API
ouCloud Run API
nos serviços restritos com base no seu tipo de endpoint. Confira mais detalhes em VPC Service Controls do Cloud Functions e Controles de serviço da VPC do Cloud Run.
Práticas recomendadas em funções remotas
Pré-filtre a entrada: se sua entrada puder ser filtrada com facilidade antes de ser passada para uma função remota, a consulta poderá ficar mais rápida e mais barata.
Mantenha a Função do Cloud escalonável. A escalonabilidade é uma função do número mínimo de instâncias, do máximo de instâncias e da simultaneidade.
- Sempre que possível, use o valor padrão para o número máximo de instâncias da função do Cloud.
- Observe que não há limite padrão para o Cloud Functions HTTP de primeira geração. Para evitar eventos de escalonamento ilimitado com o Cloud Functions HTTP de primeira geração durante testes ou na produção, recomendamos definir um limite. Por exemplo, 3000.
Siga outras dicas do Cloud Functions para melhorar o desempenho. As consultas de funções remotas que interagem com uma função do Cloud de alta latência podem falhar devido ao tempo limite.
Implemente seu endpoint para retornar um código de resposta HTTP e um payload certos para uma resposta com falha.
Para minimizar as tentativas do BigQuery, use códigos de resposta HTTP que não sejam 408, 429, 500, 503 e 504 para uma resposta com falha e verifique todas as exceções no código da função. Caso contrário, o framework de serviço HTTP poderá retornar automaticamente 500 para quaisquer exceções não identificadas. Ainda é possível ter novas solicitações HTTP quando o BigQuery tenta executar novamente uma partição ou consulta de dados com falha.
Seu endpoint precisa retornar um payload JSON no formato definido para uma resposta com falha. Mesmo que não seja estritamente obrigatório, ajuda o BigQuery a diferenciar se a resposta com falha é da implementação da função ou da infraestrutura do Cloud Functions/Cloud Run. Nesse caso, o BigQuery pode tentar novamente com um limite interno diferente.
Cotas
Para saber mais sobre cotas de funções remotas, consulte Cotas e limites.