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

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

מפרידים בין סוגי כשל

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

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

Backoff צריך להשתלב בתקציב זמן

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

attempt 1 -> temporary failure
attempt 2 -> wait + jitter -> retry
attempt 3 -> deadline exceeded -> stop and surface

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

האישור מגיע אחרי האפקט — ואז יש מרווח מסוכן

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

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

כותבים למסד הנתונים ומפרסמים אירוע

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

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

הודעה רעילה צריכה מסלול טיפול

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

הקפידו לא לחסום את כל העבודה בגלל הודעה אחת. מצד שני, אם נדרש סדר לפי חשבון, דילוג על הודעה עשוי לשבור את ההנחות של ההודעות הבאות. סדר ואפשרות התקדמות הם trade-off שצריך להסביר. ב־Kafka, למשל, חלוקת עבודה וערבויות תלויות גם במחיצות ובדרך הצריכה; עיינו בתיעוד התכנון הרשמי.

איך בודקים שהמערכת מתאוששת

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

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