ניהול פונקציות


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

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

כדי לפרוס את הפונקציות, מריצים את הפקודה Firebase ב-CLI:

firebase deploy --only functions

כברירת מחדל, ה-CLI של Firebase פורס את כל הפונקציות בתוך המקור בו-זמנית. אם בפרויקט יש יותר מ-5 פונקציות, מומלץ להשתמש בדגל --only עם שמות הפונקציות הספציפיות כדי לפרוס רק את הפונקציות שערכתם. פריסה של פונקציות ספציפיות בדרך הזו מזרזת את תהליך הפריסה ומונעת מצב שבו תגיעו למכסות הפריסה. לדוגמה:

firebase deploy --only functions:addMessage,functions:makeUppercase

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

במאמרי העזרה של CLI של Firebase יש רשימה מלאה של הפקודות הזמינות.

כברירת מחדל, ה-CLI של Firebase מחפש את קוד המקור בתיקייה functions/. אם אתם מעדיפים, תוכלו לארגן פונקציות בבסיס קוד או במספר קבוצות של קבצים.

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

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

  • באופן מפורש ב-CLI של Firebase באמצעות functions:delete
  • בצורה מפורשת במסוף Google Cloud.
  • באופן משתמע על ידי הסרת הפונקציה מהמקור לפני הפריסה.

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

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

# Delete all functions that match the specified name in all regions.
firebase functions:delete myFunction
# Delete a specified function running in a specific region.
firebase functions:delete myFunction --region us-east-1
# Delete more than one function
firebase functions:delete myFunction myOtherFunction
# Delete a specified functions group.
firebase functions:delete groupA
# Bypass the confirmation prompt.
firebase functions:delete myFunction --force

כשמשתמשים במחיקה משתמעת של פונקציות, firebase deploy מנתח את המקור ומסיר מהייצור את כל הפונקציות שהוסרו מהקובץ.

שינוי השם, האזור או הטריגר של פונקציה

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

שינוי שם של פונקציה

כדי לשנות שם של פונקציה, יוצרים גרסה חדשה עם השם החדש של הפונקציה במקור ולאחר מכן מריצים שתי פקודות פריסה נפרדות. הפקודה הראשונה פורסת את הפונקציה עם השם החדש, והפקודה השנייה מסירה את הגרסה שפרסמתם בעבר. לדוגמה, אם יש לכם פונקציה של Node.js בשם webhook שאתם רוצים לשנות ל-webhookNew, עליכם לשנות את הקוד באופן הבא:

// before
const functions = require('firebase-functions/v1');

exports.webhook = functions.https.onRequest((req, res) => {
    res.send("Hello");
});

// after
const functions = require('firebase-functions/v1');

exports.webhookNew = functions.https.onRequest((req, res) => {
    res.send("Hello");
});

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

# Deploy new function called webhookNew
firebase deploy --only functions:webhookNew

# Wait until deployment is done; now both webhookNew and webhook are running

# Delete webhook
firebase functions:delete webhook

שינוי האזור או האזורים של פונקציה

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

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

לדוגמה, אם יש פונקציה בשם webhook שנמצאת כרגע באזור הפונקציות שמוגדר כברירת מחדל us-central1, ואתם רוצים להעביר אותה אל asia-northeast1, צריך קודם לשנות את קוד המקור כדי לשנות את שם הפונקציה ולשנות את האזור.

// before
const functions = require('firebase-functions/v1');

exports.webhook = functions
    .https.onRequest((req, res) => {
            res.send("Hello");
    });

// after
const functions = require('firebase-functions/v1');

exports.webhookAsia = functions
    .region('asia-northeast1')
    .https.onRequest((req, res) => {
            res.send("Hello");
    });

לאחר מכן מבצעים את הפריסה באמצעות הרצה של:

firebase deploy --only functions:webhookAsia

עכשיו יש שתי פונקציות זהות שפועלות: webhook פועלת ב-us-central1 ו-webhookAsia פועלת ב-asia-northeast1.

לאחר מכן מוחקים את webhook:

firebase functions:delete webhook

עכשיו יש רק פונקציה אחת – webhookAsia, שפועלת ב-asia-northeast1.

שינוי סוג הטריגר של פונקציה

