Active-Active Architecture: כשכל האזורים עובדים בו-זמנית ואף אחד לא רק מחכה

מאת צוות מדיה דיל · 06.08.2026 · DevOps · 7 דק׳

מדריך טכני ל-Active-Active Architecture: ניצול מלא של תשתית בכמה אזורים, conflict resolution, CRDTs, ואיך נמנעים מ-split-brain כשכולם כותבים במקביל.

הבעיה: לשלם על תשתית שלא עובדת

ב-Active-Passive קלאסי, region שני עומד מוכן — משלמים עליו מלא, אבל הוא לא משרת תעבורה אמיתית עד שקורה אסון. זה מבזבז capacity ולרוב גם latency: משתמשים רחוקים מה-primary סובלים מ-round trip ארוך גם ביום רגיל לגמרי. Active-Active Architecture פותרת את שתי הבעיות: כל האזורים משרתים תעבורה אמיתית בו-זמנית, כל משתמש מקבל שירות מהאזור הקרוב לו, וכל הקיבולת בשימוש מלא כל הזמן. המחיר: מורכבות אמיתית בניהול כתיבות מקבילות מכמה מקומות בו-זמנית לאותו דאטה.

מה בדיוק הופך ארכיטקטורה ל-Active-Active

ההגדרה המדויקת: יותר מ-region (או data center) אחד מקבל ומעבד traffic יצרני אמיתי באותו רגע נתון, לא רק "עומד מוכן". זה נכון גם לרמת compute (app servers בכל region עונים לבקשות) וגם — וזה החלק הקשה — לרמת הדאטה: כתיבות יכולות להתקבל בכל region, לא רק להיקרא ממנו.

חשוב להבחין בין שני מודלים שלעיתים מבלבלים ביניהם:

  • Active-Active ברמת קריאה בלבד — כל regions משרתים reads (עם replicas מקומיים), אבל כל הכתיבות מנותבות למקום אחד (single writer). זה למעשה active-passive מוסווה מבחינת דאטה, פשוט עם read replicas גלובליים. הרבה יותר קל למימוש, ומכסה חלק גדול מהתועלת (רוב האפליקציות הן read-heavy).
  • Active-Active ברמת כתיבה אמיתית — כל region יכול לקבל כתיבות עצמאיות. זה הביטוי המלא של הפאטרן, וגם המקום שבו כל הקושי האמיתי גר.

הבעיה המרכזית: Write Conflicts

ברגע ששני regions יכולים לקבל כתיבה לאותו רשומה בו-זמנית, נוצרת שאלה בלתי נמנעת: מה קורה כששני משתמשים (או אותו משתמש דרך שני חיבורים שונים) מעדכנים את אותה רשומה בשני regions שונים כמעט באותו רגע, לפני שה-replication הספיקה להסתנכרן? יש כמה אסטרטגיות מבוססות לפתור זאת:

  • Last-Write-Wins (LWW) — הכתיבה עם ה-timestamp המאוחר יותר "מנצחת", השנייה נדרסת. פשוט למימוש, אבל מסוכן — עלול לאבד דאטה שקטית לחלוטין בלי שאף אחד ידע, במיוחד אם השעונים בין השרתים לא מסונכרנים מדויק (clock skew).
  • CRDTs (Conflict-free Replicated Data Types) — מבני נתונים שמעוצבים מתמטית כך שמיזוג בין שתי גרסאות תמיד מניב תוצאה עקבית וללא איבוד מידע, בלי צורך בתיאום מרכזי. עובד מצוין למבנים ספציפיים (counters, sets, sorted logs) אבל לא לכל סוג דאטה — עדכון "שם משתמש" הוא לא בעיה שCRDT פותר בקלות.
  • Application-Level Merge — הלוגיקה העסקית עצמה יודעת למזג (למשל: עגלת קניות ממזגת את שני הסטים של פריטים, במקום לבחור אחד). דורש מאמץ הנדסי ספציפי לכל domain.
  • Sticky Sessions / Home Region — הפתרון הפרגמטי הנפוץ ביותר בפועל: כל ישות (משתמש, tenant) "שייכת" ל-region אחד עבור כתיבות, כך שלא קורים כתיבות מקבילות לאותה רשומה משני מקומות מלכתחילה. שאר ה-regions הם read replicas עבורה. זה בעצם מוותר על active-active מלא ברמת record בודד, ומקבל אותו רק ברמת המערכת כולה.

