עבודה עם פונקציות מרחוק

פונקציה מרוחקת ב-BigQuery מאפשרת לכם להטמיע את הפונקציה בשפות אחרות מלבד SQL ו-JavaScript, או באמצעות ספריות או שירותים שלא מותרים בפונקציות בהגדרת המשתמש ב-BigQuery.

סקירה כללית

פונקציה מרוחקת של BigQuery מאפשרת לכם לשלב פונקציונליות של GoogleSQL עם תוכנה מחוץ ל-BigQuery, באמצעות שילוב ישיר עם פונקציות Cloud Run ועם Cloud Run. באמצעות פונקציות מרוחקות של BigQuery, אתם יכולים לפרוס את הפונקציות שלכם בפונקציות Cloud Run או ב-Cloud Run שמוטמעות בכל שפה נתמכת, ואז להפעיל אותן משאילתות GoogleSQL.

תהליך עבודה

  1. יוצרים את נקודת הקצה של ה-HTTP בפונקציות Cloud Run או ב-Cloud Run.
  2. יוצרים פונקציה מרוחקת ב-BigQuery.
    1. יוצרים חיבור מסוג CLOUD_RESOURCE.
    2. יוצרים פונקציה מרחוק.
  3. משתמשים בפונקציה המרוחקת באילתא בדיוק כמו בכל פונקציה אחרת בהגדרת המשתמש.

מגבלות

  • פונקציות מרוחקות תומכות רק באחד מסוגי הנתונים הבאים כסוג ארגומנט או כסוג החזרה:

    • בוליאני
    • בייטים
    • Numeric
    • String
    • תאריך
    • תאריך ושעה
    • שעה
    • חותמת הזמן
    • JSON

    פונקציות מרחוק לא תומכות בסוגים ARRAY,‏ STRUCT,‏ INTERVAL או GEOGRAPHY.

  • אי אפשר ליצור פונקציות מרוחקות שמחזירות טבלה.

  • אי אפשר להשתמש בפונקציות מרוחקות כשיוצרים תצוגות חומריות.

  • הערך שמוחזר מפונקציה מרוחקת תמיד נחשב כלא דטרמיניסטי, ולכן התוצאה של שאילתה שקוראת לפונקציה מרוחקת לא נשמרת במטמון.

  • יכול להיות שתראו בקשות חוזרות עם אותם נתונים לנקודת הקצה, גם אחרי קבלת תגובות מוצלחות, בגלל שגיאות זמניות ברשת או שגיאות פנימיות ב-BigQuery.

  • אם דילוג על הערכה של פונקציה מרוחקת מתבצע בשורות מסוימות בגלל קיצור דרך, למשל בביטויי תנאים או בהצהרת MERGE עם WHEN [NOT] MATCHED, לא מתבצעת אצווה עם הפונקציה המרוחקת. במקרה הזה, השדה calls בגוף בקשת ה-HTTP מכיל בדיוק רכיב אחד.

  • אם מערך הנתונים שמשויך לפונקציה המרוחקת משוכפל לאזור יעד באמצעות שכפול של מערכי נתונים באזורים שונים, אפשר לבצע שאילתות על הפונקציה המרוחקת רק באזור שבו היא נוצרה.

יצירת נקודת קצה

כדי ליצור פונקציה מרוחקת שיכולה להטמיע לוגיקה עסקית, צריך ליצור נקודת קצה של HTTP באמצעות פונקציות Cloud Run או Cloud Run. נקודת הקצה צריכה להיות מסוגלת לעבד אצווה של שורות בבקשת HTTP POST אחת ולהחזיר את התוצאות של האצווה כתגובת HTTP.

אם יוצרים את הפונקציה המרוחקת באמצעות BigQuery DataFrames, לא צריך ליצור את נקודת הקצה (endpoint) של HTTP באופן ידני, כי השירות עושה זאת באופן אוטומטי.

בהמדריך לפונקציות Cloud Run ובמסמכי התיעוד האחרים בנושא פונקציות Cloud Run מוסבר איך לכתוב, לפרוס, לבדוק ולתחזק פונקציית Cloud Run.

במדריך למתחילים בנושא Cloud Run ובמסמכי Cloud Run אחרים מוסבר איך לכתוב, לפרוס, לבדוק ולתחזק שירות Cloud Run.

