לדלג לתוכן

Exponential Backoff מוסבר - איך retry חכם מונע מכשל קטן להפוך לקריסה

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

למה retry מיידי הוא בעצם מסוכן

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

הרעיון - להגדיל את ההשהיה בין ניסיון לניסיון

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

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

Jitter - למה חייבים גם רעש אקראי

יש בעיה סמויה גם ב-exponential backoff נקי. אם כל אלף הלקוחות נכשלו באותו רגע בדיוק, וכולם משתמשים באותה נוסחת השהיה בדיוק, כולם ינסו שוב יחד, בדיוק באותה שנייה, שוב ושוב - רק עכשיו במרווחים גדולים יותר. הבעיה המקורית, גל בקשות מסונכרן, לא נעלמה, היא רק הואטה.

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

מתי בכלל כדאי לנסות שוב, ומתי לא

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

למה זה נושא שכל מי שכותב קוד שמדבר עם שירות חיצוני צריך

כמעט כל אפליקציה שמדברת עם API חיצוני, מסד נתונים מרוחק, או שירות פנימי אחר, נתקלת בכשלים זמניים מדי פעם - זו לא שאלה של אם, אלא מתי. exponential backoff עם jitter הוא הפתרון הכמעט אוניברסלי, וזו הסיבה שכמעט כל ספריית HTTP רצינית וכל SDK של ענן כולל אותו כברירת מחדל.

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

יש לכם שירות שמפסיק לענות תחת עומס בגלל retry לא נכון? שאלו בדיסקורד.

הצטרפו לקהילה בדיסקורד

לסיכום

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