מודלי עקביות - Eventual מול Strong Consistency¶
כשמערכת שומרת עותק אחד של הנתונים שלה, שאלת העקביות פשוטה - כל קריאה מחזירה את הערך העדכני ביותר, כי יש רק עותק אחד. ברגע שמתחילים לשכפל נתונים בין כמה מקומות - לצורך זמינות, ביצועים, או פיזור גיאוגרפי - נוצרת שאלה אמיתית - אם עדכנתם עותק אחד, מתי בדיוק שאר העותקים "רואים" את העדכון? התשובה לשאלה הזאת היא בדיוק מודל העקביות שהמערכת בוחרת.
Strong Consistency - כולם רואים את אותו הדבר, תמיד¶
עקביות חזקה מבטיחה שברגע שעדכון הסתיים, כל קריאה עתידית - מכל עותק, מכל מקום - תחזיר את הערך המעודכן. אין רגע שבו שני קליינטים שונים יכולים לראות ערכים שונים לאותו נתון. כדי להשיג את זה, המערכת צריכה לתאם בין העותקים לפני שהיא מאשרת שהעדכון הצליח - לרוב על ידי המתנה לאישור מרוב העותקים (majority) לפני שהתשובה חוזרת ללקוח. זה נותן ודאות מלאה, אבל בעלות של זמן תגובה גבוה יותר, כי כל כתיבה צריכה לתאם בין כמה מכונות, ולפעמים אפילו בין אזורים גיאוגרפיים מרוחקים.
Eventual Consistency - כולם יגיעו לאותו מקום, בסוף¶
עקביות סופית מוותרת על הערבות המיידית - עדכון שקורה בעותק אחד לא בהכרח מופיע מיד בשאר העותקים, אבל המערכת מבטיחה שאם מפסיקים לבצע עדכונים חדשים, כל העותקים יתכנסו בסוף לאותו ערך. בין הרגע שהעדכון קרה לרגע שכל העותקים מתעדכנים, יש חלון זמן - לרוב אלפיות שנייה עד כמה שניות - שבו קריאה מעותקים שונים יכולה להחזיר תוצאות שונות זו מזו. היתרון הוא ביצועים גבוהים בהרבה - כתיבה מאושרת מיד מבלי לחכות לתיאום מלא בין כל העותקים.
דוגמה מוחשית - ספירת לייקים מול יתרת חשבון בנק¶
ספירת לייקים על פוסט היא דוגמה מצוינת לעקביות סופית מספיקה בהחלט. אם מישהו לוחץ לייק, וכמה שניות עוברות עד שכל מי שרואה את הפוסט רואה את המספר המעודכן, שום נזק אמיתי לא נגרם. לעומת זאת, יתרת חשבון בנק היא דוגמה שבה כמעט תמיד רוצים עקביות חזקה - שתי משיכות בו זמנית מאותו חשבון, שכל אחת רואה יתרה שכבר לא מעודכנת בגלל חלון עקביות סופית, יכולות לגרום לחשבון לרדת מתחת לאפס בלי שהמערכת בכלל שמה לב.
זו לא בחירה גלובלית אחת למערכת שלמה¶
טעות נפוצה היא לחשוב שצריך לבחור מודל עקביות אחד לכל המערכת. בפועל, מערכות גדולות בוחרות ברמת כל רכיב בנפרד - מסד נתונים אחד שמנהל יתרות כספיות עם עקביות חזקה, לצד מסד נתונים אחר שמנהל ספירות מעורבות או לוגים עם עקביות סופית, כי שם המחיר של אי-דיוק זמני נמוך בהרבה מהמחיר של ביצועים איטיים.
מה זה אומר בפועל בעבודה עם מסדי נתונים¶
מסדי נתונים יחסיים מסורתיים כמו PostgreSQL, כשהם רצים במופע יחיד, נותנים עקביות חזקה כברירת מחדל. ברגע שמוסיפים שכפול (replication) בין כמה שרתים, צריך להחליט במפורש - קריאות מהעותק המשוכפל (replica) עלולות להחזיר נתונים "מעט ישנים" אם השכפול לא מיידי, וזה בדיוק אותו טרייד-אוף. מסדי נתונים כמו Cassandra או DynamoDB נבנו מלכתחילה סביב עקביות סופית כברירת מחדל, עם אפשרות לבקש עקביות חזקה יותר לפעולות ספציפיות במחיר ביצועים, כשצריך.
בקורס ארכיטקטורת תוכנה שלנו עוברים על מודלי עקביות לצד משפט CAP, ומראים איך הבחירה הזאת משפיעה על כל שאר עיצוב המערכת.
התלבטתם באיזה מודל עקביות לבחור לרכיב מסוים במערכת שלכם? הצטרפו לקהילה בדיסקורד ותביאו את המקרה שלכם לדיון.
לסיכום¶
עקביות חזקה מבטיחה שכל קריאה תמיד תראה את העדכון האחרון, במחיר זמן תגובה גבוה יותר בגלל תיאום בין עותקים. עקביות סופית מוותרת על הערבות המיידית תמורת ביצועים גבוהים בהרבה, ומבטיחה רק שכל העותקים יתכנסו בסוף. הבחירה הנכונה תלויה במחיר של אי-דיוק זמני עבור הנתון הספציפי - ומערכות גדולות בוחרות אחרת לכל רכיב, לא מודל אחיד אחד לכל המערכת.