מומלץ להשאיר את אימות ברירת המחדל במקום לאפשר הפעלה לא מאומתת של פונקציית Cloud Run או שירות Cloud Run.

פורמט קלט

‫BigQuery שולח בקשות HTTP POST עם גוף JSON בפורמט הבא:

שם השדה תיאור סוג השדה
requestId מזהה הבקשה. ייחודיות בכמה בקשות שנשלחו לנקודת הקצה הזו בשאילתת GoogleSQL. תמיד מסופק. מחרוזת.
פונקציית קריאה חוזרת (caller) השם המלא של המשאב של הג'וב בשאילתת GoogleSQL שקוראת לפונקציה המרוחקת. תמיד מסופק. מחרוזת.
sessionUser כתובת האימייל של המשתמש שמריץ את שאילתת GoogleSQL. תמיד מסופק. מחרוזת.
userDefinedContext ההקשר שהוגדר על ידי המשתמש וששימש ליצירת הפונקציה המרוחקת ב-BigQuery. זה שינוי אופציונלי. אובייקט JSON עם צמדי מפתח-ערך.
שיחות אצווה של נתוני קלט. תמיד מסופק. מערך JSON.

כל רכיב הוא מערך JSON, שהוא רשימת ארגומנטים מקודדת ב-JSON של בקשה להפעלת פונקציה מרוחקת אחת.

דוגמה לבקשה:

{
 "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]
 ]
}

פורמט פלט

מערכת BigQuery מצפה שנקודת הקצה תחזיר תגובת HTTP בפורמט הבא. אחרת, מערכת BigQuery לא תוכל לצרוך אותה והשאילתה שקוראת לפונקציה המרוחקת תיכשל.

שם השדה תיאור טווח ערכים
תשובות קבוצה של ערכים שמוחזרים. חובה למלא את השדות האלה כדי שהתגובה תהיה תקינה. מערך JSON.

כל רכיב תואם לערך שמוחזר מהפונקציה החיצונית בקידוד JSON.

הגודל של המערך חייב להיות זהה לגודל של מערך ה-JSON של calls בבקשת ה-HTTP. לדוגמה, אם מערך ה-JSON ב-calls מכיל 4 רכיבים, גם מערך ה-JSON הזה צריך להכיל 4 רכיבים.

errorMessage הודעת שגיאה כשמוחזר קוד תגובת HTTP שאינו 200. במקרה של שגיאות שלא ניתן לנסות שוב, אנחנו מחזירים את השגיאה כחלק מהודעת השגיאה של משימת BigQuery למשתמש. זה שינוי אופציונלי. מחרוזת. הגודל צריך להיות קטן מ-1KB.

דוגמה לתגובה מוצלחת:

{
  "replies": [
    1,
    0
  ]
}

דוגמה לתגובה שנכשלה:

{
  "errorMessage": "Received but not expected that the argument 0 be null".
}

קוד תגובת HTTP

נקודת הקצה צריכה להחזיר את קוד התגובה 200 של HTTP לתגובה מוצלחת. כש-BigQuery מקבל ערך אחר, הוא מחשיב את התגובה ככישלון ומנסה שוב אם קוד תגובת ה-HTTP הוא 408, ‏ 429, ‏ 500, ‏ 503 או 504, עד שמגיעים למגבלה פנימית מסוימת.

קידוד JSON של סוג נתונים של SQL

קידוד JSON בבקשת HTTP או בתגובה ל-HTTP מתבצע לפי קידוד ה-JSON הקיים ב-BigQuery עבור הפונקציה TO_JSON_STRING.

קוד לדוגמה של פונקציית Cloud Run

בדוגמת הקוד הבאה ב-Python מוצגת הטמעה של הוספת כל הארגומנטים של המספרים השלמים של הפונקציה המרוחקת. הוא מטפל בבקשה עם הארגומנטים להפעלות באצווה ומחזיר את כל התוצאות בתגובה.

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

בהנחה שהפונקציה נפרסה בפרויקט my_gcf_project באזור us-east1 עם שם הפונקציה remote_add, אפשר לגשת אליה דרך נקודת הקצה https://us-east1-my_gcf_project.cloudfunctions.net/remote_add.

קוד לדוגמה של Cloud Run