בפועל, רוב הארגונים שמצליחים ב-active-active אמיתי משתמשים בגישת "home region" ברמת המשתמש/tenant — זה נותן את רוב היתרונות (latency, ניצול תשתית) בלי הסיבוך המלא של conflict resolution גלובלי.

Global Databases שנבנו במיוחד לזה

מסדי נתונים מודרניים כמו Google Spanner ו-CockroachDB פותרים את בעיית הכתיבה המקבילה ברמת ה-DB engine עצמו, באמצעות consensus protocol (Paxos/Raft) מבוזר גיאוגרפית עם strong consistency אמיתי — כל כתיבה מאושרת רק אחרי quorum על פני regions, מה שמבטיח שאין conflicts בכלל (במחיר latency כתיבה גבוה יותר, כי הbקשה מחכה לאישור ממקומות רחוקים). זה הפתרון ה"נקי" ביותר מבחינה תיאורטית, אבל דורש buy-in מלא לתשתית ייעודית ולעיתים refactor משמעותי אם המערכת כבר בנויה על DB רגיל.

גישה נפוצה אחרת היא DynamoDB Global Tables או Cosmos DB עם multi-region writes — נותנות eventual consistency עם conflict resolution אוטומטי (בדרך כלל LWW), פשוטות יותר להטמעה אבל עם הסיכונים של LWW שתוארו למעלה.

ניתוב תעבורה ב-Active-Active

מעבר לדאטה, גם ניתוב התעבורה עצמו דורש מחשבה: latency-based routing (GeoDNS או Anycast) מפנה כל משתמש לregion הקרוב. הקריטי כאן הוא health-aware routing — אם region נכשל, התעבורה חייבת "לזלוג" אוטומטית לregions אחרים תוך שניות, לא דקות. זה דורש health checks תכופים ברמת ה-global load balancer, ו-Anycast עדיף על GeoDNS בהקשר הזה בדיוק בגלל מהירות ה-failover.

Trade-offs: מתי Active-Active שווה את המורכבות

היתרון המרכזי — ניצול מלא של תשתית ו-latency אופטימלי לכולם — מגיע במחיר ישיר: מורכבות תפעולית גבוהה משמעותית מ-active-passive, סיכון ל-data conflicts שדורש תכנון קפדני, ועלות פיתוח ראשונית גבוהה יותר (במיוחד אם בוחרים ב-conflict resolution ברמת אפליקציה במקום global DB מוכן). ה-trade-off האמיתי הוא בין "פשוט לתחזק" ל"מנצל תשתית במלואה" — ולרוב הארגונים, היתרון בlatency לא מצדיק את הסיכון בconsistency, אלא אם יש בסיס משתמשים גלובלי אמיתי וקריטי.

ניטור Conflict Rate: המדד שמראה אם ה-Architecture עובד

ברגע שמערכת active-active עם כתיבות מרובות רצה בפרודקשן, המדד הכי חשוב לניטור שוטף הוא conflict rate — כמה כתיבות בפועל מתנגשות ונדרשות ל-resolution (LWW, CRDT merge, או ידני). conflict rate נמוך ויציב מעיד שה-home region strategy (או מנגנון ה-partitioning) עובד כמצופה. עלייה פתאומית ב-conflict rate היא סימן אזהרה מוקדם לבעיה עמוקה יותר — למשל bug ב-routing שגורם למשתמשים "לקפוץ" בין regions בטעות, או ניצול לרעה (משתמש שמריץ בקשות ממספר locations בו-זמנית באופן לא צפוי).

מעבר לניטור השוטף, חשוב לתעד ולבדוק תקופתית מדגם של conflicts שנפתרו — לא רק לספור אותם אלא לוודא שה-resolution logic (LWW, merge) אכן מייצר תוצאה הגיונית מבחינה עסקית, לא רק "תקינה" מבחינה טכנית. resolution אוטומטי ש"עובד" מבחינת קוד יכול עדיין לייצר תוצאה מוזרה מנקודת מבט המשתמש (למשל, איבוד עדכון legit כי הוא הגיע כמה מילישניות אחרי עדכון אחר).

