במסמך הזה מוסבר איך לבקש מפונקציות אסינכרוניות (לא HTTPS) לבצע ניסיון חוזר במקרה של כשל.
סמנטיקה של ניסיון חוזר
Cloud Functions מאפשרת להריץ פונקציה מבוססת-אירועים לפחות פעם אחת לכל אירוע שמופעל על ידי מקור אירועים. כברירת מחדל, אם קריאה לפונקציה מסתיימת בשגיאה, הפונקציה לא מופעלת שוב והאירוע נמחק. כשמפעילים ניסיונות חוזרים בפונקציה מבוססת-אירועים, Cloud Functions מנסה שוב להפעיל את הפונקציה שנכשלה עד שהיא מסתיימת בהצלחה או שחלון הניסיונות החוזרים פג.
בפונקציות מדור שני, חלון הניסיון החוזר יפוג אחרי 24 שעות. לפונקציות מדור ראשון, התוקף פג אחרי 7 ימים. Cloud Functions מבצע ניסיונות חוזרים לפונקציות מבוססות-אירועים שנוצרו לאחרונה באמצעות אסטרטגיה של השהיה מעריכית לפני ניסיון חוזר (exponential backoff), עם השהיה הולכת וגדלה של בין 10 ל-600 שניות. המדיניות הזו חלה על פונקציות חדשות בפעם הראשונה שמפרסים אותן. השינוי לא יחול באופן רטרואקטיבי על פונקציות קיימות שנפרסו לראשונה לפני שהשינויים המתוארים בנתוני הגרסה האלה נכנסו לתוקף, גם אם תפרסו מחדש את הפונקציות.כשלא מפעילים ניסיונות חוזרים לפונקציה, כפי שמוגדר כברירת מחדל, הפונקציה תמיד מדווחת שהיא בוצעה בהצלחה, וקודי תגובה מסוג 200 OK
עשויים להופיע ביומני הפונקציה. זה קורה גם אם הפונקציה נתקלה בשגיאה. כדי להבהיר מתי הפונקציה נתקלת בשגיאה, חשוב לדווח על שגיאות בצורה מתאימה.
למה פונקציות מבוססות-אירועים לא מסתיימות
לעיתים רחוקות, פונקציה עלולה לצאת לפני הזמן בגלל שגיאה פנימית, ועל כן כברירת מחדל יכול להיות שיתבצע ניסיון חוזר אוטומטי לפונקציה או שלא.
בדרך כלל, פונקציה מבוססת-אירועים עשויה להיכשל בגלל שגיאות שהופקו בקוד הפונקציה עצמו. יכולות להיות לכך כמה סיבות:
- הפונקציה מכילה באג והזמן הריצה גורם להשלכת חריגה.
- הפונקציה לא יכולה להגיע לנקודת קצה של שירות, או שהזמן פג בזמן הניסיון לעשות זאת.
- הפונקציה מפעילה בכוונה חריגה (לדוגמה, כשפרמטר לא עובר אימות).
- פונקציה ב-Node.js מחזירה הבטחה שנדחתה, או מעבירה ערך שאינו
null
לפונקציית קריאה חוזרת.
בכל אחד מהמקרים האלה, הפונקציה תפסיק לפעול כברירת מחדל והאירוע יושלך לאשפה. כדי לנסות שוב את הפונקציה כשמתרחשת שגיאה, אפשר לשנות את מדיניות ברירת המחדל לניסיונות חוזרים על ידי הגדרת המאפיין retry on failure. כתוצאה מכך, המערכת תנסה שוב ושוב לבצע את האירוע עד שהפונקציה תושלם או שתוקף הזמן הקצוב לניסיון חוזר יפוג.
הפעלה או השבתה של ניסיונות חוזרים
הגדרת ניסיונות חוזרים מהמסוף
אם יוצרים פונקציה חדשה:
- במסך Create Function, בקטע Trigger בוחרים את סוג האירוע שישמש כטריגר לפונקציה.
- מסמנים את התיבה Retry on failure כדי להפעיל ניסיונות חוזרים.
אם מעדכנים פונקציה קיימת:
- בדף Cloud Functions Overview, לוחצים על שם הפונקציה שרוצים לעדכן כדי לפתוח את המסך Function details. לאחר מכן, בוחרים באפשרות Edit בסרגל התפריטים כדי להציג את החלונית Trigger.
- מסמנים או מבטלים את הסימון של התיבה Retry on failure כדי להפעיל או להשבית ניסיונות חוזרים.
הגדרת ניסיונות חוזרים מקוד הפונקציה
בעזרת Cloud Functions for Firebase אפשר להפעיל ניסיונות חוזרים בקוד של פונקציה. כדי לעשות זאת בפונקציה ברקע כמו functions.foo.onBar(myHandler);
, משתמשים ב-runWith
ומגדירים מדיניות כשל:
functions.runWith({failurePolicy: true}).foo.onBar(myHandler);
הגדרת true
כפי שמוצגת מגדירה פונקציה לניסיון חוזר במקרה של כשל.
שיטות מומלצות
בקטע הזה מתוארות שיטות מומלצות לשימוש בניסיונות חוזרים.
שימוש בניסיון חוזר כדי לטפל בשגיאות זמניות
מכיוון שהפונקציה תנסה שוב ושוב עד שהיא תתבצע בהצלחה, צריך להסיר מהקוד שגיאות קבועות כמו באגים באמצעות בדיקה לפני שמפעילים ניסיונות חוזרים. מומלץ להשתמש בניסיונות חוזרים כדי לטפל בכשלים זמניים או חולפים שיש סיכוי גבוה שהם יתוקנו בניסיון חוזר, כמו נקודת קצה (endpoint) של שירות לא יציבה או זמן קצוב לתפוגה.
מגדירים תנאי סיום כדי להימנע מלולאות ניסיון חוזר אינסופיות
מומלץ להגן על הפונקציה מפני לולאות מתמשכות כשמשתמשים בניסיונות חוזרים. כדי לעשות זאת, צריך לכלול תנאי סיום מוגדר היטב לפני שהפונקציה מתחילה את העיבוד. חשוב לזכור שהשיטה הזו פועלת רק אם הפונקציה מתחילה לפעול בהצלחה ויכולה להעריך את תנאי הסיום.
גישה פשוטה אך יעילה היא להשליך אירועים עם חותמות זמן ישנות יותר ממועד מסוים. כך אפשר למנוע הפעלות מיותרות כשהכשלים הם מתמשכים או נמשכים יותר מהצפוי.
לדוגמה, קטע הקוד הזה מבטל את כל האירועים שנוצרו לפני יותר מ-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 דקות לכל היותר.
- שינוי זמן ההשהיה המינימלי והמקסימלי של שיטת הניסיון החוזר עם השהיה מעריכית.
- משנים את אסטרטגיית הניסיון החוזר לניסיון חוזר מיידי.
- מגדירים נושא להודעות ללא מוצא.
- מגדירים מספר ניסיונות מקסימלי ומינימלי לשליחה.
כדי להגדיר את מדיניות הניסיונות החוזרים:
- כותבים פונקציית HTTP.
- משתמשים ב-Pub/Sub API כדי ליצור מינוי ל-Pub/Sub, ומציינים את כתובת ה-URL של הפונקציה כיעד.
למידע נוסף על הגדרה ישירה של Pub/Sub, תוכלו לעיין במסמכי התיעוד של Pub/Sub בנושא טיפול בכשלים.