קוד Python לדוגמה הבא מטמיע שירות אינטרנט שאפשר ליצור ולפרוס ב-Cloud Run כדי לקבל את אותה פונקציונליות.

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

במדריך מוסבר איך ליצור את הקוד ולפרוס אותו.

בהנחה ששירות Cloud Run נפרס בפרויקט my_gcf_project באזור us-east1 עם שם השירות remote_add, אפשר לגשת אליו דרך נקודת הקצה https://remote_add-<project_id_hash>-ue.a.run.app.

יצירת פונקציה מרוחקת

‫BigQuery משתמש בCLOUD_RESOURCE חיבור כדי ליצור אינטראקציה עם פונקציית Cloud Run. כדי ליצור פונקציה מרוחקת, צריך ליצור חיבור CLOUD_RESOURCE. אם אתם יוצרים את הפונקציה המרוחקת באמצעות BigQuery DataFrames ויש לכם תפקיד אדמין ב-IAM של הפרויקט (roles/resourcemanager.projectIamAdmin), אתם לא צריכים ליצור את החיבור ולהעניק לו גישה באופן ידני. השירות עושה זאת בשבילכם באופן אוטומטי.

יצירת חיבור

כדי להתחבר לפונקציית Cloud Run ול-Cloud Run, צריך חיבור למשאב Cloud.

אם כבר הגדרתם חיבור ברירת מחדל או שיש לכם את תפקיד האדמין ב-BigQuery, אתם יכולים לדלג על השלב הזה.

בוחרים באחת מהאפשרויות הבאות:

המסוף

  1. עוברים לדף BigQuery.

    כניסה ל-BigQuery

  2. בחלונית השמאלית, לוחצים על Explorer:

    כפתור מודגש לחלונית הסייר.

    אם החלונית הימנית לא מוצגת, לוחצים על הרחבת החלונית הימנית כדי לפתוח אותה.

  3. בחלונית Explorer מרחיבים את שם הפרויקט ואז לוחצים על Connections.

  4. בדף Connections (חיבורים), לוחצים על Create connection (יצירת חיבור).

  5. בקטע Connection type (סוג החיבור), בוחרים באפשרות Vertex AI remote models, remote functions, BigLake and Spanner (Cloud Resource) (מודלים מרוחקים של Vertex AI, פונקציות מרוחקות, BigLake ו-Spanner (משאב בענן)).

  6. בשדה מזהה החיבור, מזינים שם לחיבור.

  7. בקטע Location type, בוחרים מיקום לחיבור. החיבור צריך להיות ממוקם יחד עם המשאבים האחרים שלכם, כמו מערכי נתונים.

  8. לוחצים על יצירת קישור.

  9. לוחצים על מעבר לחיבור.

  10. בחלונית Connection info (פרטי התחברות), מעתיקים את מזהה חשבון השירות כדי להשתמש בו בשלב מאוחר יותר.

BQ

  1. בסביבת שורת פקודה, יוצרים חיבור:

    bq mk --connection --location=REGION --project_id=PROJECT_ID \
        --connection_type=CLOUD_RESOURCE CONNECTION_ID

    הפרמטר --project_id מבטל את פרויקט ברירת המחדל.

    מחליפים את מה שכתוב בשדות הבאים:

    • REGION: אזור החיבור
    • PROJECT_ID: מזהה הפרויקט ב- Cloud de Confiance
    • CONNECTION_ID: מזהה לחיבור

    כשיוצרים משאב חיבור, מערכת BigQuery יוצרת חשבון שירות ייחודי ומקשרת אותו לחיבור.

    פתרון בעיות: אם מופיעה שגיאת החיבור הבאה, צריך לעדכן את Google Cloud SDK:

    Flags parsing error: flag --connection_type=CLOUD_RESOURCE: value should be one of...
    
  2. מאחזרים ומעתיקים את מזהה חשבון השירות כדי להשתמש בו בשלב מאוחר יותר:

    bq show --connection PROJECT_ID.REGION.CONNECTION_ID

    הפלט אמור להיראות כך:

    name                          properties
    1234.REGION.CONNECTION_ID     {"serviceAccountId": "connection-1234-9u56h9@gcp-sa-bigquery-condel.s3ns-system.iam.gserviceaccount.com"}
    

Python

