ניסיון חוזר של פונקציות אסינכרוניות

במסמך הזה מוסבר איך לבקש מפונקציות אסינכרוניות (לא HTTPS) לבצע ניסיון חוזר במקרה של כשל.

סמנטיקה של ניסיון חוזר

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

בפונקציות מדור שני, חלון הניסיון החוזר יפוג אחרי 24 שעות. לפונקציות מדור ראשון, התוקף פג אחרי 7 ימים. Cloud Functions מבצע ניסיון חוזר של פונקציות מבוססות-אירועים חדשות שנוצרו באמצעות אסטרטגיית השהיה מעריכית לפני ניסיון חוזר (exponential backoff), עם השהיה לפני ניסיון חוזר (backoff) של בין 10 ל-600 שניות. המדיניות הזו חלה על פונקציות חדשות בפעם הראשונה שמפרסים אותן. הוא לא יחול באופן רטרואקטיבי על פונקציות קיימות שנפרסו לראשונה לפני שהשינויים המתוארים בנתוני הגרסה הזו נכנסו לתוקף, גם אם תפרסו מחדש את הפונקציות.

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

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

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

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

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

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

הפעלה או השבתה של ניסיונות חוזרים

הגדרת ניסיונות חוזרים מהמסוף

אם יוצרים פונקציה חדשה:

  1. במסך Create Function, בקטע Trigger בוחרים את סוג האירוע שישמש כטריגר לפונקציה.
  2. מסמנים את התיבה Retry on failure כדי להפעיל ניסיונות חוזרים.

אם אתם מעדכנים פונקציה קיימת:

  1. בדף Cloud Functions Overview, לוחצים על שם הפונקציה שרוצים לעדכן כדי לפתוח את המסך Function details. לאחר מכן, בוחרים באפשרות Edit בסרגל התפריטים כדי להציג את החלונית Trigger.
  2. כדי להפעיל או להשבית ניסיונות חוזרים, מסמנים את התיבה Retry onעוצמה או מבטלים את הסימון שלה.

הגדרת ניסיונות חוזרים מקוד הפונקציה

בעזרת Cloud Functions for Firebase אפשר להפעיל ניסיונות חוזרים בקוד של פונקציה. כדי לעשות זאת לפונקציית רקע כמו functions.foo.onBar(myHandler);, משתמשים ב-runWith ומגדירים מדיניות כשל:

functions.runWith({failurePolicy: true}).foo.onBar(myHandler);

הגדרת true כפי שמוצגת מגדירה פונקציה לניסיון חוזר במקרה של כשל.

שיטות מומלצות

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

שימוש בניסיון חוזר כדי לטפל בשגיאות זמניות

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

מגדירים תנאי סיום כדי להימנע מלולאות ניסיון חוזר אינסופיות

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

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

לדוגמה, קטע הקוד הזה מבטל את כל האירועים מלפני יותר מ-10 שניות:

const eventAgeMs = Date.now() - Date.parse(event.timestamp);
const eventMaxAgeMs = 10000;
if (eventAgeMs > eventMaxAgeMs) {
  console.log(`Dropping event ${event} with age[ms]: ${eventAgeMs}`);
  callback();
  return;
}

שימוש ב-catch עם Promises

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

דוגמה למה שצריך לעשות:

return doFooAsync().catch((err) => {
    if (isFatal(err)) {
        console.error(`Fatal error ${err}`);
    }
    return Promise.reject(err);
});

איך הופכים פונקציות מבוססות-אירועים שניתן לנסות שוב לפונקציות חד-פעמיות (idempotent)

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

  • הרבה ממשקי API חיצוניים (כמו Stripe) מאפשרים לספק מפתח אימידנסיטי כפרמטר. אם אתם משתמשים ב-API כזה, צריך להשתמש במזהה האירוע בתור מפתח החד-פעמיות.
  • אידמפוטנטיות פועלת היטב עם העברה אחת לפחות, כי היא מאפשרת לנסות שוב בבטחה. לכן, שיטה מומלצת לכתיבה של קוד מהימן היא לשלב בין אי-תלותיות ברצף לבין ניסיונות חוזרים.
  • מוודאים שהקוד אידמפוטנטי באופן פנימי. לדוגמה:
    • חשוב לוודא שאפשר לבצע מוטציות יותר מפעם אחת בלי לשנות את התוצאה.
    • שאילתת המצב של מסד הנתונים בטרנזקציה לפני שינוי המצב.
    • חשוב לוודא שכל ההשפעות הנוספות הן גם הן חד-פעמיות.
  • להחיל בדיקה עסקית מחוץ לפונקציה, ללא קשר לקוד. לדוגמה, לשמור מצב במקום כלשהו שבו מתועד שמזהה אירוע מסוים כבר עבר עיבוד.
  • טיפול בקריאות כפולות לפונקציות מחוץ לתחום. לדוגמה, כדאי לבחור תהליך ניקוי נפרד, שמתנקה אחרי הפעלות כפולות של פונקציות.

הגדרת מדיניות הניסיון החוזר

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

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

כדי להגדיר את מדיניות הניסיונות החוזרים:

  1. כותבים פונקציית HTTP.
  2. משתמשים ב-Pub/Sub API כדי ליצור מינוי ל-Pub/Sub, ומציינים את כתובת ה-URL של הפונקציה כיעד.

מידע נוסף על הגדרה ישירה של Pub/Sub זמין במסמכי התיעוד של Pub/Sub בנושא טיפול בכשלים.