כשמפתחים את הפריסה של Cloud Functions for Firebase לאורך זמן, יכול להיות שתצטרכו לשנות את סוג הטריגר של פונקציה מסיבות שונות. לדוגמה, יכול להיות שתרצו לשנות סוג של אירוע Firebase Realtime Database או Cloud Firestore לסוג אחר.

אי אפשר לשנות את סוג האירוע של פונקציה רק על ידי שינוי קוד המקור והרצה של firebase deploy. כדי למנוע שגיאות, כך תוכלו לשנות את סוג הטריגר של פונקציה:

  1. משנים את קוד המקור כך שיכלול פונקציה חדשה עם סוג הטריגר הרצוי.
  2. פורסים את הפונקציה, והתוצאה היא הפעלה זמנית של הפונקציה הישנה והחדשה.
  3. מוחקים באופן מפורש את הפונקציה הישנה מהייצור באמצעות ה-CLI Firebase.

לדוגמה, אם יש לכם פונקציית Node.js בשם objectChanged עם סוג האירוע הקודם onChange, ואתם רוצים לשנות אותו ל-onFinalize, קודם צריך לשנות את שם הפונקציה ולערוך אותה כך שתכלול את סוג האירוע onFinalize.

// before
const functions = require('firebase-functions/v1');

exports.objectChanged = functions.storage.object().onChange((object) => {
    return console.log('File name is: ', object.name);
});

// after
const functions = require('firebase-functions/v1');

exports.objectFinalized = functions.storage.object().onFinalize((object) => {
    return console.log('File name is: ', object.name);
});

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

# Create new function objectFinalized
firebase deploy --only functions:objectFinalized

# Wait until deployment is done; now both objectChanged and objectFinalized are running

# Delete objectChanged
firebase functions:delete objectChanged

הגדרת אפשרויות בסביבת זמן הריצה

באמצעות Cloud Functions for Firebase אפשר לבחור אפשרויות זמן ריצה, כמו גרסת זמן הריצה של Node.js וזמן קצוב לתפוגה לכל פונקציה, הקצאת זיכרון ומכונות מינימום/מקסימליות של פונקציות.

מומלץ להגדיר את האפשרויות האלה (למעט גרסת Node.js) באובייקט תצורה בתוך קוד הפונקציה. האובייקט RuntimeOptions הוא מקור האמת של אפשרויות זמן הריצה של הפונקציה, והוא יחליף את האפשרויות שהוגדרו בכל שיטה אחרת (למשל דרך מסוף Google Cloud או ה-CLI של gcloud).

אם בתהליך הפיתוח יש הגדרה ידנית של אפשרויות זמן ריצה דרך מסוף Google Cloud או ה-CLI של gcloud, ואתם לא רוצים לשנות את הערכים האלה בכל פריסה, צריך להגדיר את האפשרות preserveExternalChanges ל-true. כשהאפשרות הזו מוגדרת ל-true, מערכת Firebase משלבת את אפשרויות סביבת זמן הריצה שמוגדרות בקוד עם ההגדרות של הגרסה הנוכחית של הפונקציה שנפרסה, לפי סדר העדיפויות הבא:

  1. האפשרות מוגדרת בקוד הפונקציות: שינוי של שינויים חיצוניים.
  2. האפשרות מוגדרת ל-RESET_VALUE בקוד הפונקציות: ביטול שינויים חיצוניים בערך ברירת המחדל.
  3. האפשרות לא מוגדרת בקוד הפונקציות, אבל היא מוגדרת בפונקציה הנוכחית שנפרסה: משתמשים באפשרות שצוינה בפונקציה שנפרסה.

ברוב התרחישים לא מומלץ להשתמש באפשרות preserveExternalChanges: true, כי הקוד כבר לא יהיה המקור המלא האמיתי לאפשרויות של סביבת זמן הריצה לפונקציות. אם אתם משתמשים בה, תוכלו לבדוק את ההגדרה המלאה של הפונקציה במסוף Google Cloud או באמצעות ה-CLI של gcloud.

הגדרת גרסת Node.js

ה-SDK Firebase של Cloud Functions מאפשר לבחור זמן ריצה של Node.js. אתם יכולים להפעיל את כל הפונקציות בפרויקט באופן בלעדי בסביבת זמן הריצה שתואם לאחת מגרסאות Node.js הנתמכות הבאות:

  • Node.js 20 (תצוגה מקדימה)
  • Node.js 18
  • Node.js 16
  • Node.js 14