לפני שמנסים את הדוגמה הזו, צריך לפעול לפי Pythonהוראות ההגדרה שבמדריך למתחילים של BigQuery באמצעות ספריות לקוח. מידע נוסף מופיע במאמרי העזרה של BigQuery Python API.

כדי לבצע אימות ב-BigQuery, צריך להגדיר את Application Default Credentials. מידע נוסף זמין במאמר הגדרת אימות לספריות לקוח.

לפני שמריצים דוגמאות קוד, צריך להגדיר את משתנה הסביבה GOOGLE_CLOUD_UNIVERSE_DOMAIN לערך s3nsapis.fr.

import google.api_core.exceptions
from google.cloud import bigquery_connection_v1

client = bigquery_connection_v1.ConnectionServiceClient()


def create_connection(
    project_id: str,
    location: str,
    connection_id: str,
):
    """Creates a BigQuery connection to a Cloud Resource.

    Cloud Resource connection creates a service account which can then be
    granted access to other Google Cloud resources for federated queries.

    Args:
        project_id: The Google Cloud project ID.
        location: The location of the connection (for example, "us-central1").
        connection_id: The ID of the connection to create.
    """

    parent = client.common_location_path(project_id, location)

    connection = bigquery_connection_v1.Connection(
        friendly_name="Example Connection",
        description="A sample connection for a Cloud Resource.",
        cloud_resource=bigquery_connection_v1.CloudResourceProperties(),
    )

    try:
        created_connection = client.create_connection(
            parent=parent, connection_id=connection_id, connection=connection
        )
        print(f"Successfully created connection: {created_connection.name}")
        print(f"Friendly name: {created_connection.friendly_name}")
        print(
            f"Service Account: {created_connection.cloud_resource.service_account_id}"
        )

    except google.api_core.exceptions.AlreadyExists:
        print(f"Connection with ID '{connection_id}' already exists.")
        print("Please use a different connection ID.")
    except Exception as e:
        print(f"An unexpected error occurred while creating the connection: {e}")

Node.js

לפני שמנסים את הדוגמה הזו, צריך לפעול לפי Node.jsהוראות ההגדרה שבמדריך למתחילים של BigQuery באמצעות ספריות לקוח. מידע נוסף מופיע במאמרי העזרה של BigQuery Node.js API.

כדי לבצע אימות ב-BigQuery, צריך להגדיר את Application Default Credentials. מידע נוסף זמין במאמר הגדרת אימות לספריות לקוח.

לפני שמריצים דוגמאות קוד, צריך להגדיר את משתנה הסביבה GOOGLE_CLOUD_UNIVERSE_DOMAIN לערך s3nsapis.fr.

const {ConnectionServiceClient} =
  require('@google-cloud/bigquery-connection').v1;
const {status} = require('@grpc/grpc-js');

const client = new ConnectionServiceClient();

/**
 * Creates a new BigQuery connection to a Cloud Resource.
 *
 * A Cloud Resource connection creates a service account that can be granted access
 * to other Google Cloud resources.
 *
 * @param {string} projectId The Google Cloud project ID. for example, 'example-project-id'
 * @param {string} location The location of the project to create the connection in. for example, 'us-central1'
 * @param {string} connectionId The ID of the connection to create. for example, 'example-connection-id'
 */
async function createConnection(projectId, location, connectionId) {
  const parent = client.locationPath(projectId, location);

  const connection = {
    friendlyName: 'Example Connection',
    description: 'A sample connection for a Cloud Resource',
    // The service account for this cloudResource will be created by the API.
    // Its ID will be available in the response.
    cloudResource: {},
  };

  const request = {
    parent,
    connectionId,
    connection,
  };

  try {
    const [response] = await client.createConnection(request);

    console.log(`Successfully created connection: ${response.name}`);
    console.log(`Friendly name: ${response.friendlyName}`);

    console.log(`Service Account: ${response.cloudResource.serviceAccountId}`);
  } catch (err) {
    if (err.code === status.ALREADY_EXISTS) {
      console.log(`Connection '${connectionId}' already exists.`);
    } else {
      console.error(`Error creating connection: ${err.message}`);
    }
  }
}

Terraform

משתמשים במשאב google_bigquery_connection.

