בפוסט הזה אני רוצה לספר על תהליך של Hyper-Scaling שאנו עוברים בחברת Gett – וכיצד הוא משפיע על עבודת הארכיטקטורה.
![]() |
| No Scale |
אני רוצה להזכיר שהמונח "Scalability", מתייחס בהנדסת תוכנה לשני סוגים של אתגרים:
- Software Scalability – התמודדות עם יותר משתמשים, יותר פעילות, יותר נתונים.
- Development Scalability – היכולת להתנהל עם צוות פיתוח גדול יותר.
ההקבלה בין Scale של תוכנה ו Scale של קבוצות-פיתוח
- פעולות על כמות גדולה יותר של נתונים – אורכות יותר זמן. למשל: אינדקסים בבסיס נתונים רלציוני, הפחתת הרציפות בדיסק, caches פחות יעילים או סתם פעולות joins גדולות יותר (merge על work set גדול יותר).
- יותר תקשורת שנדרשת בין המחשבים השונים במערכת. יש הבדל בין הודעות עדכון שנשלחות ל 6 מחשבים – וכאלו שנשלחות ל 25 מחשבים. פעם נתקלתי במערכת שהוספה של מחשבים למערכת, מעל 16 מחשבים, כבר לא הגדילה את ה scale – בגלל ריבוי של הודעות כאלו.
- כמות הקוד שלא ניתן למקבל (parallelism) באופן טבעי תגדל, ולא תקטן – אלא אם הייתה השקעה משמעותית בצמצום קוד שכזה.
- חוסרי יעילות שונים – שצצים במערכת באקראיות טבעית.
באופן דומה, גם בגדילה של גוף פיתוח – המפתח האחרון שנוסף נוטה להיות (בממוצע!) פחות יעיל מקודמו:
- כל עבודה רוחבית במערכת (למשל: Refracting גדול), הופכים להיות קשים וארוכים פי כמה – כאשר כמות הקוד גדולה יותר.
- יותר תקשורת וסנכרון נדרשת בין המפתחים בקבוצה. אם פעם היה מספיק להרים את הראש מבהייה במסך – בכדי ליצור קשר עם מתכנת שמכיר היטב היבט מסוים של המערכת, היום כבר צריך לקום מהמקום, לחפש – ולעתים לגלות שצריך לדבר עם כמה אנשים בכדי לקבל תמונה מלאה.
- תמונות-העולם של המפתחים בארגון מתבזרות במהירות: בניגוד לצוות שהיה לו זמן להתגבש במשך תקופה ארוכה – כעת יש זרימה של אנשים חדשים, שכל אחד רגיל לשיטות שונות וגישות שונות.
הגישות הללו, עבור כל אחד, "הוכיחו את עצמן מעל ספק סביר – בעבר". אמת. אבל מה עושים כאשר הגישות הפוכות זו לזו? האם ORM הוא טוב להכל, טוב רק לקונפיגורציה, או "רעה-חולה שיש להסיר מהמערכת בהקדם האפשרי!"? - יותר ידיים עובדות => יותר קוד => מערכת מורכבת יותר. כל שינוי במערכת מורכבת יותר – אורך יותר זמן בעצמו (מגמה לחוסר יעילות מובנה).
- נוצרים יותר צווארי בקבוק ("רק משה מכיר את הקוד הזה") – שהולכים ומקשים יותר ויותר על התקדמות הפיתוח.
- יותר ישיבות, יותר המוניות, יותר אנשים שיש להכיר ולהתרגל לעבוד איתם – חוסרי יעילות שונים, שצצים במערכת באקראיות כל-כך טבעית.
| לא ממש Linear Scaling: ככל שמספר הבקשות עולה – יש להוסיף חלק יחסי גדול יותר של שרתים בכדי לענות על הביקוש. יש במערכת הזו צווארי בקבוק מסוימים ל scalability. |
"פי-2 אנשי support עונים לפי-1.9 קריאות במוקד?" – הם מספרים, "כן… אנחנו מבינים שהנדסה זה קצת יותר מורכב. הגדלנו את הפיתוח פי 4, ואי-אפשר לקבל פי-4 פיצ'רים – אבל גם אי אפשר כבר לצפות לפחות לפי-3 יותר פיצ'רים, או לקבל אותם לפחות פי-3 יותר מהר?"הלחץ מצד הביזנס הוא אולי משני, אבל הוא משפיע – וגורם לנו להתייעל יותר בכל הנוגע ל Development Scalability של הפיתוח. בעיקר ע"י צמצום חוסר-היעילות שהמערכת יוצרת למפתח הבודד.
—
ב Software Scale, יש "קסם" שיכול לסייע למערכת לצמוח ב Scale שהוא יותר מלינארי: מצב בו פי-2 שרתים, משרתים יותר מפי-2 משתמשים. כיצד זה קורה? יש כמה דרכים, אבל ה"קסם" הנפוץ ביותר הוא Cache (או memoization – בגרסה התאורטית שלו).
כאשר אנו יכולים לבצע חישוב מורכב רק פעם ב 5 דקות, ואז להפיץ את התוצאות לעוד ועוד משתמשים – כמות גדולה אפילו יותר של משתמשים תגדיל את הלחץ רק על ערוץ ההפצה (CDN?) – ולא על יצירת התוכן (החישוב).
ככל שנתכנן את המערכת שלנו בצורה בה ניתן יהיה להשתמש יותר ויותר ב Caches שכאלו – נשפר את ה Scalability של המערכת. תכנון שכזה כולל, הרבה פעמים – משא ומתן עם אנשי הביזנס ("תקבלו את זה מעודכן פעם בשעה – לא כל הזמן").
(פתרונות אחרים כוללים העברת עבודה ל Clients, או צמצום העבודה לקצב קטן מבו עולה ה scale, למשל: ביצוע חישוב על 500 משתמשי-מדגם, ללא קשר למספר כלל-המשתמשים במערכת).
| Scaling שהוא טוב מ Linear-Scaling: הוספת שרת למערכת – מוסיפה יכולת לספק קצת יותר משתמשים מחלקו במערכת. |
ב Development Scaling יש גם כמה "קסמים" שכאלו. הבולט בהם – הוא code re-usability: היכולת להשתמש בקוד שנכתב בעבר – עבור פיצ'ר חדש.
פתרון שונה-דומה הוא Generalization: כתיבת קוד כללי יותר – שיכול לשרת מטרות דומות, אך שונות.
הקסמים האלו – הם חמקמקים ביותר!
הם באמת לפעמים "עושים את הקסם" – אבל פעמים אחרות "יוצא מהם כל האוויר" ברגע האמת: אנו משקיעים עוד זמן ועבודה בקוד כללי יותר / קוד המתוכנן לשימוש חוזר – אבל אז השימוש החוזר פשוט לא מתאים. אם השקענו בקוד כללי – ואין לו שימוש, אנו נשארים עם קוד יקר יותר, מסובך יותר, ועם הצורך עדיין לספק פתרון לפיצ'ר השני (או, חס וחלילה: לאנוס את הפיצ'ר השני להיות משהו אחר – בכדי להתאים לקוד שכבר קיים).
מהנדסים צעירים, ואולי אף מהנדסים בכלל – נוטים לבצע הערכת יתר (גסה?) ליכולת שלהם לייצר קוד יעיל לשימוש חוזר / קוד כללי יעיל. משם נוצר הכלל You Ain't Gonna Need It (בקיצור: YAGNI) המציע פשוט לא לנסות לחזות מקרים אלו מראש, אלא לעשות Refactoring לקוד כללי רק ברגע שהוכח הצורך – מעל ספק סביר.
בכל מקרה: שימוש חוזר בקוד והכללה, גם אם נעשים בדיעבד – הם כלים חשובים מאוד לשיפור ה Development Scalability.
אז מה עם ארכיטקטורה ל Hyper-Scaling?!
אולי אתם מאוכזבים מעט מהפוסט: יש בו הרבה דיבורים כללים, ואין בו Hadoop, HPC או Big Data משום צורה!
אני אנסה לתמצת:
הפיתוח של Gett עובר כרגע תהליך של Development Hyper-Scaling. יש גם בעיות של Software-Scaling – אבל הן (עדיין) פחות מאתגרות – אולי אזכיר לקחים משם בפוסט אחר.
הארכיטקטורה, או תוכנית-העל שלנו להתמודד עם בעיות ה Development Hyper Scaling הן כאלו:
- בראש ובראשונה – מעבר ל Micro-Services: הפיכת מערכת אחת מורכבת – לכמה מערכות קטנות יותר, ומורכבות פחות. המעבר הוא אינטנסיבי, אבל הוא מאפשר להבין ביתר קלות את כלל המערכת – ולדעת להיכן לצלול בעת הצורך. כמו כן – הוא מצמצם במידה רבה את הצורך בתקשורת מרובה, לטובת תקשורת בסיסית יותר וממוקדת יותר, למשל: סך האחריויות של כל שירות, וה APIs שלו – שמוגדרים היטב (אנחנו משתמשים ב Swagger לתיעוד – כלי שמשרת אותנו היטב).
את השימוש ב MSA להתמודדות עם Development Hyper-Scaling לא המצאנו בעצמנו: למדנו מ case-studies על חברות שעמדו באתגר דומה (למשל: אמזון).- שימוש-חוזר בקוד, הוא רעיון שקשה לממש (מעבר לפונקציה פה ושם). דווקא Micro-services, בכך שאנו מגדירים שירותים עם שימוש עסקי ברור, ו APIs מוגדרים היטב – מסייעים לנו ליצור יחידות גדולות של קוד שמתאימות לשימוש-חוזר. כבר בחצי-שנה האחרונה, היו לנו כמה הצלחות יפות.
- אנו עוסקים בצורה פרואקטיבית בארגון בשיתוף ידע על חלקי המערכת השונים, האחריויות שלהם, וה flows העיקריים במערכת. לא עובר כמעט שבוע שאני לא עושה session שכזה, לצוות כלשהו בפיתוח – ואני לא היחידי. עוד פעם ועוד פעם – עד שלכולם כבר יימאס (אנחנו עוד רחוקים משם…).
שמות פשוטים, מטפורות טובות, וסיפורים קליטים – הם מרכיב עקרי בבניית והפצת הידע. - צוות הארכיטקטים לוקח תפקיד קצת יותר ריכוזי מהרגיל (אולי: יותר מהאידאל האישי שלי?!) בהגדרת superflows חדשים במערכת. כן! אנחנו רוצים לעבוד יותר agile ולתת לאנשים יותר ויותר אחריות והשפעה, אבל בנקודת הזמן הזו – תוצאות טובות יותר מושגות כאשר לפחות את ה flows העיקרים – מוגדרים מרכזית ע"י הארכיטקטים.
כאשר מפתחים עושים שינויים ושיפורים ב flows – זו סיבה לשמחה (אלא אם בכך הם סותרים עקרונות או flows אחרים במערכת). - אנו מנסים לקדם בקוד כמה עקרונות:
- קידום תרבות של הצגת פתרונות – ולא רק בעיות (בכל ה R&D).
- קוד פשוט להבנה – עדיף יותר על פני קוד קצר או מתוחכם. אם אתם קוראים של הבלוג זמן רב, אתם אולי יודעים שזו הנטייה הטבעית שלי – אבל זה לא הסטנדרט הברור של ריילס (שם עקרונות של קוד קצר ו DRY – מושרשים עמוק בקהילה).
- יותר כלי monitoring ו supportability עבור הפיתוח. בכתיבה של כל פיצ'ר – לחשוב איזו השקעה תהיה משתלמת כאשר לפיצ'ר הזה תהיה בעיה ב production. כלי supportability יכולים להציג חווית שימוש עלובה למדי – כל עוד הם עוזרים.
- הכנסה של אוטומציה / בדיקות יחידה / בדיקות-API / בדיקות-אינטגרציה. בכל ארגון שראיתי בעשור האחרון זו הייתה המגמה – אבל אנחנו עכשיו צריכים את זה יותר.
אני מודע לכך שעדיין אין פה Design Patterns הנדסיים משמעותיים (אולי מלבד MSA) – אבל זה מה שעובד, ואנו עושים את מה שעובד – ולא רק מה שמתאים לציפיה מסוימת (ארכיטקטורה = "תרשימים של ריבועים"). סורי! 🙂
זהו… מקווה שנהניתם, ואולי אף השכלתם. כרגיל – אשמח לתגובות.
שיהיה לנו בהצלחה!

