קצת על Bulkheads – דפוס עיצוב של חוסן (Resiliency Design Pattern)

מספינות לעולם הפיתוח: ה-bulkhead הוא רעיון עקרוני ליציבות של מערכות, שאותו ניתן לראות בשימוש גם בעולם התוכנה

ship cc0 pd pixabay

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

ירידה לעומקו של עניין

רעיון ה-bulkhead [א] הוא רעיון עקרוני ליציבות של מערכות, שאותו ניתן לראות בשימוש גם בעולם התוכנה. מצבי-כשל של מערכות עשויים לנבוע מאינספור מצבים – שלא את כולם ניתן אפילו לחזות. דפוס ה-bulkhead מאפשר להתגונן מ”כשל בלתי ידוע” על ידי בידוד נזק אפשרי לאיזור מוגבל במערכת.

בניגוד ל-redundancy (רעיון דומה), ב-bulkhead כל החלקים הם בשימוש. כלומר: אין חלקים יתירים.
ב-redundancy התסריט הנפוץ הוא פגיעה ללא הפרעה לשירות, אך ב-bulkhead – ישנה פגיעה חלקית במקום מלאה (תמורת חסכון עלויות ה-redundancy).

ל-Bulkheads יש גם מחירים: בניית המנגנון, תחזוקתו, ויצירת חוסר יעילות מסוים במערכת – ולכן נרצה להשתמש בו בעיקר במערכות בעלות חשיבות עסקית גבוהה לארגון, וגם אז – במקרים בהם התשואה של הפתרון תהיה גבוהה יחסית להשקעה.

הנה שתי דוגמאות מוכרות ליישום של bulkhead שאנו מכירים מהיום-יום:

availability zones ב-AWS (או המקבילה בעננים אחרים) – כשל של AZ יחיד יפגע בשירות (בטווח הקצר) – אך יאפשר לנו להמשיך את השירות כרגיל ב-AZs האחרים.

לצורך כך מושקעים ב-Amazon מאמצים רבים על מנת לוודא ש-AZ אינם תלויים זה בזה, ושכשל באחד ה-AZ (הצפה, נפילת מתח, בעיית תוכנה, וכו’) – לא יגרור כשל של ה-AZ האחרים.

כמובן שבתכנון מערכת המשתמשת ב-AWS עלינו ליצור יתירות של שירותים חיוניים (למשל: NAT gateway או בסיס-נתונים) על מנת שנוכל להמשיך ולרוץ בזמן ש-AZ אחד כשל.

תהליכים במערכת ההפעלה – מערכת ההפעלה יוצרת הפרדה בין תהליכים (processes) שונים כך שכשל בתהליך אחד לא ישפיע על תהליכים אחרים: תהליך אחד קורס – והשאר יכולים להמשיך לרוץ ללא הפרעה. למען הדיוק הטכני שווה לציין שההפרדה הזו אינה bullet proof כאשר מדובר בגישה למשאבים משותפים.

למשל: תהליך שגוזל 100% CPU עלול להיות מתוזמן (לחלופין) על כל ה cores של המכונה ולשתק בפועל את כולה. עלינו להצמיד את התהליך (בעזרת CPU binding / affinity) ל-core מסוים – בכדי לקבל הגנה טובה בפני תסריט ה “CPU 100%”. עניין דומה קיים לגבי זיכרון, גישה ל-I/O, או כל משאב משותף אחר.

היישום שאני רוצה להתמקד בו הוא יישום אפליקטיבי של מערכת (ווב).

יישום בסיסי של bulkheads: להפריד את השרתים שלנו לשני clusters שונים (ומבודדים זה-מזה) ולנתב בקשות שונות ל-cluster שונה. החלוקה יכולה להיות עבור microservice בודד, קבוצה של microservices, או אולי אפילו כל המערכת.

חלוקה גנרית וחסרת בידול היא לרוב רעיון רע ליישום של רעיון ה-bulkhead. אם נחלק טראפיק לפי סיומת כתובת ה-IP (זוגי/אי-זוגי) הרי לא באמת עזרנו למערכת – אקראיות יכולה לגרום שקבוצה אחת תדרוש יותר משאבים, אך לא תקבל אותם: את המשאבים חייבנו להיות “חצי-חצי”.

