הרחבת 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 תקף כי {messageId=*} תמיד מצביע על ישות.

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

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

  • אם מזהה סוג או מזהה ישות כולל את התו ~ או /, צריך להוסיף תו escape למזהה במסנן האירועים. כדי להוסיף מקף ל-ID, משתמשים בפורמט __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. צריך להוסיף את התו __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

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