כדי לבצע אימות ב-BigQuery, צריך להגדיר את Application Default Credentials. מידע נוסף זמין במאמר הגדרת אימות לספריות לקוח.

בדוגמה הבאה נוצר חיבור למשאב Cloud בשם my_cloud_resource_connection באזור US:


# This queries the provider for project information.
data "google_project" "default" {}

# This creates a cloud resource connection in the US region named my_cloud_resource_connection.
# Note: The cloud resource nested object has only one output field - serviceAccountId.
resource "google_bigquery_connection" "default" {
  connection_id = "my_cloud_resource_connection"
  project       = data.google_project.default.project_id
  location      = "US"
  cloud_resource {}
}

כדי להחיל את ההגדרות של Terraform בפרויקט ב- Cloud de Confiance , מבצעים את השלבים בקטעים הבאים.

הכנת Cloud Shell

  1. מפעילים את Cloud Shell.
  2. מגדירים את פרויקט ברירת המחדל שבו רוצים להחיל את ההגדרות של Terraform. Cloud de Confiance

    תצטרכו להריץ את הפקודה הזו רק פעם אחת לכל פרויקט, ותוכלו לעשות זאת בכל ספרייה.

    export GOOGLE_CLOUD_PROJECT=PROJECT_ID

    אם תגדירו ערכים ספציפיים בקובץ התצורה של Terraform, הם יבטלו את ערכי ברירת המחדל של משתני הסביבה.

הכנת הספרייה

לכל קובץ תצורה של Terraform צריכה להיות ספרייה משלו (שנקראת גם מודול ברמה הבסיסית).

  1. יוצרים ספרייה חדשה ב-Cloud Shell ובה יוצרים קובץ חדש. שם הקובץ חייב לכלול את הסיומת .tf, למשל main.tf. במדריך הזה, הקובץ נקרא main.tf.
    mkdir DIRECTORY && cd DIRECTORY && touch main.tf
  2. אם אתם עוקבים אחרי המדריך, תוכלו להעתיק את הקוד לדוגמה בכל קטע או שלב.

    מעתיקים את הקוד לדוגמה בקובץ main.tf החדש שיצרתם.

    לחלופין, אפשר גם להעתיק את הקוד מ-GitHub. כדאי לעשות את זה כשקטע הקוד של Terraform הוא חלק מפתרון מקצה לקצה.

  3. בודקים את הפרמטרים לדוגמה ומשנים אותם בהתאם לסביבה שלכם.
  4. שומרים את השינויים.
  5. מפעילים את Terraform. צריך לעשות זאת רק פעם אחת לכל ספרייה.
    terraform init

    אופציונלי: תוכלו לכלול את האפשרות -upgrade, כדי להשתמש בגרסה העדכנית ביותר של הספק של Google:

    terraform init -upgrade

החלה של השינויים

  1. בודקים את ההגדרות ומוודאים שהמשאבים שמערכת Terraform תיצור או תעדכן תואמים לציפיות שלכם:
    terraform plan

    מתקנים את ההגדרות לפי הצורך.

  2. מריצים את הפקודה הבאה ומזינים yes בהודעה שמופיעה, כדי להחיל את הגדרות Terraform:
    terraform apply

    ממתינים עד שב-Terraform תוצג ההודעה "Apply complete!‎".

  3. פותחים את Cloud de Confiance הפרויקט כדי לראות את התוצאות. במסוף Cloud de Confiance , נכנסים למשאבים בממשק המשתמש כדי לוודא שהם נוצרו או עודכנו ב-Terraform.

אישור הגישה

צריך לתת לחיבור החדש גישת קריאה בלבד לפונקציית Cloud Run או לשירות Cloud Run. לא מומלץ לאפשר הפעלה לא מאומתת של פונקציית Cloud Run או שירות Cloud Run.

כדי להקצות תפקידים, בצע את השלבים הבאים:

  1. עוברים לדף IAM & Admin.

    כניסה לדף IAM & Admin

  2. לוחצים על הוספה.

    תיבת הדו-שיח Add principals נפתחת.

  3. בשדה New principals, מזינים את מזהה חשבון השירות שהעתקתם קודם.

  4. בשדה Select a role, בוחרים באחת מהאפשרויות הבאות:

    • אם אתם משתמשים בפונקציית Cloud Run מדור ראשון, בוחרים באפשרות Cloud Function ואז באפשרות Cloud Function Invoker role.
    • אם אתם משתמשים בפונקציית Cloud Run מדור שני, בוחרים באפשרות Cloud Run ואז באפשרות Cloud Run Invoker role.
    • אם אתם משתמשים בשירות Cloud Run, בוחרים באפשרות Cloud Run ואז בוחרים באפשרות Cloud Run Invoker role.
  5. לוחצים על Save.