החכמה ביישום bulkhead מוצלח היא חלוקה סלקטיבית ע”פ שני קריטריונים:

  • מאפייני כשל (failure conditions) – כך שתעבורה מסוג I עלולה לכשול בעוד תעבורה מסוג II עשויה לעבוד כרגיל.
  • יתרון עסקי (financial benefit) – כאשר יש חשיבות עסקית מאחורי סוגי התעבורה השונים שעשויה להצדיק מצב בו תעבורה סוג I שורדת בעוד תעבורה סוג II כושלת.

Bulkhead מוצלח עשוי להיות על בסיס שני הקריטריונים, או רק אחד מהם.

הנה כמה דוגמאות ליישום של Bulkhead ברמה האפליקטיבית:

הדוגמה הקלאסית היא כנראה הפרדה בין לקוחות משלמים ללקוחות לא-משלמים.
נניח: אתר שנותן שירות מוגבל בחינם, אך שירות משופר בתשלום (freemium).
שימו לב שהחלוקה היא עסקית.
וריאציה מקובלת: שני clusters:

  • Cluster A – ללקוחות משלמים
  • Cluster B – ללקוחות שאינם משלמים.

אם יש בעיה בפיצ’ר של לקוחות לא-משלמים שגורם לבעיה – לקוחות משלמים יכולים (ובצדק!) להמשיך ליהנות משירות תקין. אפשר לשים יותר חומרה ומשאבים, קונפיגרציות יותר אמינות (גם אם עולות יותר) – ב-cluster של הלקוחות המשלמים.

החולשה של המודל היא במאפייני הכשל: דווקא הלקוחות המשלמים מקבלים כנראה יותר יכולות, ולכן יש סבירות גבוהה יותר שדווקא הטראפיק שלהם ייתקל בבאג כלשהו – שלא יקרה ללקוחות ה”חינמיים”.
קצת פדיחה אם Cluster A נפל – בעוד cluster B עובד כרגיל…

תת וריאציה היא ש-Cluster B יקבל תעבורה של שני סוגי הלקוחות: משלמים ולא-משלמים.
במקרה של תקלה – אפשר לדחות לקוחות לא-משלמים כליל מהמערכת. אם יש משהו שיציל את התעבורה של לקוחות משלמים (נניח: עוד חומרה) – אדרבא!

אם יש כשל שנובע מ”פיצ’ר חינמי” (נניח: פרסומות) – יש הגיון עסקי רב לבודד את הכשל מלקוחות משלמים. הוריאציה הזו הגיונית ככל ש-Cluster B גדול מ-Cluster A (נניח: פי כמה מונים).

דוגמה: הפרדה בין לקוחות ישירים ללקוחות דרך שותפים עסקיים – בהנחה שה-flow של שותפים עסקיים הוא שונה (למשל: חישוב מחיר שונה, מעקב שונה, ועדכון השותף העסקי בזמן אמת – וכו’).

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

בדוגמה הזו יש ל-bulkheads פוטנציאל גדול יותר להשיג שיפור ממשי מהדוגמה הקודמת.

דוגמה: הפרדה לפי שווקים

למשל:

  • Cluster ללקוחות ארה”ב
  • Cluster ללקוחות מערב אירופה
  • Cluster ללקוחות מזרח-אירופה
  • Cluster ללקוחות אנגליה

בהנחה שעבור כל מדינה יש חלקי קוד ייחודים המתאימים לרגולציה ו/או settings מעט שונים שהם מקובלים יותר (בגלל הבדלים בין השירות במדינות) – העלולים לגרום לתנאי כשל שונים.

ייתכן ויש בעצם 20 מדינות בהן עובדים, כאשר לכל מדינה יש תצורת עבודה מעט שונה. אבל – מאוד יקר לנהל 20 clusters, וגם אחוז המשאבים המבוזבז (כי לא משתפים אותם) – יגדל ויתעצם.

