הרחבת Datastore באמצעות Cloud Functions (דור שני)

בעזרת פונקציות Cloud Run ו-Eventarc, אפשר לפרוס קוד לטיפול באירועים שמופעלים על ידי שינויים במסד הנתונים שלכם ב-Firestore במצב Datastore. כך תוכלו להוסיף פונקציונליות בצד השרת בלי להפעיל שרתים משלכם.

טריגרים במצב Datastore

‫Eventarc תומך בטריגרים הבאים של Firestore במצב Datastore, כדי לאפשר לכם ליצור פונקציות (דור שני) של Cloud Run שמקושרות לאירועים של Firestore במצב Datastore:

סוג האירוע הטריגר
google.cloud.datastore.entity.v1.created מופעל כשכותבים ישות בפעם הראשונה.
google.cloud.datastore.entity.v1.updated מופעל כשישות כבר קיימת וערך כלשהו שלה משתנה.
google.cloud.datastore.entity.v1.deleted מופעל כשמחקתם ישות.
google.cloud.datastore.entity.v1.written מופעל כשמופעלים created, ‏ updated או deleted.
google.cloud.datastore.entity.v1.created.withAuthContext זהה ל-created אבל כולל פרטי אימות.
google.cloud.datastore.entity.v1.updated.withAuthContext זהה ל-updated אבל כולל פרטי אימות.
google.cloud.datastore.entity.v1.deleted.withAuthContext זהה ל-deleted אבל כולל פרטי אימות.
google.cloud.datastore.entity.v1.written.withAuthContext זהה ל-written אבל כולל פרטי אימות.

טריגרים של אירועים במצב Datastore מגיבים רק לשינויים בישויות. עדכון של ישות במצב Datastore שבו הנתונים לא משתנים (פעולת כתיבה שלא עושה כלום) לא יוצר עדכון או אירוע כתיבה. אי אפשר ליצור אירועים רק לנכסים ספציפיים.

הכללת הקשר של האימות באירוע

כדי לכלול מידע נוסף על האימות של האירוע, משתמשים בטריגר של אירוע עם התוסף withAuthContext. התוסף הזה מוסיף מידע נוסף על הגורם שהפעיל את האירוע. הוא מוסיף את המאפיינים authtype ו-authid בנוסף למידע שמוחזר באירוע הבסיסי. מידע נוסף על ערכי מאפיינים זמין במאמר בנושא authcontext.

כתיבת פונקציה שמופעלת על ידי ישות

כדי לכתוב פונקציה שמגיבה לאירועים של Firestore במצב Datastore, צריך לציין את הפרטים הבאים במהלך הפריסה:

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

מסנני אירועים להפעלת טריגר

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

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

users/marie

אפשר להשתמש בתווים כלליים, * או **, כדי להגיב לשינויים בישויות שתואמות לתבנית. התו הכללי לחיפוש * מתאים לפלח אחד, והתו הכללי לחיפוש ** מתאים לאפס פלחים או יותר בדפוס.

במקרה של התאמות של פלח יחיד (*), אפשר גם להשתמש בקבוצה לחילוץ עם שם, כמו users/{userId}.

בטבלה הבאה מוצגות תבניות נתיבים תקינות:

דוגמת קוד תיאור
users/* או users/{userId} התאמה לכל הישויות מסוג users. לא תואם לרמה של ישויות צאצא כמו /users/marie/messages/33e2IxYBD9enzS50SJ68
users/** תואם לכל הישויות מסוג users ולכל הישויות הצאצאות, כמו /users/marie/messages/33e2IxYBD9enzS50SJ68

מידע נוסף על תבניות נתיבים זמין במאמר תבניות נתיבים ב-Eventarc.

הטריגר חייב תמיד להפנות לישות, גם אם משתמשים בתו כללי. דוגמאות:

  • הערך users/{userId=*}/{messages=*} לא תקין כי {messages=*} הוא מזהה סוג.

  • users/{userId=*}/{messages}/{messageId=*} is valid because {messageId=*} always points to an entity.

שימוש בתו בריחה (escape)

בסעיף הזה מתוארים מצבים שבהם צריך להשתמש בתו בריחה (escape) במזהי סוג ובמזהי ישות. הוספת תו בריחה מאפשרת למסנן האירועים לפרש את המזהה בצורה נכונה.

  • אם מזהה סוג או מזהה ישות כולל את התו ~ או /, צריך להוסיף תו בריחה למזהה במסנן האירועים. כדי להוסיף תו מיוחד למזהה, משתמשים בפורמט __escENCODED_ID__. מחליפים את ENCODED_ID במזהה סוג או במזהה ישות שבו כל התווים ~ ו-/ הוחלפו במזהי הקידוד שלהם, שהם:

    • ~: ~0
    • /: ~1

    לדוגמה, מזהה הסוג user/profile הופך ל-__escusers~1profile__. דוגמה לדפוס נתיב עם מזהה מסוג כזה היא __escusers~1profile__/{userId}

  • אם משתמשים במזהה הסוג או במזהה הישות של . או של .. במסנן האירועים, צריך להוסיף תו escape למזהה באופן הבא:

    • .: __esc~2__
    • ..: __esc~2~2__

    צריך להשתמש בתו בריחה (escape) עם התו . רק אם המזהה הוא בדיוק . או ... לדוגמה, לא צריך להשתמש בתו בריחה עבור מזהה הסוג customers.info.

  • אם סוג הישות או מזהה הישות הם ערך מספרי ולא ערך מחרוזתי, צריך להוסיף לפני המזהה את התו __idNUMERIC_VALUE__. לדוגמה, תבנית הנתיב של ישות מסוג 111 ומזהה ישות 222 היא __id111__/__id222__.

  • אם עברתם מ-Legacy Cloud Datastore ל-Firestore במצב Datastore, יכול להיות שמסד הנתונים שלכם מכיל מזהים מדור קודם בקידוד שאינו UTF8. חובה להשתמש בתו בריחה (escape) __bytesBASE64_ENCODING__ עם המזהים האלה. מחליפים את BASE64_ENCODING בקידוד Base-64 של המזהה. לדוגמה, תבנית הנתיב Task/{task} עם escape למזהה מסוג שאינו UTF8 Task הופכת ל-__bytesVGFzaw==__/{task}.

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

בדוגמה הבאה אפשר לראות איך מקבלים אירועים במצב Datastore. כדי לעבוד עם הנתונים שקשורים לאירוע, צריך לבדוק את השדות value ו-old_value.

  • value: אובייקט EntityResult שמכיל קובץ snapshot של ישות אחרי הפעולה. השדה הזה לא מאוכלס באירועי מחיקה.
  • old_value: אובייקט EntityResult שמכיל תמונת מצב של ישות לפני הפעולה. השדה הזה מאוכלס רק באירועי עדכון ומחיקה.

Java

מידע על התקנת ספריית הלקוח למצב Datastore ושימוש בה מופיע במאמר ספריות הלקוח של מצב Datastore. מידע נוסף מופיע במאמרי העזרה של Datastore mode Java API.

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

import com.google.cloud.functions.CloudEventsFunction;
import com.google.events.cloud.datastore.v1.EntityEventData;
import com.google.protobuf.InvalidProtocolBufferException;
import io.cloudevents.CloudEvent;
import java.util.logging.Logger;

public class Datastore implements CloudEventsFunction {
  private static final Logger logger = Logger.getLogger(Datastore.class.getName());

  @Override
  public void accept(CloudEvent event) throws InvalidProtocolBufferException {
    EntityEventData datastoreEventData = EntityEventData.parseFrom(event.getData().toBytes());

    logger.info("Function triggered by event on: " + event.getSource());
    logger.info("Event type: " + event.getType());

    logger.info("Old value:");
    logger.info(datastoreEventData.getOldValue().toString());

    logger.info("New value:");
    logger.info(datastoreEventData.getValue().toString());
  }
}

הכללת יחסי התלות של ה-proto במקור

צריך לכלול את הקובץ מצב Datastore data.proto בספריית קובצי המקור של הפונקציה. הקובץ הזה מייבא את קובצי ה-proto הבאים, שצריך לכלול גם אותם בספריית קובצי המקור:

משתמשים באותה מבנה ספריות עבור התלויות. לדוגמה, אפשר להציב את struct.proto בתוך google/protobuf.

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

מאפייני אירוע

כל אירוע כולל מאפייני נתונים שכוללים מידע על האירוע, כמו השעה שבה האירוע הופעל. ‫Firestore במצב Datastore מוסיף נתונים נוספים על מסד הנתונים והישות שקשורים לאירוע. כך ניגשים למאפיינים האלה:

Java
logger.info("Event time " + event.getTime());
logger.info("Event project: " + event.getExtension("project"));
logger.info("Event location: " + event.getExtension("location"));
logger.info("Database name: " + event.getExtension("database"));
logger.info("Database namespace: " + event.getExtension("namespace"));
logger.info("Database entity: " + event.getExtension("entity"));
// For withAuthContext events
logger.info("Auth information: " + event.getExtension("authid"));
logger.info("Auth information: " + event.getExtension("authtype"));

פריסת פונקציה

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

אפשר לפרוס פונקציה באמצעות ה-CLI של gcloud או מסוף Google Cloud . בדוגמה הבאה מוצגת פריסה באמצעות ה-CLI של gcloud. פרטים על פריסה באמצעות מסוף Google Cloud זמינים במאמר פריסת פונקציות Cloud Run.

  1. במסוף Google Cloud , מפעילים את Cloud Shell.

    הפעלת Cloud Shell

    בחלק התחתון של Google Cloud המסוף יתחיל סשן של Cloud Shell ותופיע הודעה של שורת הפקודה. Cloud Shell היא סביבת מעטפת שבה ה-CLI של Google Cloud מותקן ומוגדרים ערכים לפרויקט הקיים. הסשן יופעל תוך כמה שניות.

  2. כדי לפרוס פונקציה, משתמשים בפקודה gcloud functions deploy:

    gcloud functions deploy FUNCTION_NAME \
    --gen2 \
    --region=FUNCTION_LOCATION \
    --trigger-location=TRIGGER_LOCATION \
    --runtime=RUNTIME \
    --source=SOURCE_LOCATION \
    --entry-point=CODE_ENTRYPOINT \
    --trigger-event-filters="type=EVENT_FILTER_TYPE" \
    --trigger-event-filters="database=DATABASE" \
    --trigger-event-filters="namespace=NAMESPACE" \
    --trigger-event-filters-path-pattern="entity=ENTITY_OR_PATH"
    

    הארגומנט הראשון, FUNCTION_NAME, הוא שם לפונקציה שפרסתם. שם הפונקציה צריך להתחיל באות ואחריה עד 62 אותיות, מספרים, מקפים או קווים תחתונים, ולהסתיים באות או במספר. מחליפים את FUNCTION_NAME בשם פונקציה תקין. לאחר מכן, מוסיפים את הדגלים הבאים:

    • הדגל --gen2 מציין שרוצים לפרוס ל-Cloud Run Functions (דור שני). אם לא מציינים את האפשרות הזו, הפריסה מתבצעת ב-Cloud Run functions (דור ראשון).

    • הדגל --region=FUNCTION_LOCATION מציין את האזור שבו הפונקציה תופעל.

      כדי למקסם את הקרבה, מגדירים את FUNCTION_LOCATION לאזור שקרוב למסד הנתונים של Firestore. אם מסד הנתונים שלכם ב-Firestore נמצא במיקום במספר אזורים, צריך להגדיר את הערך us-central1 למסדי נתונים ב-nam5 ואת הערך europe-west4 למסדי נתונים ב-eur3. למיקומי Firestore אזוריים, צריך להגדיר את אותו אזור.

    • הדגל --trigger-location=TRIGGER_LOCATION מציין את המיקום של הטריגר. צריך להגדיר את TRIGGER_LOCATION למיקום של מסד הנתונים במצב Datastore.

    • הדגל --runtime=RUNTIME מציין באיזו שפת זמן ריצה הפונקציה משתמשת. פונקציות Cloud Run תומכות בכמה סביבות זמן ריצה. מידע נוסף זמין במאמר בנושא סביבות זמן ריצה. מגדירים את RUNTIME לסביבת זמן ריצה נתמכת.

    • הדגל --source=SOURCE_LOCATION מציין את המיקום של קוד המקור של הפונקציה. פרטים נוספים מופיעים במקורות המידע הבאים:

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

    • הדגל --entry-point=CODE_ENTRYPOINT מציין את נקודת הכניסה לפונקציה בקוד המקור. זה הקוד שהפונקציה מריצה כשהיא פועלת. צריך להגדיר את CODE_ENTRYPOINT לשם של פונקציה או לשם מלא של מחלקה שקיימים בקוד המקור. מידע נוסף זמין במאמר נקודת כניסה לפונקציה.

    • הדגלים --trigger-event-filters מגדירים את מסנן האירועים שכולל את סוג הטריגר ואת הישות או הנתיב שמפעילים את האירועים. מגדירים את ערכי המאפיינים הבאים כדי להגדיר את מסנן האירועים:

      • type=EVENT_FILTER_TYPE: Firestore תומך בסוגי האירועים הבאים:

        • google.cloud.datastore.entity.v1.created: האירוע נשלח כשמבצעים כתיבה של ישות בפעם הראשונה.
        • google.cloud.datastore.entity.v1.updated: האירוע נשלח כשישות כבר קיימת וערך כלשהו שלה השתנה.
        • google.cloud.datastore.entity.v1.deleted: האירוע נשלח כשמחקתם ישות.
        • google.cloud.datastore.entity.v1.written: האירוע נשלח כשיוצרים, מעדכנים או מוחקים ישות.
        • google.cloud.datastore.entity.v1.created.withAuthContext: האירוע נשלח כשמסמך נכתב בפעם הראשונה והוא כולל פרטי אימות נוספים
        • google.cloud.datastore.entity.v1.updated.withAuthContext: האירוע נשלח אם מסמך כבר קיים וערך כלשהו בו השתנה. כולל פרטי אימות נוספים
        • google.cloud.datastore.entity.v1.deleted.withAuthContext: האירוע נשלח כשמסמך נמחק. כולל פרטי אימות נוספים
        • google.cloud.datastore.entity.v1.written.withAuthContext: האירוע נשלח כשמסמך נוצר, מתעדכן או נמחק. כולל פרטי אימות נוספים

        מגדירים את EVENT_FILTER_TYPE לאחד מסוגי האירועים האלה.

      • database=DATABASE: מסד הנתונים של Firestore. בשם מסד הנתונים שמוגדר כברירת מחדל, מגדירים את DATABASE ל-(default).

      • namespace=NAMESPACE: מרחב השמות של מסד הנתונים. בשדה של שם מסד הנתונים שמוגדר כברירת מחדל, מגדירים את NAMESPACE ל-(default). מסירים את הדגל כדי להתאים לכל מרחב שמות.

      • entity=ENTITY_OR_PATH: הנתיב למסד הנתונים שמפעיל אירועים כשנתונים נוצרים, מתעדכנים או נמחקים. הערכים הקבילים לENTITY_OR_PATH הם:

        • שווה; לדוגמה, --trigger-event-filters="entity='users/marie'"
        • דפוס נתיב; לדוגמה, --trigger-event-filters-path-pattern="entity='users/*'". מידע נוסף זמין במאמר הסבר על דפוסי נתיבים.

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

      במאמרי העזרה בנושא gcloud functions deploy מופיע מידע מלא על פקודת הפריסה והדגלים שלה.

פריסות לדוגמה

בדוגמאות הבאות מוצגות פריסות באמצעות Google Cloud CLI.

פריסת פונקציה למסד נתונים באזור us-west2:

gcloud functions deploy gcfv2-trigger-datastore-node \
--gen2 \
--region=us-west2 \
--trigger-location=us-west2 \
--runtime=nodejs18 \
--source=gs://example_bucket-1/datastoreEventFunction.zip \
--entry-point=makeUpperCase \
--trigger-event-filters=type=google.cloud.datastore.entity.v1.written \
--trigger-event-filters=database='(default)' \
--trigger-event-filters-path-pattern="entity='messages/{pushId}'"

פריסת פונקציה למסד נתונים באזור nam5 עם כמה אזורים:

gcloud functions deploy gcfv2-trigger-datastore-python \
--gen2 \
--region=us-central1 \
--trigger-location=nam5 \
--runtime=python311 \
--source=gs://example_bucket-1/datastoreEventFunction.zip \
--entry-point=make_upper_case \
--trigger-event-filters=type=google.cloud.datastore.entity.v1.written.withAuthContext \
--trigger-event-filters=database='(default)' \
--trigger-event-filters-path-pattern="entity='messages/{pushId}'"

מגבלות

שימו לב למגבלות הבאות לגבי טריגרים של Firestore לפונקציות Cloud Run:

  • פונקציות Cloud Run (דור ראשון) דורשות מסד נתונים קיים מסוג '(default)' במצב Firestore Native. הוא לא תומך במסדי נתונים בעלי שם ב-Firestore או במצב Datastore. במקרים כאלה, צריך להשתמש בפונקציות Cloud Run (דור שני) כדי להגדיר אירועים.
  • הגדרה של פרויקטים שונים באמצעות פונקציות Cloud Run וטריגר של Firestore היא מגבלה. כדי להגדיר פונקציות Cloud Run להפעלה על ידי Firestore, הן צריכות להיות באותו פרויקט.
  • אנחנו לא מתחייבים לגבי סדר ההזמנה. שינויים מהירים יכולים להפעיל קריאות לפונקציות בסדר לא צפוי.
  • האירועים מועברים לפחות פעם אחת, אבל אירוע יחיד עשוי להוביל להפעלות מרובות של פונקציות. אל תסתמכו על מנגנונים של 'פעם אחת בלבד', ותכתבו פונקציות אידמפוטנטיות.
  • Firestore במצב Datastore דורש פונקציות Cloud Run (דור שני). פונקציות Cloud Run (דור ראשון) לא תומכות במצב Datastore.
  • טריגר משויך למסד נתונים יחיד. אי אפשר ליצור טריגר שתואם לכמה מסדי נתונים.
  • מחיקה של מסד נתונים לא תגרום למחיקה אוטומטית של טריגרים במסד הנתונים הזה. הטריגר מפסיק להעביר אירועים אבל ממשיך להתקיים עד שמוחקים את הטריגר.
  • אם אירוע תואם חורג מהגודל המקסימלי של הבקשה, יכול להיות שהאירוע לא יועבר לפונקציות Cloud Run (דור ראשון).
    • אירועים שלא נמסרו בגלל גודל הבקשה מתועדים ביומני הפלטפורמה ונספרים בשימוש ביומן של הפרויקט.
    • אפשר למצוא את היומנים האלה בכלי Logs Explorer עם ההודעה 'לא ניתן למסור את האירוע לפונקציית Cloud בגלל שהגודל חורג מהמגבלה של דור ראשון...' ברמת חומרה error. שם הפונקציה מופיע בשדה functionName. אם השדה receiveTimestamp עדיין נמצא בטווח של שעה מהזמן הנוכחי, אפשר להסיק את תוכן האירוע בפועל על ידי קריאת המסמך המדובר עם תמונת מצב לפני ואחרי חותמת הזמן.
    • כדי להימנע מקצב צעדים כזה, אתם יכולים:
      • העברה ושדרוג לפונקציות Cloud Run (דור שני)
      • הקטנת המסמך
      • מחיקת פונקציות Cloud Run הרלוונטיות
    • אפשר להשבית את הרישום עצמו באמצעות החרגות, אבל חשוב לזכור שהאירועים הבעייתיים עדיין לא יועברו.

מיקומים של Eventarc ו-Firestore במצב Datastore

‫Eventarc לא תומך במספר אזורים לטריגרים של אירועים ב-Firestore, אבל עדיין אפשר ליצור טריגרים למסדי נתונים של Firestore במיקומים מרובי-אזורים. ‫Eventarc ממפה מיקומים מרובי-אזורים של Firestore לאזורים הבאים של Eventarc:

‫Firestore במספר אזורים אזור Eventarc
nam5 us-central1
eur3 europe-west4

המאמרים הבאים