טעויות נפוצות בפרודקשן

  • מתחילים ב-full multi-writer בלי home region — במקום להתחיל בגישה הפרגמטית (sticky home region), קופצים ישר לפתרון מורכב מדי, ומגלים conflicts שלא תוכננו אליהם.
  • LWW בלי clock synchronization אמיתי (NTP מדויק) — clock skew של כמה מילישניות בין servers יכול לגרום לאובדן דאטה "אקראי" שקשה מאוד לאבחן.
  • לא בודקים split-brain scenarios — מה קורה אם network partition מפריד בין regions לזמן ממושך? בלי בדיקה, ההתנהגות בפועל לא ידועה עד שזה קורה.
  • מניחים ש-CRDT פותר הכול — CRDTs מתאימים למבני נתונים ספציפיים, לא תחליף גורף לתכנון conflict resolution.

מסלול הדרגתי: מ-Active-Passive ל-Active-Active

ארגונים כמעט אף פעם לא בונים active-active מלא מהיום הראשון — זה נבנה כהתפתחות טבעית. המסלול הטיפוסי: שלב 1, active-passive עם standby שלא משרת תעבורה. שלב 2, הפיכת ה-standby ל"active לקריאה" — reads מנותבים אליו (עם עדכון async), כתיבות עדיין מנותבות ל-primary יחיד. זה נותן כבר חלק ניכר מהיתרון (latency ל-reads, שרוב האפליקציות עשירות בהם) בלי הסיכון של כתיבות מקבילות. שלב 3, רק אם יש דרישה עסקית ברורה (בסיס משתמשים גלובלי אמיתי שדורש latency כתיבה נמוך מכל אזור), עוברים ל-multi-writer אמיתי עם home region strategy או global database.

המסלול ההדרגתי הזה מאפשר לצוות לבנות ביטחון וניסיון עם כל שלב לפני שהוא עובר לשלב הבא, ומזהה מוקדם אם בכלל יש צורך עסקי אמיתי בשלב 3 — לעיתים קרובות מתברר ששלב 2 כבר נותן את רוב הערך הנדרש, והמעבר המלא ל-multi-writer פשוט לא מצדיק את המורכבות הנוספת.

מתי כן ומתי לא

Active-Active מוצדק כשיש בסיס משתמשים גלובלי פעיל ורגיש ל-latency (gaming, trading, real-time collaboration), או כשדרישת הזמינות היא כה גבוהה שכל דקה של downtime משמעותית כלכלית. עבור רוב מוצרי ה-SaaS, Active-Passive עם RTO סביר (דקות בודדות) הוא פתרון בעל ROI טוב יותר — הוא נותן DR אמיתי בלי לשלם את מחיר ה-conflict resolution המתמיד.

Session Affinity ו-Read-Your-Writes UX

בעיה מעשית שצצה כמעט תמיד במימוש active-active ברמת read: משתמש שכותב עדכון (למשל משנה פרטי פרופיל) ב-region A, ואז מיד קורא את אותו דאטה — אם הקריאה מנותבת ל-region B שעדיין לא קיבל את ה-replication, המשתמש "לא רואה" את השינוי שהוא עצמו עשה. זו תופעה שנקראת read-your-writes consistency violation, והיא אחת התלונות הכי נפוצות ב-QA של מערכות active-active. הפתרון הנפוץ הוא session affinity (sticky sessions) — אחרי כתיבה, מנתבים את אותו משתמש לאותו region למשך חלון זמן קצר (שניות עד דקות), עד שסביר שה-replication הסתיימה. פתרון אלגנטי יותר, אם ה-DB תומך בו, הוא read-after-write consistency token — הכתיבה מחזירה "מזהה גרסה", וכל קריאה עוקבת מבטיחה לחכות לreplication שמכילה לפחות את הגרסה הזו, לא רק "קריאה מהregion הקרוב ביותר" עיוורת.

ההחלטה בין שתי הגישות היא עוד trade-off קלאסי: session affinity פשוט יותר אבל פחות מדויק (יכול להישאר "דבוק" זמן רב מדי לregion רחוק), consistency token מדויק יותר אבל דורש תמיכה מפורשת ב-DB layer ובקוד האפליקציה.

סיכום

Active-Active Architecture נותנת את חוויית המשתמש הטובה ביותר וניצול התשתית המלא ביותר, אבל דורשת פתרון אמיתי לבעיית הכתיבה המקבילה. הגישה הפרגמטית שעובדת בפועל היא לרוב "home region" לכתיבות ברמת tenant/user, עם reads גלובליים — לא active-active מלא ברמת כל רשומה, אלא אם יש global database ייעודי (Spanner, CockroachDB) שמצדיק את המחיר.

תגיות: Active-Active · Multi-Region · CRDT · Conflict Resolution · Google Spanner · CockroachDB · Global Database

← חזרה לבלוג · צור קשר