ניתוח של תנאי הכשל (אלו מדינות משתמשות בפיצ’רים שונים -> חשיפה לתנאי כשל פוטנציאלים שונים) והמשמעות העסקית מובילה אותנו לחלוקה ל-4 clusters.

במידה וכל השווקים (לאחר ה-clustering) הם בעלי חשיבות עסקית דומה, הפוטנציאל של bulkheads המתואר זה תלוי בעצם בתנאי-כשל שונים משמעותית בין ה clusters. ככל שתנאי הכשל שונים בין ה clusters – כך ההצדקה להצבת bulkheads הולכת ועולה.

להזכיר: כאשר אותו מצב כשל מתרחש בכל התסריטים – כל ה-clusters ייפגעו, וההפרדה לא תעזור.

דוגמה אחרונה: מנגנון חדש מול מנגנון ישן (“canary release”)

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

ה-bulkheads יאפשרו שכשל מתגלגל במנגנון החדש, לא יפגע במאסה של הביזנס – שפועל עדיין על המנגנון הישן.

אמנם כל הדוגמאות שנתתי הן ברמת ה-cluster האפליקטיבי, אבל הרעיון של Bulkhead הוא כללי ויכול להיות מיושם ברמות שונות. למשל: ברמת ה-thread pool או רמת הסכמה בבסיס הנתונים.

אזהרת Patterns!!! (גנרית)

Bulkheads הוא סוג של דפוס עיצוב (Design Pattern) – ודפוסי עיצוב הם דבר “מדליק” המושכים אותנו ליישם אותם. כבני אדם, אנחנו נוטים לנגן שוב בראשנו את הסיפור כיצד השימוש ב Pattern “הציל את המצב” ומלבישים את הסיפור ההוא (שקרה במקום אחר, ואנחנו לא באמת מודעים לפרטים עד הסוף) עלינו, על המערכת שלנו, ועל הארגון שלנו.

הסיפור יכול להישמע טוב – ועדיין להיות לא-בר-קיימא למערכת / לארגון שלכם.

מהנדסי תוכנה, נוטים סטטיסטית לאימוץ מופרז ולא מוצדק (Overuse) של דפוסי עיצוב. לכן: ההמלצה היא לאמת דפוס-עיצוב רק לאחר שהוכחה הבעיה, ולא כהכנה מראש — (תנו לי, אני ״אוהב״ את הביטוי הזה:) כ ״הכנה למזגן״. (ביטוי מטעה מיסודו, ולכן בזוי בעיני).

אני רק מקווה שפוסט זה יצליח לייצר יותר תועלת (פתרון בעיות אמיתיות) מנזק (over-engineering).
זו דילמה שיש לי לפני כל פרסום פוסט שעוסק ב”דפוס-עיצוב”.

שיהיה בהצלחה!


[א] בעולם הספנות bulkheads נקראים גם partitions. המונח “partitions” בעולם התוכנה הוא מאוד מוכר ומתייחס בעולם לרעיון מעט אחר, ולכן בהקשר לתוכנה משתמשים רק במונח bulkheads על מנת לתאר… bulkheads.

[ב] התרשים הזה הוא חלק מהסבר כיצד למדו בעולם הספנות לבנות bulkheads נכונים יותר: על bulkheads שבקצוות האוניה (בעיקר בחרטום) עלול להיות מופעל לחץ גדול יותר ברגע שהוא דולף, ואז הספינה נוטה ומתחילה לשקוע – ועל כן חשוב לבנות אותם חזקים יותר, משאר ה-bulkheads בספינה.
בהשלכה אלינו: bulkheads יש לבנות בחכמה: הם לא פתרון קסם לכל מצב.

הפוסט פורסם לראשונה בבלוג ארכיטקטורת תוכנה

ליאור בר-און

ליאור בר-און הוא Chief Architect בחברת סטארטאפ ישראלית גדולה.

הגב

הגב ראשון!

avatar
Photo and Image Files
 
 
 
Audio and Video Files
 
 
 
Other File Types
 
 
 

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

wpDiscuz

תגיות לכתבה: