טיפול באירועים במחזור החיים של התוסף

התוסף יכול לכלול פונקציות של Cloud Tasks שמופעלות כשמופע של תוסף עובר דרך אחת מהאפשרויות הבאות אירועים במחזור החיים:

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

אחד מהתרחישים לדוגמה החשובים ביותר לשימוש בתכונה הזו הוא מילוי חוסרים בנתונים. עבור למשל, נניח שאתם בונים תוסף שיוצר תצוגות מקדימות של תמונות ממוזערות מהתמונות שהועלו לקטגוריה של Cloud Storage. הפעולה העיקרית של התוסף יבוצע בפונקציה שמופעלת על ידי האירוע onFinalize Cloud Storage. עם זאת, רק תמונות שהועלו אחרי התקנת התוסף, ייספרו עבר עיבוד. על ידי הכללה בתוסף שלך, פונקציה המופעלת על ידי onInstallבמחזור החיים, אפשר גם ליצור תצוגות מקדימות של תמונות ממוזערות תמונות קיימות כשהתוסף מותקן.

עוד תרחישים לדוגמה של טריגרים של אירועים במחזור החיים כוללים:

  • אוטומציה של ההגדרה לאחר ההתקנה (יצירת רשומות במסד נתונים, הוספה לאינדקס וכו')
  • אם צריך לפרסם שינויים שלא תואמים לאחור, מבצעים העברה אוטומטית הנתונים בתאריך העדכון

גורמים מטפלים באירועים במחזור החיים לטווח קצר

אם המשימה יכולה לפעול במלואה בתוך משך זמן מקסימלי Cloud Functions (9) באמצעות API מהדור הראשון), אתם יכולים לכתוב את האירוע של מחזור החיים שלכם handler כפונקציה יחידה שמופעלת באירוע onDispatch בתור המשימה:

export const myTaskFunction = functions.tasks.taskQueue()
  .onDispatch(async () => {
    // Complete your lifecycle event handling task.
    // ...

    // When processing is complete, report status to the user (see below).
  });

לאחר מכן, בקובץ extension.yaml של התוסף, מבצעים את הפעולות הבאות:

  1. רושמים את הפונקציה כמשאב תוסף באמצעות קבוצת הנכסים taskQueueTrigger. אם מגדירים את taskQueueTrigger במפה הריקה ({}), התוסף יקצה תור Cloud Tasks באמצעות ברירת המחדל הגדרות; אפשר גם לכוונן את ההגדרות האלה.

    resources:
      - name: myTaskFunction
        type: firebaseextensions.v1beta.function
        description: >-
          Describe the task performed when the function is triggered by a lifecycle
          event
        properties:
          location: ${LOCATION}
          taskQueueTrigger: {}
    
  2. רושמים את הפונקציה כמתנהלת לאירוע אחד או יותר במחזור החיים:

    resources:
      - ...
    lifecycleEvents:
      onInstall:
        function: myTaskFunction
        processingMessage: Resizing your existing images
      onUpdate:
        function: myOtherTaskFunction
        processingMessage: Setting up your extension
      onConfigure:
        function: myOtherTaskFunction
        processingMessage: Setting up your extension
    
    

    אפשר לרשום פונקציות לכל אחד מהאירועים הבאים: onInstall, onUpdate ו-onConfigure. כל האירועים האלה הם אופציונליים.

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

    לדוגמה, מוסיפים פרמטר כמו:

    params:
      - param: DO_BACKFILL
        label: Backfill existing images
        description: >
          Should existing, unresized images in the Storage bucket be resized as well?
        type: select
        options:
          - label: Yes
            value: true
          - label: No
            value: false
    

    ובפונקציה, אם הפרמטר מוגדר ל-false, צאו מוקדם יותר:

    export const myTaskFunction = functions.tasks.taskQueue()
      .onDispatch(async () => {
        if (!process.env.DO_BACKFILL) {
          await runtime.setProcessingState(
            "PROCESSING_COMPLETE",
            "Existing images were not resized."
          );
          return;
        }
        // Complete your lifecycle event handling task.
        // ...
      });
    

ביצוע משימות ממושכות

אם לא ניתן להשלים את המשימה בטווח הזמן המקסימלי של Cloud Functions, לחלק את המשימה לתתי-משימות ולבצע כל תת-משימה ברצף על ידי הוספה שלה לתור משימות עם ה-TaskQueue.enqueue() של ה-Admin SDK .

לדוגמה, נניח שאתם רוצים לבצע מילוי חוסרים (backfill) של נתונים של Cloud Firestore. אפשר לפצל את אוסף המסמך למקטעי נתונים באמצעות סמנים של שאילתה. אחרי עיבוד של מקטע, מעבירים את ההיסט ההתחלתי ומעבירים עוד של הפעלת הפונקציה כפי שמוצג בהמשך:

import { getFirestore } from "firebase-admin/firestore";
import { getFunctions } from "firebase-admin/functions";

exports.backfilldata = functions.tasks.taskQueue().onDispatch(async (data) => {
  // When a lifecycle event triggers this function, it doesn't pass any data,
  // so an undefined offset indicates we're on our first invocation and should
  // start at offset 0. On subsequent invocations, we'll pass an explicit
  // offset.
  const offset = data["offset"] ?? 0;

  // Get a batch of documents, beginning at the offset.
  const snapshot = await getFirestore()
    .collection(process.env.COLLECTION_PATH)
    .startAt(offset)
    .limit(DOCS_PER_BACKFILL)
    .get();
  // Process each document in the batch.
  const processed = await Promise.allSettled(
    snapshot.docs.map(async (documentSnapshot) => {
      // Perform the processing.
    })
  );

  // If we processed a full batch, there are probably more documents to
  // process, so enqueue another invocation of this function, specifying
  // the offset to start with.
  //
  // If we processed less than a full batch, we're done.
  if (processed.length == DOCS_PER_BACKFILL) {
    const queue = getFunctions().taskQueue(
      "backfilldata",
      process.env.EXT_INSTANCE_ID
    );
    await queue.enqueue({
      offset: offset + DOCS_PER_BACKFILL,
    });
  } else {
      // Processing is complete. Report status to the user (see below).
  }
});

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

סטטוס דיווח

כשכל פונקציות העיבוד מסתיימות, בהצלחה או עם שגיאה, דווח על סטטוס המשימה באמצעות זמן הריצה של התוסף של ה-Admin SDK שיטות. המשתמשים יכולים לראות את הסטטוס הזה בדף הפרטים של התוסף במסוף Firebase.

השלמת בהצלחה ושגיאות לא חמורות

כדי לדווח על שגיאות שהושלמו בהצלחה ועל שגיאות לא חמורות (שגיאות שלא מנתקים את התוסף למצב לא פונקציונלי), צריך להשתמש ב-Admin SDK שיטת זמן ריצה של תוסף setProcessingState():

import { getExtensions } from "firebase-admin/extensions";

// ...

getExtensions().runtime().setProcessingState(processingState, message);

אפשר להגדיר את המצבים הבאים:

מצבים לא חמורים
PROCESSING_COMPLETE

משתמשים בה כדי לדווח על השלמת משימה. דוגמה:

getExtensions().runtime().setProcessingState(
  "PROCESSING_COMPLETE",
  `Backfill complete. Successfully processed ${numSuccess} documents.`
);
PROCESSING_WARNING

משמש לדיווח על הצלחה חלקית. דוגמה:

getExtensions().runtime().setProcessingState(
  "PROCESSING_WARNING",
  `Backfill complete. ${numSuccess} documents processed successfully.`
    + ` ${numFailed} documents failed to process. ${listOfErrors}.`
    + ` ${instructionsToFixTheProblem}`
);
PROCESSING_FAILED

משמש לדיווח על שגיאות שמונעות את השלמת המשימה, אבל לא לא ניתן להשתמש בתוסף. דוגמה:

getExtensions().runtime().setProcessingState(
  "PROCESSING_FAILED",
  `Backfill failed. ${errorMsg} ${optionalInstructionsToFixTheProblem}.`
);

כדי לדווח על שגיאות שכן לא מאפשרות את השימוש בתוסף, התקשרו: setFatalError().

NONE

משמש למחיקת סטטוס המשימה. אפשר להשתמש באפשרות הזו כדי למחוק את הודעת הסטטוס מהמסוף (לדוגמה, אחרי שחולף זמן מה מאז ההגדרה של PROCESSING_COMPLETE). דוגמה:

getExtensions().runtime().setProcessingState("NONE");

שגיאות חמורות

אם מתרחשת שגיאה שמונעת מהתוסף לפעול – לדוגמה, משימת הגדרה נדרשת נכשלה – אפשר לדווח על השגיאה הקטלנית באמצעות setFatalError():

import { getExtensions } from "firebase-admin/extensions";

// ...

getExtensions().runtime().setFatalError(`Post-installation setup failed. ${errorMessage}`);

שינוי ההגדרות של תור המשימות

אם מגדירים את הנכס taskQueueTrigger לערך {}, התוסף: הקצאת תור ב-Cloud Tasks עם הגדרות ברירת המחדל כשתוסף מותקן. לחלופין, אפשר לכוונן את בו-זמניות (concurrency) בתור המשימות של הפעולות המותרות והניסיונות החוזרים של הפעולה על ידי ציון ערכים ספציפיים:

resources:
  - name: myTaskFunction
    type: firebaseextensions.v1beta.function
    description: >-
      Perform a task when triggered by a lifecycle event
    properties:
      location: ${LOCATION}
      taskQueueTrigger:
        rateLimits:
          maxConcurrentDispatches: 1000
          maxDispatchesPerSecond: 500
        retryConfig:
          maxAttempts: 100  # Warning: setting this too low can prevent the function from running
          minBackoffSeconds: 0.1
          maxBackoffSeconds: 3600
          maxDoublings: 16
lifecycleEvents:
  onInstall: 
    function: myTaskFunction
    processingMessage: Resizing your existing images
  onUpdate:
    function: myTaskFunction
    processingMessage: Setting up your extension
  onConfigure:
    function: myOtherTaskFunction
    processingMessage: Setting up your extension

פרטים על הפרמטרים האלה מופיעים במאמר הגדרת תורים ב-Cloud Tasks במסמכי העזרה של Google Cloud.

אל תנסו לציין פרמטרים של תור משימות על ידי העברתם אל taskQueue(). המערכת מתעלמת מההגדרות האלה לטובת התצורה ב-extension.yaml את ברירות המחדל של ההגדרות האישיות.

לדוגמה, הכתובת הזו לא תעבוד:

export const myBrokenTaskFunction = functions.tasks
  // DON'T DO THIS IN AN EXTENSION! THESE SETTINGS ARE IGNORED.
  .taskQueue({
    retryConfig: {
      maxAttempts: 5,
      minBackoffSeconds: 60,
    },
    rateLimits: {
      maxConcurrentDispatches: 1000,
      maxDispatchesPerSecond: 10,
    },
  })
  .onDispatch(
    // ...
  );

הדרך היחידה להגדיר את הנכס taskQueueTrigger בextension.yaml היא בתורי משימות של תוסף.

דוגמאות

הקוד הרשמי של storage-resize-images, firestore-bigquery-export, ו-firestore-translate-text התוספים משתמשים בגורמים מטפלים באירועים במחזור החיים כדי למלא את החוסרים (backfill) בנתונים.