Saga Pattern: איך מנהלים טרנזקציה מבוזרת בלי נעילה גלובלית
מאת צוות מדיה דיל · 30.08.2026 · טכנולוגיה · 6 דק׳
כשפעולה עסקית אחת פרושה על כמה שירותים נפרדים, טרנזקציה קלאסית לא עובדת. Saga Pattern פותר את זה עם רצף פעולות ופיצויים הפוכים.
הזמנה באתר מסחר מערבת חיוב כרטיס אשראי, הפחתת מלאי, ויצירת משלוח — לרוב שלושה שירותים נפרדים לגמרי. במסד נתונים יחיד, טרנזקציה קלאסית הייתה פותרת את זה בפשטות: הכל מצליח ביחד, או הכל נכשל ביחד. במערכת מבוזרת, זה לא אפשרי — וכאן נכנס Saga Pattern.
הבעיה עם טרנזקציות מבוזרות קלאסיות
פרוטוקול Two-Phase Commit, שמנסה לתאם טרנזקציה בין כמה מסדי נתונים בבת אחת, דורש נעילה של כל המשאבים המעורבים עד לסיום — מה שהופך אותו לאיטי ושביר בקנה מידה גדול. ברגע שאחד השירותים לא זמין, כל השרשרת נתקעת.
Saga: רצף פעולות מקומיות עם פיצוי
במקום טרנזקציה גלובלית אחת, Saga מפרקת את הפעולה לרצף של טרנזקציות מקומיות קטנות, כל אחת בשירות שלה. אם שלב באמצע הרצף נכשל, המערכת לא "מבטלת" את השלבים הקודמים בדרך הרגילה — היא מריצה פעולות פיצוי (Compensating Actions) שהופכות את מה שכבר קרה.
דוגמה מעשית: הזמנה שנכשלת באמצע
חיוב האשראי הצליח, הפחתת המלאי הצליחה, אבל יצירת המשלוח נכשלת כי הכתובת לא תקינה. פעולת הפיצוי: להחזיר את הפריט למלאי, ולזכות את הלקוח בחזרה. אף שלב לא "נעול" בזמן ההמתנה — כל שירות מתקדם וחוזר לפי הצורך, בלי לעצור את המערכת כולה.
Choreography: כל שירות מגיב בעצמו
בגישת Choreography, כל שירות מאזין לאירועים משירותים אחרים ומגיב בעצמו, בלי מתאם מרכזי — שירות התשלומים משדר "תשלום הצליח", ושירות המלאי מגיב לכך בעצמו. פשוט להטמיע בהתחלה, אבל קשה למעקב ככל שמספר השירותים גדל, כי אין מקום אחד שמראה את כל הרצף.
Orchestration: מתאם מרכזי אחד
בגישת Orchestration, רכיב מתאם ייעודי מנהל את כל הרצף במפורש — קורא לכל שירות בתורו, ומפעיל פיצוי כשמשהו נכשל. קל יותר למעקב ולדבוג כי הלוגיקה מרוכזת במקום אחד, אבל יוצר תלות ברכיב המתאם עצמו כנקודת כשל פוטנציאלית.
Idempotency: תנאי סף להצלחה
מכיוון שפעולות ברשת עלולות להישלח פעמיים בגלל תקלת רשת זמנית, כל שלב ב-Saga, כולל פעולות הפיצוי עצמן, חייב להיות אידמפוטנטי — הרצה כפולה של אותה פעולה לא יוצרת תוצאה כפולה. בלי זה, ניסיון חוזר אחרי כשל רשת עלול לגרום לחיוב כפול או להחזרת מלאי כפולה.
מתי Saga מוצדק
Saga פותר בעיה אמיתית רק כשהפעולה העסקית באמת פרושה על כמה שירותים עצמאיים. אם כל הלוגיקה יכולה לחיות במסד נתונים אחד עם טרנזקציה רגילה, אין סיבה לייבא את המורכבות הנוספת של רצף פיצויים — זו בדיוק אותה זהירות שהזכרנו לגבי הטמעת CQRS על בעיה שלא קיימת.
Timeout וזיהוי כשל תקוע
שלב ב-Saga שלא מקבל תשובה תוך זמן סביר צריך מנגנון Timeout מפורש שמפעיל את שרשרת הפיצוי, במקום להשאיר את הרצף תקוע לצמיתות. בלי טיפול מפורש בזה, הזמנה יכולה להישאר במצב "בתהליך" לנצח, כשאף גורם לא יודע להמשיך אותה קדימה או אחורה.
מעקב ותצפית על רצף מבוזר
מכיוון שרצף Saga מתפרש על כמה שירותים, מעקב אחרי מצב הזמנה בודדת דורש כלי תצפית ייעודיים — מזהה מתאם (Correlation ID) שמלווה את כל הבקשה לאורך כל השירותים, כך שאפשר לשחזר בדיוק היכן רצף מסוים נעצר או נכשל.
בדיקת תרחישי כשל מראש
לפני העלייה לפרודקשן, כדאי לבדוק במפורש כל נקודת כשל אפשרית ברצף — מה קורה אם השלב השלישי נכשל אחרי שהשניים הראשונים הצליחו, מה קורה אם פעולת הפיצוי עצמה נכשלת. Saga שלא נבדקה בתרחישי כשל אמיתיים עלולה להתגלות כשבורה בדיוק ברגע שהיא הכי צריכה לעבוד.
בונים מערכת עם כמה שירותים שצריכים לתאם פעולה עסקית אחת? מוזמנים לפתוח שיחה בוואטסאפ.
תגיות: Saga Pattern · Distributed Transactions · Microservices · טרנזקציות מבוזרות