כדי להגדיר את גרסת Node.js:

אפשר להגדיר את הגרסה בשדה engines בקובץ package.json שנוצר בתיקייה functions/ במהלך האיפוס. לדוגמה, כדי להשתמש רק בגרסה 18, עורכים את השורה הזו ב-package.json:

  "engines": {"node": "18"}

אם אתם משתמשים במנהל החבילות Yarn או שיש לכם דרישות ספציפיות אחרות לשדה engines, תוכלו להגדיר את סביבת זמן הריצה של ה-SDK של Firebase עבור Cloud Functions ב-firebase.json במקום זאת:

  {
    "functions": {
      "runtime": "nodejs18" // or nodejs14, nodejs16 or nodejs20
    }
  }

ה-CLI משתמש בערך שמוגדר ב-firebase.json במקום בערך או בטווח שתגדירו בנפרד ב-package.json.

שדרוג זמן הריצה של Node.js

כדי לשדרג את זמן הריצה של Node.js:

  1. חשוב לוודא שהפרויקט נכלל בתוכנית התמחור של Bllaze.
  2. אתם צריכים לוודא שאתם משתמשים ב-CLI של Firebase מגרסה 11.18.0 ואילך.
  3. משנים את הערך engines בקובץ package.json שנוצר בספרייה functions/ במהלך האתחול. לדוגמה, אם משדרגים מגרסה 16 לגרסה 18, הרשומה צריכה להיראות כך: "engines": {"node": "18"}
  4. אפשר גם לבדוק את השינויים באמצעות Firebase Local Emulator Suite.
  5. פורסים מחדש את כל הפונקציות.

שליטה בהתנהגות ההתאמה לעומס

כברירת מחדל, Cloud Functions for Firebase משתנה בהתאם למספר הבקשות הנכנסות, כך שיכול להיות שהוא יגיע לאפס מכונות בזמנים של תנועה מופחתת. עם זאת, אם האפליקציה דורשת זמן אחזור קצר ואתם רוצים להגביל את מספר ההפעלות במצב התחלתי (cold start), אפשר לשנות את התנהגות ברירת המחדל הזו על ידי ציון מספר מינימלי של מכונות בקונטיינרים שיישמרו במצב ביניים (warm start) ויהיו מוכנות למלא בקשות.

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

הפחתת מספר ההפעלות במצב התחלתי (cold start)

כדי להגדיר מספר מינימלי של מכונות לפונקציה בקוד המקור, משתמשים ב-method runWith. ה-method הזה מקבל אובייקט JSON שתואם לממשק RuntimeOptions, שמגדיר את הערך של minInstances. לדוגמה, הפונקציה הזו מגדירה 5 מכונות לפחות לשימור בזיכרון:

exports.getAutocompleteResponse = functions
    .runWith({
      // Keep 5 instances warm for this latency-critical function
      minInstances: 5,
    })
    .https.onCall((data, context) => {
      // Autocomplete a user's search term
    });

הנה כמה דברים שכדאי להביא בחשבון כשמגדירים ערך ל-minInstances:

  • אם Cloud Functions for Firebase יעלה את האפליקציה מעל ההגדרה של minInstances, בכל מכונה תופיע הפעלה במצב התחלתי (cold start) מעל הסף הזה.
  • הפעלות ראשוניות (cold start) משפיעות בצורה החמורה ביותר על אפליקציות עם תנועה חדה. אם לאפליקציה יש נפח תנועה גבוה ומגדירים ערך של minInstances גבוה מספיק כדי שהתחלות קרות יצטמצמו בכל עלייה בתנועה, זמן האחזור יצטמצם באופן משמעותי. באפליקציות עם תנועה קבועה, הפעלה במצב התחלתי לא צפויה להשפיע באופן משמעותי על הביצועים.
  • הגדרת מספר מינימלי של מכונות יכולה להיות הגיונית בסביבות ייצור, אבל בדרך כלל כדאי להימנע מכך בסביבות בדיקה. כדי לשנות את קנה המידה לאפס בפרויקט הבדיקה, אבל עדיין לצמצם את ההפעלה במצב התחלתי בפרויקט הייצור, אפשר להגדיר את minInstances על סמך משתנה הסביבה FIREBASE_CONFIG:

    // Get Firebase project id from `FIREBASE_CONFIG` environment variable
    const envProjectId = JSON.parse(process.env.FIREBASE_CONFIG).projectId;
    
    exports.renderProfilePage = functions
        .runWith({
          // Keep 5 instances warm for this latency-critical function
          // in production only. Default to 0 for test projects.
          minInstances: envProjectId === "my-production-project" ? 5 : 0,
        })
        .https.onRequest((req, res) => {
          // render some html
        });
    