יצירת פונקציה מרוחקת

כדי ליצור פונקציה מרחוק:

SQL

מריצים את ההצהרה הבאה CREATE FUNCTION ב-BigQuery:

  1. במסוף Cloud de Confiance , עוברים לדף BigQuery.

    כניסה ל-BigQuery

  2. מזינים את ההצהרה הבאה בעורך השאילתות:

    CREATE FUNCTION PROJECT_ID.DATASET_ID.remote_add(x INT64, y INT64) RETURNS INT64
    REMOTE WITH CONNECTION PROJECT_ID.LOCATION.CONNECTION_NAME
    OPTIONS (
      endpoint = 'ENDPOINT_URL'
    )

    מחליפים את מה שכתוב בשדות הבאים:

    • DATASET_ID: המזהה של מערך הנתונים ב-BigQuery.
    • ENDPOINT_URL: כתובת ה-URL של פונקציית Cloud Run או של נקודת הקצה של פונקציית Cloud Run מרוחקת.

  3. לוחצים על הפעלה.

מידע נוסף על הרצת שאילתות זמין במאמר הרצת שאילתה אינטראקטיבית.

‫BigQuery DataFrames

  1. מפעילים את ממשקי ה-API הנדרשים ומוודאים שהוקצו לכם התפקידים הנדרשים, כמו שמתואר בקטע דרישות במאמר בנושא פונקציות מרוחקות.
  2. משתמשים בremote_function decorator:

    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 will be letting the default connection `bigframes-default-connection`
    # be used. 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(
        reuse=False,
        cloud_function_service_account="default",
    )
    def get_bucket(num: float) -> str:
        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. The custom function in this example has external
    # package dependency, which can be specified via `packages` parameter.
    @bpd.remote_function(
        reuse=False,
        packages=["cryptography"],
        cloud_function_service_account="default",
    )
    def get_hash(input: str) -> str:
        from cryptography.fernet import Fernet
    
        # handle missing value
        if input is None:
            input = ""
    
        key = Fernet.generate_key()
        f = Fernet(key)
        return f.encrypt(input.encode()).decode()
    
    # 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)
    
    

צריך שתהיה לכם ההרשאה bigquery.routines.create במערך הנתונים שבו אתם יוצרים את הפונקציה המרוחקת, וההרשאה bigquery.connections.delegate (שזמינה מהתפקיד 'אדמין של חיבור BigQuery') בחיבור שבו משתמשת הפונקציה המרוחקת.

הוספת הקשר שהוגדר על ידי המשתמש

אפשר לציין את user_defined_context ב-OPTIONS כזוגות של מפתח וערך, שיהיו חלק מכל בקשת HTTP לנקודת הקצה. בעזרת הקשר שמוגדר על ידי המשתמש, אפשר ליצור כמה פונקציות מרחוק אבל להשתמש שוב בנקודת קצה אחת, שמספקת התנהגויות שונות בהתאם להקשר שמועבר אליה.

בדוגמאות הבאות נוצרות שתי פונקציות מרוחקות להצפנה ולפענוח של נתוני BYTES באמצעות אותה נקודת קצה.

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

הגבלת מספר השורות בבקשת Batch

כדי למנוע פסק זמן בפונקציות Cloud Run, אפשר לציין את max_batching_rows ב-OPTIONS כמספר המקסימלי של השורות בכל בקשת HTTP. אם מציינים את max_batching_rows, ‏ BigQuery קובע את מספר השורות בחבילה עד למגבלת max_batching_rows. אם לא מציינים את מספר השורות, BigQuery קובע באופן אוטומטי את מספר השורות לאיחוד.

שימוש בפונקציה מרוחקת בשאילתה

צריך לוודא שהענקתם הרשאה בפונקציית Cloud Run, כדי שחשבון השירות של BigQuery שמשויך לחיבור של הפונקציה המרוחקת יוכל לגשת אליה.

בנוסף, צריך לקבל את ההרשאה bigquery.routines.get במערך הנתונים שבו נמצאת הפונקציה המרוחקת, ואת ההרשאה bigquery.connections.use, שאפשר לקבל דרך התפקיד BigQuery Connection User, בחיבור שבו נעשה שימוש בפונקציה המרוחקת.

אפשר להשתמש בפונקציה מרוחקת בשאילתה בדיוק כמו בפונקציה בהגדרת המשתמש.

לדוגמה, אפשר להשתמש בפונקציה remote_add בשאילתה לדוגמה:

SELECT
  val,
  `PROJECT_ID.DATASET_ID`.remote_add(val, 2)
FROM
  UNNEST([NULL,2,3,5,8]) AS val;

הדוגמה הזו יוצרת את הפלט הבא:

+------+-----+
|  val | f0_ |
+------+-----+
| NULL |   2 |
|    2 |   4 |
|    3 |   5 |
|    5 |   7 |
|    8 |  10 |
+------+-----+

אזורים נתמכים

יש שני סוגים של מיקומים ב-BigQuery:

  • השדה הזה יכול להכיל אזור, כלומר, מקום גיאוגרפי ספציפי, כמו לונדון.

  • מספר אזורים – אזור גיאוגרפי נרחב, כמו ארצות הברית, שכולל שני מקומות גיאוגרפיים או יותר.

אזורים יחידים

במערך נתונים של אזור יחיד ב-BigQuery, אפשר ליצור רק פונקציה מרוחקת שמשתמשת בפונקציית Cloud Run שנפרסה באותו אזור. לדוגמה:

  • פונקציה מרוחקת באזור יחיד ב-BigQuery‏ us-east4 יכולה להשתמש רק בפונקציית Cloud Run ב-us-east4.

לכן, באזורים יחידים, פונקציות מרוחקות נתמכות רק באזורים שתומכים גם בפונקציות Cloud Run וגם ב-BigQuery.

במספר אזורים