הגבלת מספר המופעים המקסימלי של פונקציה

כדי להגדיר את מספר המכונות המקסימלי בקוד המקור של הפונקציה, משתמשים ב-method runWith. השיטה הזו מקבלת אובייקט JSON שתואם לממשק RuntimeOptions, שמגדיר ערכים ל-maxInstances. לדוגמה, הפונקציה הזו מגדירה מגבלה של 100 מכונות כדי לא להעמיס על מסד נתונים היפותטי מדור קודם:

exports.mirrorOrdersToLegacyDatabase = functions
    .runWith({
      // Legacy database only supports 100 simultaneous connections
      maxInstances: 100,
    })
    .firestore.document("orders/{orderId}")
    .onWrite((change, context) => {
      // Connect to legacy database
    });

אם תרחיבו את ההיקף של פונקציית HTTP עד למגבלה maxInstances, בקשות חדשות ייכנסו לתור למשך 30 שניות, ולאחר מכן נדחות עם קוד התגובה 429 Too Many Requests אם עד אז לא תהיה מכונה זמינה.

מידע נוסף על שיטות מומלצות לשימוש בהגדרות המספר המרבי של מכונות מופיע במאמר שיטות מומלצות לשימוש ב-maxInstances.

הגדרה של זמן קצוב לתפוגה והקצאת זיכרון

בחלק מהמקרים, לפונקציות עשויות להיות דרישות מיוחדות לזמן קצוב לתפוגה ארוך או להקצאה גדולה של הזיכרון. אפשר להגדיר את הערכים האלה במסוף Google Cloud או בקוד המקור של הפונקציה (Firebase בלבד).

כדי להגדיר הקצאת זיכרון וטיימר תפוגה בקוד המקור של הפונקציות, משתמשים בפרמטר runWith שהוצג ב-Firebase SDK לגרסה Cloud Functions 2.0.0. אפשרות סביבת זמן הריצה הזו מקבלת אובייקט JSON שתואם לממשק RuntimeOptions, שמגדיר ערכים ל-timeoutSeconds ול-memory. לדוגמה, פונקציית האחסון הזו משתמשת ב-1GB של זיכרון ותקופת הקצוב לתפוגה שלה היא 300 שניות:

exports.convertLargeFile = functions
    .runWith({
      // Ensure the function has enough memory and time
      // to process large files
      timeoutSeconds: 300,
      memory: "1GB",
    })
    .storage.object()
    .onFinalize((object) => {
      // Do some complicated things that take a lot of memory and time
    });

הערך המקסימלי של timeoutSeconds הוא 540, או 9 דקות. כמות הזיכרון שמוענקת לפונקציה תואמת למעבד (CPU) שהוקצה לפונקציה, כפי שמפורט ברשימת הערכים התקינים של memory:

  • 128MB — 200MHz
  • 256MB — 400MHz
  • 512MB — 800MHz
  • 1GB — 1.4GHz
  • 2GB — 2.4 GHz
  • 4GB — 4.8GHz
  • 8GB — 4.8GHz

כדי להגדיר הקצאת זיכרון וזמן קצוב לתפוגה במסוף Google Cloud:

  1. במסוף Google Google Cloud, בוחרים באפשרות Cloud Functions בתפריט השמאלי.
  2. בוחרים פונקציה על ידי לחיצה על השם שלה ברשימת הפונקציות.
  3. לוחצים על הסמל עריכה בתפריט העליון.
  4. בוחרים הקצאת זיכרון מהתפריט הנפתח עם התווית הקצאת זיכרון.
  5. לוחצים על עוד כדי להציג את האפשרויות המתקדמות, ומזינים מספר שניות בתיבה Timeout.
  6. כדי לעדכן את הפונקציה, לוחצים על Save.