במערך נתונים של BigQuery במספר אזורים (US, EU), אפשר ליצור רק פונקציה מרוחקת שמשתמשת בפונקציית Cloud Run שנפרסה באזור שנמצא באותו אזור גיאוגרפי גדול (ארה"ב, האיחוד האירופי). לדוגמה:

  • פונקציה מרוחקת ב-BigQuery US במספר אזורים יכולה להשתמש רק בפונקציית Cloud Run שנפרסה באזור יחיד באזור הגיאוגרפי של ארה"ב, כמו us-central1,‏ us-east4,‏ us-west2 וכו'.
  • פונקציה מרוחקת ב-BigQuery EU במספר אזורים יכולה להשתמש רק בפונקציית Cloud Run שפריסתה בוצעה באזור יחיד מתוך מדינות חברות באיחוד האירופי, כמו europe-north1, europe-west3 וכו'.

מידע נוסף על אזורים ואזורים מרובים ב-BigQuery זמין בדף מיקומי מערכי נתונים. מידע נוסף על אזורים של פונקציות Cloud Run זמין בדף מיקומים של פונקציות Cloud Run.

חיבורים

בין אם מדובר במיקום באזור יחיד או במיקום במספר אזורים, אפשר ליצור פונקציה מרחוק רק באותו מיקום שבו נמצא החיבור שבו אתם משתמשים. לדוגמה, כדי ליצור פונקציה מרוחקת באזור הגיאוגרפי הנרחב US, צריך להשתמש בחיבור שנמצא באזור הגיאוגרפי הנרחב US.

תמחור

  • יחולו תעריפי BigQuery הרגילים.

  • בנוסף, יכול להיות שיהיו עלויות על פונקציות Cloud Run ועל Cloud Run בגלל השימוש בתכונה הזו. פרטים נוספים מופיעים בדפי המחירון של פונקציות Cloud Run ושל Cloud Run.

שימוש ב-VPC Service Controls

VPC Service Controls הוא תכונה Cloud de Confiance by S3NS שמאפשרת להגדיר מתחם אבטחה היקפית כדי להגן מפני זליגת נתונים. כדי להשתמש ב-VPC Service Controls עם פונקציות מרחוק לאבטחה נוספת, או כדי להשתמש בנקודות קצה עם internal traffic הגדרות Ingress, צריך לפעול לפי המדריך ל-VPC Service Controls כדי:

  1. יוצרים גבולות גזרה לשירות.

  2. מוסיפים את פרויקט BigQuery של השאילתה באמצעות הפונקציה המרוחקת להיקף.

  3. מוסיפים את פרויקט נקודת הקצה לגבולות הגזרה ומגדירים את Cloud Functions API או Cloud Run API בשירותים המוגבלים בהתאם לסוג נקודת הקצה. פרטים נוספים זמינים במאמרים VPC Service Controls ב-Cloud Run functions וVPC Service Controls ב-Cloud Run.

שיטות מומלצות לפונקציות מרחוק

  • סינון מראש של הקלט: אם אפשר לסנן בקלות את הקלט לפני שהוא מועבר לפונקציה מרוחקת, סביר להניח שהשאילתה תהיה מהירה וזולה יותר.

  • שמירה על יכולת ההרחבה של פונקציית Cloud Run. המדרגיות היא פונקציה של מספר המכונות המינימלי, מספר המכונות המקסימלי ובו-זמניות.

    • במקרים שבהם אפשר, כדאי להשתמש בערך ברירת המחדל של המספר המקסימלי של המופעים של פונקציית Cloud Run.
    • שימו לב: אין מגבלת ברירת מחדל לפונקציות HTTP Cloud Run מהדור הראשון. כדי למנוע אירועי שינוי גודל בלתי מוגבלים בפונקציות HTTP Cloud Run מהדור הראשון במהלך בדיקות או בסביבת ייצור, מומלץ להגדיר מגבלה, למשל 3, 000.
  • כדי לשפר את הביצועים, כדאי לפעול לפי הטיפים האחרים לגבי פונקציות Cloud Run. יכול להיות ששאילתות של פונקציות מרחוק שמתקשרות עם פונקציית Cloud Run עם זמן אחזור גבוה ייכשלו בגלל פסק זמן.

  • מטמיעים את נקודת הקצה כדי להחזיר קוד תגובת HTTP ומטען ייעודי (payload) נכונים לתגובה שנכשלה.

    • כדי לצמצם את מספר הניסיונות החוזרים מ-BigQuery, צריך להשתמש בקודים של תגובות HTTP שאינם 408, ‏ 429, ‏ 500, ‏ 503 ו-504 לתגובה שנכשלה, ולוודא שכל החריגים נתפסים בקוד הפונקציה. אחרת, יכול להיות שמסגרת שירות ה-HTTP תחזיר באופן אוטומטי את קוד השגיאה 500 לכל חריגה שלא נתפסה. יכול להיות שעדיין תראו ניסיונות חוזרים של בקשות HTTP כש-BigQuery מנסה שוב לבצע מחיצת נתונים או שאילתה שנכשלו.

    • נקודת הקצה צריכה להחזיר מטען ייעודי (payload) של JSON בפורמט המוגדר לתגובה שנכשלה. למרות שזה לא חובה, זה עוזר ל-BigQuery להבחין בין תגובה שנכשלה בגלל ההטמעה של הפונקציה לבין תגובה שנכשלה בגלל התשתית של פונקציות Cloud Run או Cloud Run. במקרה האחרון, יכול להיות ש-BigQuery ינסה שוב עם מגבלה פנימית שונה.

מכסות

המידע הבא יעזור לכם לפתור בעיות שקשורות למכסות של פונקציות מרוחקות.

מספר מקסימלי של שאילתות בו-זמניות שמכילות פונקציות מרוחקות

‫BigQuery מחזיר את השגיאה הזו כשמספר השאילתות המקבילות שמכילות פונקציות מרוחקות חורג מהמגבלה.

מידע נוסף על מגבלות של פונקציות מרחוק זמין במאמר פונקציות מרחוק.

הודעת השגיאה

Exceeded rate limits: too many concurrent queries with remote functions for
this project

אפשר להגדיל את המכסה הזו. קודם כדאי לנסות את הפתרונות העקיפים והשיטות המומלצות.

אבחון

כדי לראות את המכסות על שאילתות בו-זמניות שמכילות פונקציות מרוחקות, אפשר לעיין במאמר מכסות על פונקציות מרוחקות.

רזולוציה