הכנס השני לארכיטקטורת תוכנה (30 לנובמבר – 1 בדצמבר)

בסוף החודש הקרוב, 30 בנובמבר – 1 בדצמבר, יתקיים בהרצליה הכנס השני לארכיטקטורת תוכנה.

הכנס מאורגן ע\"י IASA ואילתם.

IASA, למי שלא מכיר, הוא ארגון עולמי שמנסה ליצור קהילה גלובלית עולמית של ארכיטקטים – בכדי לקדם את המקצוע הזה, ואת המקצועיות שלו ברחבי העולם. ל IASA יש סניף ישראלי – והחבר\'ה שם מנסים לארגן הרצאות מעניינות ודיונים בין אנשי מקצוע (כלומר: ארכיטקטים) על דילמות של המקצוע (ארכיטקט מהו? כיצד טרנד האג\'ייל משפיע על תפקיד הארכיטקט, וכאלה).

ILTAM, היא קהילה מקצועית, רחבה יותר, המורכבת בעיקר מחברות הייטק גדולות ומבוססות (לא רק חברות תוכנה נטו: אלתא, 3M, וצה\"ל למשל – הם חברים) במטרה לקדם ולשתף ידע.

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

היום ראשון מורכב מהרצאות. הנה התכנית:

ביום שני מתקיים Tutorial בהדרכתה של Rebecca Wirfs-Brock (שהגיעה לארץ במיוחד, אני מניח), בנושאי ארכיטקטורה באג\'ייל ו Quality Attributes בפרט (הנה פוסט שפירסמתי בנושא, אם אתם רוצים לקבל מושג במה מדובר).

האם כדאי לבוא?

תקשיבו, זו שאלה דיי אינדיבדואלית, ואני בד\"כ זהיר במתן המלצות. בכל זאת, כשאני מסתכל התכנים – נראה לי שהצליחו לרכז באמת שורה של נושאים ומרצים מעניינים ביום הראשון – שרלוונטיים לאנשי-תוכנה כמעט מכל הסוגים.
היום השני הוא באמת ממוקד יותר לארכיטקטים, או מי שרוצה שהתעמק בטכניקה תאורטית שהתמורה שלה להשקעה היא ארוכת-טווח. אני לא יודע להמליץ ספיצית על ה Tutorial שנבחר, אבל אם הוא נבחר באותה רוח של בניית האג\'נדה ליום הראשון – ייתכן בהחלט וזה יהיה Tutorial מוצלח!
  • אני נותן הרצאה על מיקרו שירותים (עדיין לא החלטתי בדיוק איך להעביר את הנושא…). אם אתם מגיעים – קפצו לומר שלום!
  • קצת אחרי ייתן הרצאה יונתן ממן, שכתב כאן פוסט אורח בנושא – ממש לאחרונה.
  • הנה אתר הכנס: http://conference70.wix.com/sw-architecture
  • בטופס ההרשמה שבאתר, ניתן לקבל הנחה אם מזינים את המילה \"Presenter\", בסעיף של קבוצת שיוך (אופס, אני מקווה שבאמת היה מותר לי לספר את זה…).
שיהיה בהצלחה!
ליאור
 

על Performance Monitoring ו New Relic

New Relic (בקיצור NR) הוא כלי ה Application Performance Monitoring (בקיצור APM) מהמוערכים שזמינים בשוק היום.
Relic הוא שריד קדום, והשם New Relic נבחר כמעט כבדרך אגב: היזם (Lew Cirne) השתעשע בתוכנה שמוצאת צמדי-מלים המורכבות מהאותיות של שם המשתמש, ובעת רישום החברה זה השם שנבחר, כמעט באקראיות. מאז הוא נותר.

NR הוא לא יחיד: יש את AppDynamics (שמכוון יותר ל Enterprise), או Nagios (פתרון Open Source) נפוץ למדי – ויש עוד רבים.

NR איננו זול: הוא עולה כ 100-150$ ל host לחודש. אם מנטרים כמה עשרות שרתים יכולים להגיע בקלות לכמה אלפי דולרים בחודש, אבל אנחנו (כמו עוד לקוחות רבים של פתרונות ה Premium) – החלטנו שזו השקעה משתלמת.

אנו ב Gett משתמשים ב NewRelic, ובכלי משלים בשם Graphite, שהוא יותר תשתית ל\"הרכבה עצמית\" של monitoring. יש גם את StackDriver. דיי נפוץ לראות ארגונים שמשתמשים ביותר מכלי אחד ל APM.

ל APM יש שני שימושים עיקריים:

ניטור בעיות ב production בזמן אמת
הרבה תקלות טכניות ניתן לזהות ע\"י מדידת שיוניים בביצועי המערכת.
לעתים, לא נזרקות הודעות שגיאה חריגות, ושום Alerts מרכזי שכיוונתם ואתם מנטרים לא מזהה משהו חריג – אבל משהו רע קורה למערכת. לפני שהתקלה ממש משפיעה על המשתמשים – יש סימנים מקדימים: לעתים אלה יהיו spikes של עומס על המערכת, ולעתים דווקא ירידת עומס בלתי מוסברת: כמו צונאמי שמתחיל בירידת מפלס הים… וקצת אח\"כ הוא מכה.
אם העסק שלכם מבוסס על זמינות המערכת, בוודאי תרצו לאתר את הסימנים המקדימים הללו – ולהגיב בהקדם.
שיפור ביצועי המערכת
מדי פעם, מחליטים שהגיע הזמן לשפר ביצועים. אולי בגלל feature חדש שמעמיס על המערכת מעבר לצפוי, ולעתים בגלל שהמערכת כבר \"לא סוחבת\" כפי שסחבה פעם (או שסתם נמאס לכם לשלם עשרות אלפי דולרים בחודש על Infrastructure).
NR (וכלים דומים) יכולים לספק במהירות ובקלות ניתוחים התחלתיים טובים לצווארי הבקבוק העיקריים במערכת. לפעמים הניתוח של NR כמו שהוא – מספיק בכדי להבין את השיפור המדויק שיש לבצע  (למשל: שאילתת SQL יעילה יותר), לעתים אחרות NR מצביע על אזור הבעיה, ואז מתחילים באיטרציות של ניסויים / הוספת monitors (לעתים ב new relic, ולעתים בכלים קרובים יותר לקוד) – עד לאיתור המדויק של הבעיה ומציאת הפתרון.

במהלך הפוסט אתמקד ב New Relic, אבל רבים מהעקרונות שאציג – נכונים לכלים רבים אחרים.

הטריגר לפוסט, אגב, הוא כוננות \"Tier 1\" שעשיתי השבוע. זו כוננות ב Gett לקבל טלפון אם יש Crisis במערכת, באחת מהמדינות, לבצע ניטור ראשוני ולהעיר את הצוות שיטפל במשבר. New Relic הוא כנראה המקום הראשון שאבדוק בו, על מה המהומה. בלילות כאלו – זהו חבר קרוב.

צורת העבודה של New Relic

New Relic תומך בסביבות קוד מסוימות:

  • רובי
  • ג\'אווה
  • NET.
  • פייטון
  • PHP
  • node.js

בכדי לחבר את השרת שלכם ל new relic יהיה עליכם להטמיע ספרייה קטנה בקוד שתאסוף נתונים מתוך האפליקציה ותשלח אותם לשרתים של NR. NR הוא פתרון SaaS.

על כל שרת שאנו מנטרים בעזרת NR, מתקינים agent. ה agent הזה יודע לאסוף נתונים מתוך מערכת ההפעלה, מתוך הקוד שלנו (שלו הוספנו את הספרייה הקטנה של NR) ועוד סדרה של שרתים – בעזרת מערכת ה Plugins של NR. ישנם Plugins לעשרות אפליקציות מעניינות שנרצה לנטר: שרתי ווב (כמו nginx), בסיסי נתונים, memcached, רדיס, AWS, ועוד

בכדי לצמצם את ההשפעה של ה agent על השרת עצמו, NR מתמקדת באיסוף נתונים בחשיבת עלות (ערך לניתוח ביצועים) / תועלת (ההשפעה על המערכת באיסוף שלהם). כמו כן ה agent מפחית את ההשפעה שלו על השרת בו הוא רץ בכך שהוא אוסף buffer של נתונים ושולח אותם כ batch פעם בדקה (זו החלטה סבירה מכיוון שהניתוח עצמו נעשה כמה דקות אחורה, ולא באמת ב realtime). שליחת Alerts מה Agent, כמובן – מתבצעת בו במקום.

השרתים של NR אוספים את כל הנתונים שה agents שולחים ומבצעים קורולציה (תיאום) ביניהם – לתמונה אחת ואחידה.

מקור: New Relic

חלק מה\"קסם\" שגרם ל New Relic להיות מאוד פופולארי הוא בהצגת מדדים שימושיים (ולא סתם spam של נתונים) בצורה מאוד נוחה לגישה, ומאוד אינטואטיבית. מרגע שאני מחבר את השרתים שלי ל NR – אני יכול לעבוד מייד, ויש לי סט מסודר והרמוני של כלים ותצוגות לעבוד איתו.

למשל: בצפייה ב Dashboard הראשי, ניתן ללחוץ על כפתור שמציג את הנתונים הנוכחיים, מול נתונים באותה השעה אתמול, ומול נתונים באותה השעה לפני שבוע.

בדומה לחוויית השימוש ב iPhone, גם כאן לא ניתן לקנפג את הפונקציה הזו יותר מדי: אינני יכול לבחור להציג השוואה של \"לפני חודש\", ושל \"ממוצע של שבוע שעבר\". אם ארצה, אוכל לפתוח 3 חלונות בדפדפן ולבצע פילטורים בכדי להגיע לתצוגה של המצב הנ\"ל. זה יקח זמן ויהיה קצת פחות נוח לשימוש. אבל את ההשוואה של \"אתמול\" ו\"לפני שבוע\", שזו השוואה שימושית למדי – אני מקבל בלחיצת כפתור אחת.

זהו Tradeoff בין פשטות ושימושיות, על חשבון יכולת להתאמה אישית.

NR יודע לנטר גם זמן רנדור של אפליקציות ווב בדפדפן ואפילו אפליקציות Native Mobile. יש גם מוצר ל Analytics עמוקים יותר על הנתונים הסטטיסטיים, בשם \"Insights\". למרות הכל – בפוסט זה אתמקד רק במוצר ה APM לניתוח נתוני ביצועים מהשרת.

על מדד ה Apdex

מדד ה Apdex (קיצור של Application Performance Index), הוא מדד שמזוהה מאוד עם NR, מכיוון שזו עושה בו שימוש אינטנסיבי במוצר.
Apdex לא הומצא ע\"י NR, אך בגלל השימוש הנרחב שלו במוצר, כדאי מאוד להבין מה הוא מתאר וכיצד הוא מתנהג.
עצם הרעיון של Apdex נובע מהחסרונות המובנים של מדדים כגון ממוצע או Median. מדדים שכאלו יכולים להסתיר בקלות התנהגויות חריגות ובעייתיות במערכת.

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

כאשר אנו \"מסכמים\" את ההתנהגות תחת מדדים כמו ממוצע או Variance – אנו מאבדים מידע חשוב.

הרעיון של Apdex הוא להתמקד בחווית המשתמש: כמה משתמשים מקבלים את ההתנהגות שהם מצפים לה, וקצת פחות במספרים סטטיסטיים שאנו, כבני-אדם, לא כ\"כ טובים בהבנה אינטואטיבית שלהם.

מדוע \"Apdex עובד\"? – אני לא יודע להסבר. אך הוא נחשב מדד מוצלח.
כמובן שגם הוא לא מושלם.

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

בנוסף ל\"בולענים סטטיסטיים\", NR עשוי \"לעגל פינות\" בגרפיים המכילים מידע רב – כך שהתמונה האמיתית תאבד לגמרי. אל תתעצלו: תעשו Zoom In על ציר הזמן (טיפ: בחירת זמן עם העכבר על הגרף) ותפלטרו שוב ושוב את הנתונים לקבלת תמונה יותר ויותר מדוייקת.

מקור: New Relic

מדד Appdex עובד כך:
עבור כל שרת (או טרנזקציה מרכזית) אנו מגדירים את הזמן הממוצע שאנו מצפים מהשרת להגיב. זוהי \"נקודת הייחוס\", והיא מסומנת כ Apdex-T.

  • כל טרנזקציה שתסתיים בזמן שהוגדר או מהר יותר – תחשב ל\"משביעת רצון\" מבחינת הביצועים. (בירוק בתרשים למעלה)
  • כל טרנזקציה שתסתיים בטווח שהוא בין הזמן שהוגדר כ\"משביע רצון\", עד לפי-4 מכך – תחשב כ \"נסבלת\" מבחינת ביצועים (בכתום / ירוק זית – בתרשים למעלה).
  • כל טרנזקציה ארוכה מכך, או טרנזקציה שנסתיימה בשגיאה (למשל: HTTP 500) – תחשב ל \"מתסכלת\".
מדד Apdex הוא אחוז הפעמים בהן הגענו לטרנזקציות \"משביעות רצון\" ועוד חצי מהפעמים (משקל פחות) בהן הגענו לטרנזקציות \"נסבלות\". טווחי הערכים של Apdex נעים בין 0.0 (אסון) ל 1.0 (מעולה)

הנה דוגמה:

בשרת הזה אנו מצפים לביצוע טרנזקציה תוך 125ms, ומקבלים זאת ברוב הזמן.
מדד של 0.87 הוא טוב – אך לא מעולה. ניתן לשאוף לקצת יותר.

במדידות הדפדפן (NR מסמלצת גם קריאות מדפדפן לשרת – בכדי לבדוק את זמן התגובה של דף באתר, כולל הרינדור) הגדרנו נקודת ייחוס של 12 שניות (פשוט לא הגדרנו נקודת ייחוס) – ולכן אנו נמצאים ב Apdex 1.0 עגול. מעולה! ;-).

במעבר עם העכבר על הגרף אני נחשף למידע נוסף, ויכול לראות שהתנודות ב Apdex שלנו נעו בין 0.84 ל 0.92 בשש שעות האחרונות (נקודת החיתוך) – יציבות סבירה לכל הדעות.

rpm (קיצור של requests per minutes) הוא המקבילה של tps המקובל יותר (transactions per minute) ואנו כרגע עומדים (אני רואה במעבר עם העכבר) על כ 8.43k rpm 130tps, או כ 140tps, עם מגמה קלה של עליה (יש בד\"כ מחזוריות ברורה, יומית ושבועית, לשימוש בשירותים)

מדדים עיקריים ש New Relic מספק

יש מדדים רבים ש NR מספקת, אספק להלן \"טעימה\" רק בכדי להרגיש במה מדובר. כמו כן, מחמת הספק, טישטשתי נתונים מהמערכות שייתכן ולא נרצה לחלק עם \"כל העולם\".

הנה ה Dashboard העיקרי של NR:

  1. זהו הגרף הראשי המציג \"לאן הלך הזמן במערכת\". הוא מציג את הממוצע של הזמן שהושקע בבסיס הנתונים, ה Application Server, או קוד האפליקציה שלנו (במקרה הזה – רובי). בגלל ש NR מכיר את ריילס, הוא מסוגל לבצע חיתוך ולהראות לנו כמה זמן הושקע ב ActiveRecords (ספריית ה ORM של Ruby on Rails).
    במבט חטוף ניתן לראות שזהו שירות שמשתמש בתכיפות בבסיס הנתונים (צהוב) – אך יש לו גם הרבה עבודת CPU (תכלת).
  2. זהו הגרף שהצגתי קודם לכן, של Apdex ו Throughput.
  3. זוהי רשימת הטרנזקציות היקרות ביותר (שקלול של זמן ביצוע הטרנזקציה x שכיחות הטרנזקציה). אם נרצה לבצע שיפורי ביצועים בשרת – זה המקום להתחיל בו.
  4. Error rate של השרת – כמות הטרנזקציות שלא הסתיימו כשורה.
  5. רשימת ה alerts שעלו מהמערכת.
  6. רשימת השרתים שב cluster. ניתן לראות CPU, disk-usage, צריכת זכרון ו Apdex – לכל שרת בנפרד.
כשאנו רוצים לשפר ביצועים, הכי הגיוני יהיה להתמקד בטרנזקציות שגוזלות הכי הרבה זמן (משוקלל). ניתן לבחור טרנזקיות ע\"פ מדדים שונים (הכי אטיות, גוזלות הכי הרבה זמן, בעלות Apdex הנמוך ביותר, וכו\') – ואך לעשות Drill down לטרנזקציה:

  1. אנו יכולים לראות את השונות בזמני התגובה של הטרנזקציות. האם יש הרבה אטיות / מהירות במיוחד – או שיש סוג של זמן קבוע שמסביבו כולן סבות? (צהוב – האחוזון ה 95% האטי, אדום – ה media, ירוק – הממוצע)
  2. לאן הולך הזמן בטרנזקציה: GC, קוד, בסיס נתונים, מערכות 3rd Party וכו\'.
  3. כמה טרנזקציות לדוגמה (לרוב מהאזור הפחות טוב). הנה טרנזקציה שלקחה 1.3 שניות.
  4. למרות שמפתה למדוד את הטרנזקציה הגרועה ביותר (#3), לרוב זהו מקרה קצה שעלול להטעות (למשל: בדיוק היה אירוע Full GC).
    אני מעדיף לבחור את השורה השלישית, שהיא קצת יותר נורמטיבית – ולחקור אותה. להזכיר: הזמנים המדוברים הם Wall time clock – וזמן בו הקוד נמצא ב block גם הוא נספר.
והנה כבר ה Trace ש NR אוספת על הטרנזקציה:

לעתים ניתן לזהות מתוך ה trace את הבעיה. בעיות נפוצות הן:

  • \"בעיית n+1\" – בה יש קשר master-detail בבסיס הנתונים, עושים שאילתה אחת לאובייקט האב ועוד n שאילתות – אחת לכל אובייקט בן (במקום שאילתה אחת עם inner join)
  • שאילתה בודדת יקרה ביותר – ניתן ללחוץ על שורה ולראות את השאילתה הקונקרטית שבוצעה.
  • בעיית קוד (מתאפיינת לעתים קרובות ב GC גבוה).
בתמונה למעלה ניתן לקראות דוגמה שנראית כמו בעיית קוד – 2 פעולות \"תשתית\" לכאורה, שכל אחת לוקחת יותר מ 300ms. סימן השאלה מסביר ש NR לא עשתה Drill down ומנחה מה לעשות הלאה.

Drill down לתוך הקוד NR בחר במודע לא לעשות – כדי לא להשפיע לרעה על ביצועי האפליקציה. Drill Down שכזה עשוי להיות יקר בצורה מטרידה. במקום זאת, ניתן להוסיף לקוד custom monitors. למשל, הוספת הפקודה הבאה בקוד:

add_method_tracer  :my_method  \'Custom/MyClass::my_method\'

בתוך המחלקה MyClass תיעזר בספריה של NR שהוספנו לקוד ותבצע רישום זמני כניסה ויציאה מהמתודה my_method בה אני חושד שאיננה יעילה. הרישומים יופיעו כמובן ב NR רגעים מספר מרגע העדכון.

בכדי לא לבצע deploy לכל המערכת, ניתן בקלות יחסית (לפחות ברובי) לכתוב קוד ש\"יפתח\" ו\"יסגור\" monitoring על מתודות מסוימות ע\"פ קונפיגורציה שנשלחת מרחוק. ניתוחים זריזים שכאלו יכולים להיות בעלי חשיבות רבה מ-2 סיבות:

  1. ייתכן וניסיון לשחזר את הבעיה שלא ב production, עם הגדרות, נתונים, ועומסים ששונים מסביבת production – פשוט לא יצליח.
  2. כלי שמבצע ניטור \"שתי וערב\" על הקוד – אתם כנראה לא רוצים להביא ל production. הכלי עצמו יכול להכפיל בכמה מונים את כל זמני הריצה של המערכת…

אם אנו רוצים להיעזר ב NR לבצע ניתוח מקיף יותר וממוקד, אנו יכולים להתחיל \"X-Ray session\" שעוקב אחר דגימה גדולה יחסית של טרנזקציות (נאמר 50 בשעה), למשך זמן מוגבל (נאמר: חצי שעה). על טרנזקציות אלו יאסף כל המידע האפשרי – והמידע ייתן לנו מידע על מגוון טרנזקציות (ולא דווקא האטיות יותר).

עוד יכולות מעניינות (בקיצור)

Developer Mode

ה Developer Mode הוא גרסה רזה של NR שניתן להפעיל On-Premises – על מכונת הפיתוח, ויכולה לשמש לצורך ניטור ראשוני של הקוד לפני שהוא מגיע ל production. למשל, Queries בעייתיים ניתן לאתר בשלב מוקדם כבר כך – ולפני שמגיעים ל production. כמובן שהתנהגות המערכת ב production ועל מחשב של מפתח היא שונה – ולא כדאי לנסות להסיק יותר מדי מתוך ה Developer Mode.

ניתן להפעיל את ה Developer Mode מתוך קובץ הקונפיגורציה, newrelic.yml, ע\"י הפיכת הערך developer_mode ל true – ואז יהיה ניתן לגשת ל dashboard דרך http://localhost:3000/newrelic.
ה Dashboard של ה Developer Mode הוא הרבה פחות אטרקטיבי ועשיר – אבל הוא מכיל את נתוני הבסיס החשובים על הטרנזקציות.

דו\"חות מוכנים

ל NR יש סדרה של דוחות מגניבים – חלקם אפילו שימושיים! למשל:

  • חישוב ה up-time של המערכת (למשל: 99.9734%) בתקופת זמן נתונה.
  • בניית גרף ה Scalability של המערכת – עלייה בזמני התגובה של השרת ככל שמספר הבקשות גדל (ואולי יש עוד nodes ב cluster בכדי לשרת). גרף זה עוזר לזהות צווארי בקבוק פוטנציאליים.
  • Speed Index – השוואה של הנתונים של השרת שלנו מול אתרים אחרים בתעשייה (לא ראיתי כיצד זה יכול להיות מועיל)
  • מיפוי ויזואלי של התקשורת העיקרית בין השרתים שלכם.
ניתוח אוטומטי של NR על תלות בין כמה שרתים שלנו
Insights
את המידע העצום ש NR אוספת על השרתים שלכם ניתן לתחקר באופן חופשי (יחסית) ובעזרת שפת NRQL (שפה דומה ל SQL) – על מנת לבצע ניתוחים ש NR לא מספקת \"Out of the box\"

סיכום

שוק ה APM עבר כמה סבבים של שיפורים בשנים האחרונות. New Relic מציג את הזן החדש של המוצרים: קל מאוד להקמה, קל לשימוש והבנה. צבעוני וידידותי למשתמש. כמעט מהנה – כמעט, כי עדיין עדכון של תצוגות הוא עדיין קצת אטי מכדי לאפשר לניתוח \"לזרום בקצב החשיבה\".

למרות הכיף והנוחות ש New Relic מספק, ניתוח ביצועים של מערכת היא עדיין אחת המטלות שאני מרגיש שבהם אני מנצל את מיטב המשאבים שלי: ללא ידע עמוק על המערכת, ידע תאורטי טוב במערכות הפעלה / רשת / סביבת הריצה הספציפית, וקצת חשיבה מקורית – קשה להגיע בניתוח ביצועים לתוצאות איכותיות.

איני יודע כיצד אנשים אחרים רואים ניתוח ביצועים של מערכת – אבל אני רואה במלאכה זו פעילות מקצועית ברמה הגבוהה ביותר.

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

—–

קישורים מעניינים

הפלטפורמה של NR (מתוך הבלוג הרשמי)
NR ב highscalability.com (פוסט מ 2011)

Crash Course ב NR (וידאו של חצי שעה). מוצלח יותר מרוב החומרים שמצאתי בנושא ברשת
New Relic vs. AppDynamics (פוסט מבית טאקיפי)

שלום, מונגו! (MongoDB)

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

בסיס נתונים לא-רלציוני, בשם MongoDB תפס את המקום החמישי הנחשק במדד הפופולריות של DB-Engine, ובעצם הפך לבסיס הנתונים הלא-רלציוני הראשון שנכנס לחמישייה הפותחת [א].

יש היגיון מסוים בכך שדווקא MongoDB הוא הראשון מבין בסיסי הנתונים מסוג NoSQL לפרוץ לרשימת בסיסי הנתונים הרלציוניים: מבחינות מסוימות הוא קצת יותר דומה להם: יש לו (יחסית ל NoSQL DB) עושר רב של יכולות, וותק, וזמינות של תמיכה מסודרת (לאחרונה גם בישראל) – הכוללת "גרסאות Enterprise".

האופן בו אנשי מונגו אוהבים להציג את בסיס הנתונים: מעט פחות יכולות והרבה יותר Scalability מ RDBMS + הרבה-הרבה יותר יכולות מבסיסי נתונים NoSQL אחרים…

מקור השם של מונגו הוא מהמילה humongous (גדול, אדיר), אולם היכולת לטפל בכמויות גדולות של Data היא כנראה לא התכונה הבולטת, המושכת אליו כ"כ הרבה מפתחים. בעולם ה Internet Scale דווקא אני שומע קולות שמחשיבים את Mongo כבסיס נתונים בעל יכולות Scale מתונות.

הפופולריות של MongoDB (בקיצור: "מונגו") נובעת מהקלות שהוא מספק למפתחים בעזרת הסכֶמה (db schema) הגמישה שלו. לא עוד Migrations, או יותר גרוע: תקלות migration על סביבת production (המוכרות כנראה היטב לכל מי שעבד עם SQL בסביבת ה Enterprise).
מונגו הופך להיות מועמד לקחת את מקומו של MySQL כבסיס נתונים זול ויעיל ל"משימות רגילות" – ויש הרבה מאוד כאלו.

הבהרה חשובה: מונגו הוא לא תחליף ישיר ל RDBMS. הוא שונה. כאשר זקוקים להרבה joins על נתונים, ופעולות רוחביות ("SELECT") על הרבה אובייקטים – מונגו (או Document-Based Database אחר) כנראה לא יתאים. מונגו מתאים כאשר יש אובייקטים עצמאיים ("לקוח", "אתר", "משלוח"), ורוב הפעולות נעשות על האובייקטים הבודדים – ורק מיעוט הפעולות הן רוחביות ("כל המשלוחים שיצאו אתמול ויגיעו מחר").

סקר המראה מה מושך מפתחים ב NoSQL: לא, זהו לא ה Scale במקום הראשון – אלא דווקא הגמישות. מקור

בפוסט זה אינני מתכוון לגעת בנושאים של Scalability או Availability של מונגו או בכלל ,לא הקדמה כללית ל NoSQL (נתתי כזו פעם, היא עדיין רלוונטית דייה) ולא דיון על ה CAP theorem.

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

… עוד קיטלוג של בסיסי נתונים NoSql

מונגו DB – מי זה הבחור הזה? 

ראשית הייתי רוצה להזכיר בכמה מילים את מודל ה Document-Based Database בכלל, ואת זה של מונגו בפרט.
אם נשווה את המודל של Mongo לבסיס נתונים רלציוני ההשוואה תראה בערך כך:

מונגו מנהל מספר בסיסי נתונים, ממש כמו RDBMS. יש אוספים של "מסמכים" (ניתן לחשוב עליהם בשלב זה כמחרוזות של JSON). בתוך ה JSON יש ערכי key:value (= שדות) שמונגו מודע להם (בניגוד לבסיס נתונים K/V – שם הוא לא).

ניתן ליישם הדמייה ל KVDB (בסיסי נתונים מסוג K/V) על גבי RDBMS בכך שמייצרים טבלה עם 2 עמודות: ID ו BLOB של נתונים (למשל: קובץ JSON).

באופן עקרוני, בסיס הנתונים הרלציוני יספק את היכולת הבסיסית של KVDB, אך ללא הביצועים / Scalability / זמינות שניתן לקבל מבסיס נתונים KVDB. הוא עתיד "להתפרק" אחרי מאות מיליונים ספורים של רשומות והקצב בו הוא ישרת את לקוחותיו לא הולך להיות מרשים במיוחד.

בסיס נתונים מבוסס מסמכים (בקיצור DBDB) הוא כמו[ב] KVDB עם 2 הבדלים משמעותיים נפוצים:

  • הוא מכיר את תוכן המסמך, והפורמט שלו – והוא מאנדקס חלקים ממנו, לצורך Queries רוחביים יעילים ו/או מתוחכמים.
  • לעתים קרובות: הוא מאפשר הגדרת קשרים (למשל: היררכי) בין המסמכים.
אפשר גם "לקמבן" התנהגות כזו בעזרת RDBMS בעזרת כמה stored procedures, מה שעשוי ליצור יישום יעיל אפילו פחות מ"חיקוי ה KVDB" שתארתי למעלה.

דייט ראשון – התקנה וכלים ראשוניים

ההתקנה של מונגו היא פשוטה למדי:

  • פתיחת ZIP הכולל את קבצי ה exe. לאיזו תיקיה (ב"חלונות").
  • יצירת תיקיה ריקה בשם data/db/ (יחסית לכונן בו מונגו מותקן) – התיקייה בה מונגו מאכסן את ה data שלו. ניתן לקנפג תיקיה אחרת, כמובן.
בין קובצי ה exe ניתן למצוא כמה מעניינים:
  • mongod.exe – השדון החרוץ (daemon [ג]) של בסיס הנתונים. כלומר: התהליך המרכזי.
  • mongo.exe – ה Admin Shell, דרכו ניתן לבצע פעולות רבות.
  • mongoexport.exe – פעולות import/export של קבצי נתונים (BSON).
  • mongodump.exe/mongorestore.exe – גיבוי / אחזור של בסיס הנתונים (כקובץ בינארי דחוס).
  • mongostat.exe – תהליך שאוסף נתונים על השימוש ב mongo.
בגדול, כדי להפעיל ולנסות את mongo בצורה קלה יש פשוט להפעיל את mongod.exe ואחריו את mogno.exe. דרך ה console ניתן לבצע / לנסות את רוב הפעולות המשמעותיות של בסיס הנתונים – ולקבל פידבק מהיר.

בואו ננסה משהו:

  1. ביקשנו לראות אלו בסיסי נתונים קיימים בעזרת פקודת show dbs. בסיס הנתונים היחידי הקיים הוא local, למרות שמונגו חיבר אותנו בכניסה ל test – בסיס נתונים ריק. מונגו הוא לעתים רבות עצלן, וייצור אובייקטים מבנים רק כאשר יש בהם תוכן ממשי.
  2. השתמשנו ב use בכדי לעבור לבסיס נתונים חדש (שעדיין לא קיים). מונגו יזכור אותו, אך עדיין לא ייצור אותו.
  3. פקודת db בודקת באיזה בסיס נתונים אנו כעת.
  4. נתחיל בהכנסות: נכניס לתוך אוסף שעדיין לא קיים (people), בבסיס הנתונים שעדיין לא קיים ("db" הוא האובייקט המייצג את בסיס הנתונים הנוכחי) רשומה – כלומר מסמך, המתאר את ג'ון.
    ברגע זה מונגו ייצור את בסיס הנתונים ואת האוסף (collection) בשם people – ויכניס לתוכו את הרשומה.שימו לב שמונגו יקצה קובץ בשם data/db/myNewDb.0 בו הוא יאכסן את בסיס הנתונים. למרות שיש לי רק מסמך אחד קטן – מונגו בחר להקצות אצלי כ 200MB בדיסק – מה שעשוי להראות קצת מבהיל. הסברים – בהמשך הפוסט.

    לאחר שלמונגו יש קובץ המייצג את בסיס הנתונים – הוא יכניס במהירות את המסמך השני – לו סכמה דומה אך רחבה יותר.

  5. בשלב זה אני בודק אלו אוספים יש בבסיס הנתונים: מכיוון שבסיס הנתונים נוצר – האוספים כבר קיימים (people ואוסף האינדקסים – שמכיל רשומות ראשוניות בלבד). בדומה ל RDBMS, מונגו משתמש בעצמו כדי לנהל מידע מערכת.
  6. אני אבצע שאילתה (מקבילה ל * SELECT) על אוסף האנשים.
    הממ… אני לא זוכר שהכנסתי שדה בשם id_, אתם זוכרים?!

סביבת ה shell, כדרך אגב, היא javaScript לכל דבר – וניתן להשתמש ביכולות ה JavaScript שאתם מכירים (var, פונקציות ועוד).

בלינק זה תוכלו למצוא עוד פקודות רבות שניתן להפעיל ב shell. בעיקרון ה shell הוא כלי מצוין ללמידה, ניסוי, או תפעול של mongoDB.

שדה ה id_

כפי ששמתם לב מונגו הוסיף לנו שדה בתוך המסמך בשם id_. הוא יוסיף שדה זה אם לא הגדרנו אותו בעצמנו (בד"כ נשאיר זאת לו).

מבנה ה id_

תכונה מעניינת של ה id_ (נקרא גם object id) היא שה timestamp הוא ראשון, מה שמאפשר לבצע מיון של אובייקטים ע"פ זמן היצירה, בקירוב. הזמן נשמר ע"פ epoch[ה] של unix.

בבסיסי נתונים מסוג KVDB ו DBDB יש חשיבות לא-מעטה לדרך בה בונים את ה ids/keys:

  • ה key הוא לעתים קרובות המפתח ל partitioning, ויכול להשפיע רבות על הביצועים.
  • יעילות של ה hash function יכול להיות משמעותי כאשר מדברים על המון פעולות בשנייה.
  • מה קורה כאשר שני ids מתמפים לאותו אובייקט? זה לא סביר כאשר יש אלפי ערכים – אך סביר יותר, כאשר יש מיליארדים.

חיפוש ואינדקסים

במונגו, ניתן לחפש אחר ערכים בתוך collection בעזרת פקודת find, למשל:
db.persons.find({ lastname: 'Smith' });
החיפוש נעשה ע"י "דוגמה" או prototype: אנו מספקים את הערכים שאנו מחפשים ומונגו יחזיר לנו cursor המצביע על האובייקטים שמכילים את הערכים הללו.
ניתן לעשות חיפוש קצת יותר כללי, בעזרת query modifiers המספקים יכולות מספריות / לוגיות, למשל:
db.persons.find( { childrenCount: { $gt: 3 } } );
gt$ הוא קיצור של greater than, כלומר: אנשים בעלי 3 ילדים או יותר. שדה שלא הוגדר באובייקט (כלומר: מסמך) מסוים, יהיה בעל ערך 0 – לצורך העניין. יש גם query modifiers נוספים כגון min$ או or$ ועוד.

אפשר לחפש על אובייקטים מקוננים, למשל address.city או להגדיר שאילותות מורכבות הכוללת מספר רב של שדות או query modifiers לדוגמה:

db.persons.find( { childrenCount: { $gt: 3, $lt 20 } } , { 'address.city': 'Holon' } );
הערה: הסיבה ששמתי את address.city בתוך מרכאות נובעת מג'אווהסקריפט. שימוש בנקודה איננו תקני במפתח של אובייקט.אפשר גם לצמצם את התשובה לשדות מסוים (בדומה ל RDBMS select) בעזרת אובייקט projection – אותו מגדירים בפרמטר השני של הפקודה find:

db.persons.find({ lastname: 'Smith' }, { tile:1, lastname: 1 });

בדוגמה זו אנו רוצים לקבל רק "תואר" ושם משפחה. ערכי ה "1" הם דרך מקוצרת לכתוב true – כלומר: החזר לי את השדה הזה. שימו לב: השדות צריכים להיות כולם true או כולם false – לא ניתן לערבב. יוצא הדופן היחיד הוא השדה id_, אותו ניתן להשמיט גם כאשר יש רשימה "פוזיטיבית" של שדות בהם מעוניינים.

מה עוד ניתן לעשות ב queries? ניתן לעשות הרבה. אציין שליפת מספר קבוע-מראש של ערכים מתוך מערך במסמך (נאמר תגובות בבלוג: רק 10 תגובות ראשונות מכל פוסט), או את היכולת לעשות שאילתות קיבוץ מורכבות (db.collection.group) – ולהוסיף להן פונקציות פילטור בג'אווהסקריפט, ad-hoc.

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

בעולם ה NoSQL עושים הפרדה בין "planned Queries" ו "Ad-hoc Queries".
Planned Queries הן כאלו שהתכוננו אליהן, לרוב בעזרת יצירת אינדקסים (פנימיים או חיצוניים לבסיס הנתונים), בעוד Ad-hoc Queries הן כאלו שעושים בצורה "ספונטנית" – ללא קיום של אינדקסים, pre-fetch או כל הכנה מקדימה.

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

אינדקסים

 
הגדרה של אינדקסים היא פשוטה למדי, דומה להגדרת החיפוש:
db.persons.ensureIndex( { firstname: 1 } );
ייצור אינדקס לשדה ה firstname.
db.person.ensureIndex( { "address.city" : 1 } );
ייצור אינדקס לשדה city בתוך תת-האובייקט (או המסמך) address. כמו כן:
db.person.ensureIndex( { lastname: 1, firstname: 1} );
ייצור אינדקס של מפתח המורכב מ2 שדות: שם פרטי, ושם משפחה.
התחביר ensureIndex מצביע על כך שאם אינדקס קיים – מונגו לא ישכפל אותו.
יש עוד אפשרויות אינדוקס רבות, עליהן ניתן לקרוא בתיעוד הרשמי.
מבני הנתונים המשמשים את מונגו. מקור

אכסון (Persistency)

שאלה שבוודאי מעניינת מאוד את מי שמגיע מרקע והבנה כיצד עובד RDBMS היא "מה קורה שם בתוך מונגו?" או "כיצד ממימשו זאת?".
בחלק זה אנסה לענות על שאלה זו בקיצור.מונגו כתוב ב ++C וש לו הפצות למערכות הפעלה שונות. מערכת ההפעלה אליה הוא תוכנן במקור היא Unix/Linux (כלומר: מערכת POSIX). יוצרי מונגו לקחו כמה החלטות Design מהותיות:

  1. מונגו יעבוד קודם כל עם זיכרון (עבור המהירות). מונגו אוהב זיכרון, והרבה!
    כל בסיס נתונים שמכבד את עצמו מרוויח מזיכרון, אך כשלמונגו חסר זיכרון – נפילת הביצועים היא משמעותית מאוד.
  2. עדכון של הזיכרון לדיסק יינתן (delegated) למערכת ההפעלה. התכנון המקורי התבסס על הדרך בה מערכת Linux עובדת, וספציפית פקודת mmap למיפוי קבצים – ישירות לתוך הזיכרון. המימוש עבור מערכת ההפעלה "חלונות" הוא כנראה דומה (במסגרת הכלים ש"חלונות" מספקת), אך ידוע כפחות יעיל.
    היתרונות של גישה זו היא קלות פיתוח שמונגו הרוויח, ואי שכפול caches בין מערכת ההפעלה למונגו (מה שמנצל את הזיכרון בצורה יעילה יותר). החיסרון: המנגנון מאחורי mmap הוא כללי ואינו האופטימלי-ביותר לצרכים של מונגו.
  3. למונגו יש רמות "durability" שונות הניתנות לקנפוג. האם כתיבה לבסיס הנתונים תכנס לתור של כתיבה לדיסק ("fire and forget" – דומה ל INSERT_DELAYED של MySQL) או האם כל כתיבה נכתבת במקום ל journal על הדיסק ("fully safe"). ברירת המחדל היא "fire and forget".

מונגו מאכסן כל Database בסדרת קבצים. הקבצים מתחילים בקובץ בגודל 64MB (למשל db.0) ומכפילים את עצמם בכל פעם (128MB – עבור db.1 ואז 256MB עבור db.2) וכו' עד שמגיעים לגודל של 2GB – ונשארים שם. מונגו מקצה יותר מקום ממה שנדרש למידע בפועל – כדי לא "להתקע" באמצע רצף של כתיבות. במערכת ההפעלה שלי הוא מקצה 200MB לכל בסיס נתונים שרק נוצר.

את הקבצים עצמם מונגו מחלק ל Extents – מן בלוקים הכוללים data או אינדקסים של collection מסוים (לא מערבבים סוגים ולא מערבבים collections).
גם בתוך extent של data, מונגו שומר לכל document מעט Padding – כלומר: יותר מקום ממה שנדרש, במידה והמסמך יגדל במעט. לדוגמה: עדכון של שדה לערך ארוך יותר או הוספה של שדה. המסמכים וה extents הם רציפים בדיסק / זכרון.

db.collection.dataSize();
יספק לנו מידע מה גודל ה collection בבתים, עבור המסמכים וה paddings שלהם.
db.collection.storageSize();
יספק לנו את גודל ה data extents של ה collection. כלומר ()dataSize ועוד מקום שהוקצה בדיסק ועדיין לא בשימוש.
db.collection.totalIndexSize();

יספק לנו את גודל ה index extents של ה collection, כולל שטחים ב extents שעדיין לא בשימוש.

סטטיסטיקות נוסף ניתן לקבל בעזרת אובייקט ה dbStat.

מונגו משתמש בפקודת mmap של לינוקס בכדי למפות extents (בלוקים בקבצים) לזיכרון. הקבצים הם כמו "גדר הגנה" בפני fragmentation שיכולה להיווצר באופן טבעי במערכת הקבצים.

כאשר מסמכים גדלים מעבר ל padding הקיים – הם מועברים למקום חדש עם padding חדש. מונגו לומד את קצב השינויים ב collection ולפיו מבסס את ה padding factor לפיו יקבע גודל padding חדש. גדילות תכופות של מסמכים –> paddings גדולים יותר.

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


fragments בתוך extents של מונגו. מקור.


הפתרון בעיקרון הוא לבצע de-fragmantation בעזרת פקודת compact.

הקושי:  למונגו יש thread יחיד לכתיבות, ופעולת compact תתקע את בסיס הנתונים כולו לזמן מה (כלומר: down time). אפשר לתזמן פעולות תחזוקה אלו או אפשר, בעזרת replication של nodes של מונגו – לעשות את זה offline.
זה כנראה לא מה שהייתם מצפים מ"בסיס הנתונים מהדור החדש" – אבל ככה זה.
עוד נקודה כואבת במונגו היא נעילות (לצורך consistency):
נעילות הן ברמה של בסיס נתונים (database). עד גרסה 2.1 – נעילות בכלל היו ברמת כל ה instance של מונגו. ישנן תוכניות לעתיד לשפר את רמת הנעילה במונגו.
המשמעות היא כמובן מגבלה משמעותית לביצוע כתיבות ב scale, לפחות כאשר אנו עובדים ברמת durability (או safety) גבוהה.
הברירה היא בין לוותר על מידה מסוימת של durability, לצמצם כתיבות, או להשתמש ב very-high-end SSD עם IOPS (פעולות IO בשנייה) גבוה במיוחד: איזה חצי מיליון IOPS, הייתי אומר. האפשרות השלישית – חסומה בחומרה הקיימת כיום, כמובן.
ה consistency במונגו מובטח רק ברמת פעולה על מסמך – ואין להניח על סדר הפעולות בכלל המערכת (isolation).
מה קורה כאשר רוצים לבצע שינוי במסמך א' בצורה אטומית (למשל: להגדיל ערך מספרי ב 3)?
או שמוכנים לספוג חוסר consistency מסוים, או שאפשר להשתמש בכלי מיוחד של מונגו בשם collection.findAndModify המקבלת הוראות ומבצעת שינויים במסמכים בצורה אטומית.
הפקודה תבצע שינוי אטומי במסמך אחר מסמך, אך כל פעם – במסמך בודד.
מה קורה כאשר רוצים לבצע שינוי במסמך ב' על בסיס של נתונים ממסמך א'?
לבעיה זו אין למונגו פתרון מובנה, ויש כל מיני פתרונות מקובלים / "design patterns" כיצד לפתור את הבעיה. בגדול סט הפתרונות מתבסס על יצירת מסמך שלישי זמני בשם (מפתיע:) transaction שמסייע לנהל את הפעולה.
נקודה אחרונה מעניינת היא נושא גודל המידע. לחוסר הסכמה של מונגו יש חסרון אחד ברור: יש לאכסן את ה keys בכל פעם, מה שיכול להגדיל את גודל המידע בצורה משמעותית הן בדיסק והן ברשת (מול ה clients של מונגו).
אמנם JSON הוא מבנה נתונים יחסית יעיל, וכאשר מונגו שולח / מאכסן מסמכים – הוא בעצם משתמש בצורה בינרית יותר יעילה שנקראת BSON [ד] (קיצור של Binary JSON), אך עדיין מדובר בבזבוז.

סיכום

מונגו הוא לא בסיס נתונים מושלם, אך כנראה שהוא מספיק טוב למערכות רבות, ויכול לסייע בקיצור זמני פיתוח ותחזוקה.
ב technology stacks כמו MEAN (קיצור של Mongo, Express, Angular and Node.js) – השימוש הטבעי של מונגו ב javaScript ו JSON מאפשר לכתוב פתרון קצה-אל-קצה בשפה יחידה: javaScript.

למרות שמונגו יחסית עשיר ביכולות, יש לא מעט ספריות ODM (קיצור של Object-Document Mapping), כגון mongoose או doctrine, המסייעות במידול הנתונים או בהעשרת היכולות בשימוש במונגו.

מונגו, ומודל ה documents בכלל, טבעי מאוד לפיתוח מערכות בהן היינו משתמשים בכלי ORM: מסמך (document) הוא סוג של אובייקט, ועבודת התרגום בין שפת התכנות לשפת בסיס הנתונים, ובחזרה – נחסכת מאיתנו. ביוחד כאשר יש לפרק/להרכיב "אובייקט" יחיד מטבלאות רבות.
מונגו הוא בד"כ מהיר מ RDBMSs, לא בגלל שהמפתחים שלו חכמים יותר – אלא בגלל שהם עשו בחירות תכנוניות שמעדיפות מהירות על דברים אחרים (למשל: ההחלטה ל durability לא מושלם, כברירת מחדל). בטלו את הבחירות הללו – והפערים יצטמצמו. בעוד מימוש של KVDB הוא יחסית פשוט על גבי RDBMSs קיים (עם scale מוגבל יותר), מימוש של DBDB על גבי RDBMS הוא קשה – בגלל תהליכי האינדוקס.

כמו בסיס NoSQL רבים אחרים, מונגו מוסיף אחריות על המפתח: לדאוג לשלמות ואחידות הנתונים. מונגו העלים סט בעיות אחד מהמפתח – אך חושף אותו לסט אחר (בשאיפה: מצומצם יותר) של בעיות אחרות.
כשאתם ניגשים לעבוד במונגו – על תצפו ל Internet Scale. הוא כנראה טוב ב Scale בהרבה מ RDBMS – אך לא טוב כמו Riak או Cassandra. כמו כן אל תצפו לבגרות של RDBMS – המבוססים על מודל שכבר בעבודה במשך עשרות שנים.

בכל זאת, אם אתם מתחילים לפתח מערכת חדשה, במיוחד מערכות ווב (במידה, "המגרש הביתי של מונגו") ובמיוחד כאלו שאינן מוגדרות היטב עדיין – יש סיכוי טוב שעם מונגו תתקדמו מהר וטוב יותר מאשר עם MySQL או PostgresDB.

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

זמן קצר לאחר שחרור הפוסט שוחררה גרסה משמעותית, 2.6, של מונגו. ניתן לקרוא highlights בלינק הבא.

נעשה עדכון ב 30/3 בכדי לחדד את הנקודה שמונגו הוא לא "RDBMS מהיר" או "גמיש יותר" – הוא שונה, והוא יעיל לסוגים מסויימים של בעיות.

לינקים רלוונטיים:
מצגת מעניינת על מידול נתונים במונגו
השוואה (מעט פרובוקטיבית) בין הביצועים של מונגו ו MS SQL

—-

[א] DB-Engines קיים "כולו" שנתיים ומשהו, אבל ע"פ כל מדד מקובל – לא נראה שבסיס נתונים לא-רלציוני היה איפשהו ברשימה הפותחת ב 30 שנה האחרונות.

עוד הערה: Cassandra ומונגו נמצאים ברשימת ה top10 מאז המדד החל, אולם Sybase ASE "נדחף" ל Cassandra באמצע והחזיר אותו למקום העשירי. הסיבה היא כנראה החיזוק ש ASE קיבל מרכישת SAP את Sybase, אולם אין בכך לגרוע מהמגמה הכללית: ASE צמח בעיקר על חשבון אורקל.

[ב] ב NoSQL, כמו ב NoSQL – אין הגדרות מוסכמות או חד-משמעיות. התייחסו לכל הגדרה כ "נקודת מבט אפשרית".

[ג] מקור המונח daemon הוא מניסוי מחשבתי בתחום התרמודינמיקה בשם Maxwell's demon בו יש שדון שעובד ללא הפסקה ברקע ופותח דלת קטנה לחלקיקים שיעברו דרכה… daemon היא צורת כתיבה עתיקה של המילה demon (שד).

[ד] פורמט BSON גם מאפשר ניהול טיפוסים שלא קיימים ב javaScript כגון object id או date כפרמיטיב.

[ה] epoch (תאריך בסיס) הוא נקודת ייחוס ממנה שומרים תאריך בצורה מספרית, בדרך כלל בשניות. MS DOS, היתה מערכת 16bit ולא רצו לשמור תאריך, בשניות, משנת 0 (או אפילו מ 1970, כמו unix) – ולכן ה epoch הוא 1/1/1980.

כדי לתאר את התאריך 2/1/1980 (יום אחרי) – יש לשמור בשדה התאריך את המספר 24*60*60 = 86,400.

במערכת הקבצים NTFS (חלונות NT) זמן הייחוס הוא שנת 1601 – השנה הראשונה במחזור 400-השנים של לוח השנה הגרוגאני בו, שימו לב: שוחררה חלונות NT!. אפל (כתגובה?) קבעה את ה epoch של OS X לשנת 2001 – השנה בה יצאה מערכת ההפעלה OS X. בגרסאות קודמות של MAC OS, ה epoch היה שנת 1904 – שנקבעה בגלל שזו הייתה "השנה המעוברת הראשונה במאה ה-20", ואולי כדי להתבשם בעובדה שזו הייתה מערכת שרצה על מעבדי 24 ביט (בזמן ש DOS הייתה תלויה בעבדי 16 ביט, ו epoch כזה היה מגביל אותה).

עשה זאת בעצמך: NoSQL

מעוניינים לשדרג משהו? לעתים זול יותר ופשוט יותר לבצע שיפוץ קטן בעצמכם, במקום להשתמש בבעל מקצוע. ייתכן והתוצאה תהיה מוצלחת לא-פחות.
בפוסט זה אני רוצה לשתף במימוש מוצלח של \"טכניקת NoSQL BIG DATA\" שביצענו על גבי מערכת קיימת, מבלי לשנות אותה באופן מהותי ומבלי להחליף את בסיס הנתונים הרלציוני הקיים.

המסר המעניין מבחינתי, הוא שניתן ליישם ״בקטנה״ רעיונות של בסיסי הנתונים NoSql בעצמכם – ולהשיג תוצאות יפות.

הבעיה

הערה: פרטי המקרה האמיתי פושטו ושונה כך שהפוסט יהיה קל יותר להבנה.
יש לנו מערכת שאחת הפונקציות שלה היא ניהול פרויקטים. במודול הפרויקטים החלו להשתמש בצורה קצת שונה ממה שתכננו, והחלו להיווצר פרויקטים רבים במערכת. כמה \"רבים\"? היה לקוח שהתעניין בניהול 2 מיליון \"פרויקטים\".

בדיקות שעשינו למערכת הראו שבסביבות 30-אלף פרויקטים, המערכת מתחילה להראות סימנים של שבירת הלינאריות ב scalability. כלומר: עד נקודה זו – אם רצו לנהל עוד פרויקטים היה ניתן להוסיף עוד חומרה ביחס ישר לגדילה בכמות הפרויקטים / הפעילות. מעבר לנקודה זו היה צריך להוסיף x וקצת חומרה ל x פעילות נוספת, וככל שהמספר גדל – העלות השולית הלכה וגדלה.
הבנו שהמערכת תוכל לטפל במשהו כמו 50 אלף עד 100 אלף פרויקטים, תלוי בכמות החומרה שהלקוח יסכים להקצות. באופן מעשי זהו בערך גבול ה Scalability שלנו ולכן ההמלצה ללקוחות הייתה לא ליצור מעל 50 אלף פרויקטים.

חשוב להבהיר שמדובר במערכת בת כ3 שנים – שעברה לאורך חייה לא מעט שיפורי performance ו scalability. בשלבים הראשונים של המערכת הצלחנו לבצע שיפור יחיד שהגדיל את ה Scalability ב 30% – אך ככל שהזמן עבר שיפרנו אלמנטים פחות משמעותיים (כיוון שהמשמעותיים כבר שופרו) והבנו שאנו מגיעים לקצה ה Scalability של הארכיטקטורה הקיימת.

פתרון אפשרי אחד היה לנסות לעבור לבסיס נתונים NoSQL, נוסח MongoDB או CouchDB – המתאימים יותר לשימוש הספציפי של המערכת, והיו יכולים בהחלט לשפר את המצב. הבעיה: שאר האלמנטים במערכת (מלבד הפרויקטים) התנהלו בצורה משביעת-רצון בבסיס הנתונים הרלציוני. מה עושים? עושים הסבה לכל הקוד לעבוד מול בסיס נתונים NoSql או דורשים מלקוחות לנהל 2 בסיסי-נתונים שונים במקביל?!

התוצאות

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

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

מקור הבעיה

ישנן סיבות שונות המצדיקות מעהר לNoSql Databases:

  • הרצון בסכמה גמישה, שלא דורשת migration בין גרסאות.
  • כמות נתונים (ב TB) שדורשת מעבר משרת אחת לכמה שרתים – מה שנקרא Scale Out.
  • בעיית Scalability מקומית. כלומר: מעל כמות נתונים מסוימת, זמן התגובה למשתמש הקצה הופך ללא-סביר.
    זו הבעיה איתה התמודדנו במערכת שלנו.

איך נוצרת בעיית Scalability?
בואו נביט על (הפשטה של) סכמת הנתונים של מערכת הפרויקטים:

האובייקטים החשובים, הבנויים בהיררכיה הם: פרויקט, נושא, דיון, תגובה ותכונה-של-תגובה.

אני רוצה להתמקד לרגע באובייקט קצת פחות טריוויאלי במודל הנתונים: Comment Attribute.
תכונה-של-תגובה (Comment Attribute) יכולה להיות דבר כמו: תאריך, שם המגיב, קישור לתמונה וכו\'

בבסיס נתונים רלציוני ניתן לשמור תכונות כאלו ב-2 אופנים:

  • סכמה קשיחה: כעמודה (column) בטבלה. על כל תכונה אפשרית יוצרים עמודה חדשה.
    יתרונות: פשטות
    חסרונות: יש תכונות (כגון \"deleted by admin\") שמתרחשות לעתים נדירות – אך עדיין יש לשמור עבורן מקום בכל רשומה, הוספת שורה = הוספת סכמה.
  • סכמה גמישה: כטבלה נוספת, בתצורת master-detail, בה כל מפתח וערך של תכונה היא שורה נוספת.
    יתרונות: גמישות רבה ללא שינויי סכמה
    חסרונות: עוד טבלה לנהל, עוד קצת סיבוך.

המערכת הנ\"ל השתמשה בסכמה גמישה.

נתונים לדוגמה, בסכמה גמישה של תכונות אובייקט (לחצו להגדלה)

שתי הגישות, הן בעייתיות בהיבט של Scalability והניתוח מה עדיף הוא לא טריוויאלי. נושא זה הוא מעבר ל scope של הפוסט הנוכחי.

ככלל אצבע: כשאנו מגיעים ל 10M או יותר רשומות בטבלה אחת בבסיס הנתונים – בסיס הנתונים מתחיל להגיב פחות טוב, במיוחד עם זו טבלה בה הנתונים הם לא רציפים (פרטים על כך בהמשך) ו/או יש ריבוי של אינדקסים.

חזרה לתיאוריה

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

הנה טבלה חשובה למדי:

מקור: גוגל

שתי נקודות שהייתי רוצה להדגיש:

  • קריאה מדיסק היא הפעולה היקרה ביותר ברשימה (נו, טוב – מלבד WAN בין-יבשתי), והיא יקרה משמעותית מפעולות מבוססות זיכרון (פי 100 עד פי 100-אלף, תלוי בתסריט).
    אם אנו מתבוננים על שכבת ה Persistence כקופסה שחורה, אזי כדאי לנו מאוד להפחית קריאות לדיסק, גם על חשבון הרבה פעולות בזיכרון. כלומר: להבין מתי בסיס הנתונים או שכבת ה ORM גורמות לקריאות לדיסק להתרחש – ולגרום לקריאות אלו לפחות ככל האפשר.
  • בניגוד לזיכרון, בו יש פער גדול בין גישה כלשהי (100ns) לקריאת 1MB של נתונים (250K ns, פי 2500), בקריאה מדיסק הפער הוא רק פי – 2. כלומר: להביא 1MB של נתונים רציפים לוקח כמו הבאה של 2 חתיכות של 4k.
    הסיבה לפער זה הוא שזמן גישה (seek time) בדיסק כוללת תנועה של זרועה מכנית וסיבוב הדיסק לנקודה הנכונה, משם קריאה רציפה היא כבר \"לא סיפור\".
    הערה: מגבלה זו השתפרה מאוד עם הצגת כונני ה SSD המודרניים. ניתן לקרוא עוד בנושא בפוסט מבט מפוכח על מהפכת ה SSD. עדיין, קריאה מדיסק ובמיוחד קריאה בלתי-רציפה, היא יקרה למדי. בפוסט הנ\"ל ניתן לראות כונן SSD שקורא 180MB בשנייה באופן רציף, אך רק 18MB בשנייה כאשר המידע מפוזר. יחס קצב העברת-נתונים של פי 10-15 בין קריאה רציפה לקריאה אקראית הוא מאפיין שכיח בכונני SSD מודרניים. יחס זה הוא בערך פי 100-200 בכוננים קלאסיים – כך שמדובר בשיפור גדול.
כיצד נגרום לבסיס הנתונים לבצע משמעותית פחות קריאות לדיסק, מבלי לגעת בקוד של ה ORM או של בסיס הנתונים? כיצד נוכל לעשות זאת מבלי לשנות דרמטית את כל המערכת שלנו? התשובה בפסקה הבאה.

Aggregate-Based Data Storage

בואו ננתח מה מתרחש בעת קריאה של פרויקט מבסיס הנתונים לזיכרון: מכיוון שיש כל-כך הרבה פרויקטים, ניתן להניח ש cache אינו משחק תפקיד משמעותי בסיפור.

כשטוענים פרויקט:

  • נטענת רשומה מטבלת הפרויקטים
  • נטענת רשומה אחת מטבלת ה Topic (נושא אחד נפתח ב default)
  • נטענות כל רשומות הדיון מאותו ה topic (כדי להציג רשימת שמות) וכל הרשומות מהדיון הראשון, כולל כל ה comments וה attributes שלהם.

 זה המידע שנדרש על מנת לספק את חווית השימוש הרצויה.

נניח מצב אופטימלי בו טבלאות הן רציפות בדיסק, ע\"פ סדר הכנסת השורות.
כמה פעולות seek של הדיסק יש פה?

  • לפחות קריאה אחת עבור כל טבלה.
  • בעצם, יש אינדקסים שבהם ייתכן ויש להיעזר – כך שבפועל ייתכנו מספר קריאות לכל טבלה.
  • המידע מגיע מהדיסק בבלוקים של 4k או 16k. אם הרשומות בטבלה אינן \"קרובות דיין\" על מנת להיכנס לבלוק של 16k נתונים – ניאלץ \"לדלג\" (seek) שוב בתוך הטבלה.
    רשומות מסוג ה Comment יכולות להיכתב בהפרש זמנים ניכר אחת מהשנייה, שכן תגובות יכולות להגיע לאחר שבוע או חודש.
    רשומות מסוג ה Comment Attribute יכולות להיכתב בהפרש (כלומר, פיזור) נוסף, מאחר והן נוספות \"רק ע\'\'פ הצורך\". לדוגמה: תכונת ה likesCount תיווצר רק בעת שנעשה ה Like הראשון ולא עם יצירת ה comment.
אין לי חישוב של מספר הפעולות בדיסק, אך יש לי בסיס להאמין שהוא יכול להסתיים בעשרות קריאות מהדיסק לכל discussion. השימוש ב ORM יכול להסתיר את העובדה שיצירת אובייקט \"discussion\" בזיכרון, רק בכדי לקחת את ה title ו lastUpdateDate – יכולה לגרום ליצירת אובייקטי ה comment והקריאה גם שלהן מהדיסק.
נקודה מעניינת, שמעצימה את הבעיה, היא שבמערכת עם הרבה מאוד פרויקטים ודיונים, הכמות הגבוהה של ה comments שמתווספים למערכת בשעה, יכולה לגרום לכך ש 2 תגובות במרחק של 5 דקות אחת מהשנייה – לא יהיו במרחק 16k בדיסק (מכיוון שנכתבו מאז הרבה comments אחרים ב discussions אחרים).
המצב הזה, בו המידע בדיסק מאורגן בניתוק מאופו השימוש בנתונים (קרי – ארצה לראות את כל ה comments מאותו דיון ביחד – ולא את כל ה comments שנכתבו בדקה מסוימת), הוא שורש הבעיה.

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

אפשר להקביל מצב בו אנו ניגשים לקבוצת נתונים כמקשה-אחת, לשימוש במסמך וורד: אולי יש שם טבלאות, תמונות וטקסט – אבל יחידת הייחוס החשובה היא המסמך, ולא טבלאות בנוסח \"כל התמונות של כל המסמכים\" או \"כל הטבלאות של כל המסכים\".
באופן דומה, בסיפור שלנו, נקודת הייחוס היא הפרויקט לא ההערה הבודדת.

במקרים כאלו יש ייתרון ברור לאיגוד הנתונים ע\"פ נקודת הייחוס המתאימה לשימוש הנפוץ במערכת, מה שנקרא document-based database או aggregate-database.

אילוסטרציה: כיצד פרויקטים יכולים להשמר בדיסק באופו רציף, כך שיהיה קל לקרוא פרויקט בודד.
ברור שפרויקטים שונים יהיו בגדלים שונים (לא הכללתי זאת בתרשים).

כיצד מממשים זאת?
פשוט מאוד: בוחרים נקודת ייחוס (למשל Topic, בכדי לצמצם מעט את גודל ה\"קובץ\" שנקרא בכל פעם), ומייצגים אותה ואת כל ההיררכיה של האובייקטים מתחתיה (למשל אובייקטי ה discussion) כרשומת JSON או XML אחת. את רשומה זו שומרים בבסיס הנתונים כ BLOB, כך שיהיה עדיין להינות משירותים של בסיס הנתונים (גיבוי, טרנזקציות וכו\').

כעת, במקום להשתמש באובייקטי ה ORM לאובייקטי ה Topic ומה שמתחת – יש לכתוב מימוש מחדש, שיקרא את המידע המתאים מרשומת ה JSON ויטפל באותם הדברים שה ORM טיפל עבורנו עד כה.
במידה (כמו במקרה למעלה) שיצירה של אובייקט Topic גורמת ליצירת כל השאר – השימוש בבסיס הנתונים יהפוך ליעיל בהרבה: כל היררכית האובייקטים נקראת ממקטע רציף על הדיסק.

משמעויות נוספות
יש כמה משמעויות נוספות בגישה זו שכדאי להיות מודעים אליהן.

  • יש לממש לבד לוגיקה של קריאה / כתיבה של אובייקטים לתוך ה BLOB (מה שציינו למעלה).
  • אנו מאבדים את היכולת לעשות שאילתת SELECT על כל האובייקטים במערכת. למשל, למצוא את כל ה Comments שנכתבו בין 2 ל 4 בבוקר.
    אם אנו רוצים לבצע שאילתה שכזו – יהיה עלינו לקרוא את כל ה Topics מהדיסק, אחד אחרי השני, ולסרוק בעצמנו את המידע בתוך ה BLOB.
    אם אנו רוצים מהירות בסריקה (ויש לנו use-case ספציפי) אנו יכולים להשתמש במנועי indexing כגון Lucne.
  • השימוש בסכמה של \"טקסט חופשי\", כגון פורמט JSON, מאפשרת לנו לבצע שינויים לסכמת הנתונים בין הגרסאות של המוצר מבלי לבצע שינויים לסכמת בסיס הנתונים.
  • השימוש בסכמה של \"טקסט חופשי\" מאפשרת לנו לשלם על שדות \"נדירים\" רק כאשר משתמשים בהם (לדוגמה: isDeletedByAdmin) ועדיין להינות מביצועים נהדרים.

עדכון ספטמבר 2015:
הנה שני סיפורים דומים של חברות שבחרו ב\"התאמה אישית\" של בסיס נתונים רלציוני על פני מעבר ל NoSQL DB:

סיכום

בסיסי הנתונים הרלציונים נבנו תחת הנחה שהנתונים מתמפים יפה לטבלאות וקשרים ביניהן. צורת מיפוי זו לעתים איננה טבעית לנתונים שאנו נתקלים בהם בפועל.
בנוסף לכך, שמירה של מיליוני רשומות שאין קשר בניהן בטבלה אחת (\"כל התמונות של כל מסמכי הוורד\") – היא גישה שמשפיעה לרעה על ה Performance וה Scalability שלנו.

כדי להינות מ BIG DATA, מספיק לעשות שינוי קצת שונה בבסיס הנתונים הרלציוני הקיים שלנו. ברור שבסיס נתונים NoSQL ייעודי יכול לתת יותר – אך לא תמיד הפער הזה מצדיק את המעבר.

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

אם אתם מעוניינים ללמוד קצת יותר על BIG DATA, אתם מוזמנים לקרוא את הפוסט מה הביג-דיל ב BIG DATA?

RESTful Services – שירותי הרשת של המחר, החל מאתמול (1)

פוסט ראשון מתוך שניים. את ההמשך אפשר למצוא כאן.

REST הוא עוד באזז של השנים האחרונות: חברות אינטרנט רבות אימצו אותו, ספרים רבים נכתבו, גוגל יצרה וריאציה משלה בשם GDATA ומנסה להפוך אותו לסטנדרט. מייקרוסופט מצידה הגדירה אלטרנטיבה בשם [ODATA[1.

על מה כל המהומה?? – אנסה לענות במאמר זה.

מהו בעצם REST?
טוב, קחו נשימה עמוקה: REST הוא סגנון ארכיטקטוני (Architectural Style) ממש כמו Pipes & Filters, Layered Architecture או (SOA (Service Oriented Architecture.
סגנון ארכיטקטוני הוא לא ארכיטקטורה, אבל אם אתם יודעים מהו הסגנון הארכיטקטוני של מערכת (וגם הסגנון הזה נשמר לאורך הפיתוח) – תוכלו לדעת דיי הרבה על איך המערכת נראית ומה העקרונות שעומדים בבסיסה. ממש כמו שאם אתם הולכים לראות מבנה שאתם יודעים שהוא בסגנון גותי, סיני או ערבי – תדעו פחות או יותר למה לצפות.

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

REST הוא סט חוקים שמערכת יכולה לבחור ולאמץ. ב High Level הוא אומר שני דברים עיקריים:

  • תיאור ממשק המערכת כעולם של Entities (כ"א instance של אובייקט) כבעל מזהה (URL) ייחודי, דרכו ניתן לבצע פעולות. ממערכות כאלו נקראות Resource-Based Distributed System – שכל entity הוא כאילו משאב מסמך עצמאי עליו עושים פעולות[2].
  • הצמדות מדויקת לפרוטוקול HTTP – פרוטוקול שיש בו הרבה חוכמה שאנו נוטים לפספס.
ה API שנחשף החוצה, הוא התוצר – לא העיקר.

איך אני יודע אם אני משתמש ב REST

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

על מנת להשתמש ב REST API (נקרא גם RESTful Service) לא צריך לדעת כמעט שום דבר: פשוט פותחים connection של HTTP, שולחים פרמטרים ע"פ התיעוד של ה API ומקבלים תשובה בפורמט שתואר.
מצד אחד קצת חבל שכמעט כל מי שמשתמש ב REST לא מודע לעקרונותיו היפים, אבל מצד שני: היי – זה דבר נהדר! הכמסה (Encapsulation) במיטבה! אל תסבכו את הצרכן שלכם בידע מיותר.

אז…הדרך הנפוצה לדעת אם אתם צורכים ב REST הוא לקרוא האם בתיעוד כתוב "REST" ולקוות שמי שכתב את התיעוד מבין על מה הוא מדבר : )

אם אתם כותבים מערכת REST, יש הרבה מה לדעת – המשיכו לקרוא.

קצת היסטוריה

תחום האינטגרציה של מערכות ארגוניות (EAI – Enterprise Application Integration) הוא תחום מסובך ויקר במיוחד. רכשת מערכת כספים מספק א' ומערכת ניהול קשרי לקוחות מספק ב' – על מנת לנצל את היתרון שהמערכות ממוחשבות וניתן להצליב בניהן נתונים, אתם צריכים לגרום למערכות לדבר אחת עם השנייה. בגלל שהמערכות מדברות בשפה (Conceptual Model) שונה ובגלל שהארכיטקטורות שלהן שונות – המאמץ הוא אדיר. כשאנחנו נזכרים בסיפורים בהם משרדי ממשלה לא מצליחים להצליב נתונים (ביטוח לאומי ומס הכנסה, או ארגוני ביון אמריקאים לפני שנת 2001) אנו נוטים לחשוב שזהו מצב של חלם, אבל בפועל עלות האינטגרציה היא אדירה ולעתים קרובות עולה על מחיר המערכות עצמן[3].

כך נראה פרוייקט EAI של ארגון בגודל בינוני

בתחילת שנות האלפיים חברו כמה חברות בראשן BEA, Microsoft, IBM ו SAP ליצור סטנדרט בתעשייה שיקל על פעולות האינטגרציה של מערכות. תקן זה ידוע כ "Web Services" הכולל Stack של פרוטוקולים שהעיקריים שבהם הם: SOAP, WSDL, UDDI ו XML (שהיה כבר קיים אך אומץ ע"י הסטנדרט). תוך כדי התפתחה מאוד ההתעסקות ב (Service Oriented Architecture (SOA. העיקרון אינו חדש: זה תיאור של מערכת מבוססת services ו מבני נתונים המחזיקים את המידע העובר בין ה services, ממש כמו שתכנתנו פעם בפאסקל או C. גם היום חלק גדול ממערכות ה .NET וה Java בנויות כך, לעתים מתוך החלטה, לעתים "כי פשוט יצא ככה". החידוש ב SOA היה הידע שנצבר והתפתח עבור אותה פרדיגמה במערכת מבוזרת.

יוצרי ה Web Services היו זקוקים לשם מפוצץ ("SOA") וצבא של יועצים-מומחים בכדי לשכנע את השוק לאמץ את גישתם. על פני השטח זה נראה כמו וויתור על פרדיגמת ה Object-Oriented (אותה, אותם יועצים ממש, מכרו כמה שנים לפני כן כ "חובה לכל ארגון") וחזרה לסגנון התכנות הפרוצדורלי (ברמת המערכת) שהיה שם קודם לכן. הם ניסו לשכנע שזו לא התדרדרות אחורה – אלא התקדמות קדימה. האמת – הם צדקו [4].

עם השנים (כמה שנים בודדות, לא יותר) תקן ה Web Services הסתבך לעשרות תקני משנה, הידועים כ WS-* (כוכבית = wildcard כמו WS-RPC, WS-Security וכו') שניסו לפתור עוד ועוד היבטים של אינטגרציה בין מערכות תוך כדי שהם נהיים מורכבים יותר ויותר לשימוש. קשה היה לאדם בודד להכיר את כל התקנים ובטח לא להתמצא בהם. בעיה נוספת הייתה performance: בגלל שהתקן מאוד כללי (בנוי לקשר בין מערכות מספקים שונים, הכתובים בשפות תכנות שונות ובין גרסאות שונות) ובגלל שהוא מבוסס על קבצי XML גדולים, פורמט המרבה במילים (verbose) – תקשורת מבוססת Web Services הייתה צוואר בקבוק גם של הרשת, אבל בעיקר של צריכת זיכרון (בשל ה parsing של קבצי xml ענקיים). עניין זה היה מטרד למערכות ארגוניות, ומכת מוות למערכות אינטרנט High Scale.

חברות האינטרנט הקטנות והיעילות יצאו למלחמה רבתי: "REST נגד SOA" – ראה [5]
הם הציגו את REST כאלטרנטיבה פשוטה, מהירה ונוחה למתכנת לייצר Web Services. הם גם נתנו לשירותים אלו שם מפוצץ משלהם: "RESTful Services". אני זוכר שהייתי בכנס QCON בלונדון בשנת 2008, ורבים מה sessions היו על נושאים ב REST או ב SOA (למשל, אני זוכר session שנקרא "REST eye for a SOA guy"). כל פעם באו אנשי המחנה השני, קראו קריאות ביניים והפריעו למרצה ב Session. מהר מאוד למדתי להדיר רגלי מכל Session באחד משני הנושאים הללו.

מכיוון שפוסט זה עוסק ב REST ולא ב Web Services אתם יכולים להסיק, בצדק, ש REST ניצחה במלחמה. השימוש ב Web Services הצטמצם משמעותית ונותר בעיקר בנישה של מערכות שונות של ספקים שונים. אפילו החברות אשר יזמו וקידמו ללא ליאות את הסטנדרטים (עבדתי ב SAP – אני יודע) הוציאו הנחיות פנימיות לצמצם שימוש ב Web Services למינימום ההכרחי.

הפשטות ניצחה את ה Coverage.

הבנת ההבדל בין Web Service ל REST
גם REST וגם Web Service הם אמצעי תקשורת בין מערכות שונות המבוססים על XML העובר על [HTTP[6 – היכן ההבדל הגדול?

עדכון: תודה לארנון רותם-גל-עוז שהאיר את עיני לכך ש REST אינו coupled ל HTTP ואותם עקרונות יכולים להיות  מיושמים על גבי פרוטוקולים אחרים (אישית, לא נתקלתי בכזה מימוש).

Web Services
ב Web Services התקשורת הוא מול Service שמטפל בנושא מסוים, לדוגמה הזמנות, אשר עליו מוגדרות הפונקציות השונות הקשורות לתחום. לרוב, מעט Services עם הרבה פעולות על כ"א. לדוגמה:

getOrderDetails()
updateOrder()
Subscribe()
cancelSubscription()
findMatchingOrder()
listOrderProviders()

פונקציות כמו ()Subscribe או ()ListOrderProviders אינן קשורות בדיוק להזמנה, הן בתחום. את ה interface השירות חושף בעזרת XML שנקרא WSDL כך שמי שצורך אותו יוכל בקלות לבצע import ל IDE אשר ייצר proxy לוקלי לקריאה לשירות כאילו מדובר באובייקט מקומי. Visual Studio, כבר מימיו הראשונים של .NET עושה זאת בצורה נהדרת.
כאשר מתבצעת קריאה ל Web Service בפועל, נוצר XML עם הפרמטרים הרלוונטיים. XML זה נעטף ב XML נוסף הנקרא Envelope של פרוטוקול ה SOAP (ה envelope מוסיף נתונים העוזרים ל cross platform interoperability אך ייתכן ויהיה גדול משמעותית מההודעה עצמה). אם ה Web Service תומך או דורש שימוש בכל מיני שירותים נלווים (WS-* למיניהם) יש להתייחס אליהם וייתכן שהם יוסיפו תוכן או ישנו את צורת ההתקשרות.

RESTful Web Services
REST, כפי שאמרנו, מתאר Resource-Based Distributed System. הגישה היא ל resource (או האובייקט) עצמו ולא לשירות. לרוב מדובר על המון משאבים (הממופים כ"א ב URL), אשר כל כ"א סט מצומצם וקבוע של פעולות המוגדרות  בפרוטוקול HTTP.

על מנת לקרוא את פרטי ההזמנה אבצע קריאת HTTP GET ל URL:

על מנת לעדכן את ההזמנה אבצע קריאת HTTP PUT ל URL:

את נערכים שאני רוצה לעדכן אשלח כ Post Parameter בפורמט XML או JSON הכולל את הערכים הרלוונטיים.
על מנת לבצע שאילתה על כל ההזמנות בשנת 2009 של לקוח AMEX אני אבצע קריאת HTTP GET ל URL:

האובייקט הוא orders, אני מבצע קריאה ושולח פרמטרים ל Query בשם year ו customer.

כמובן שאני לא יכול לשלוח מה שבא לי – רק מה שהוגדר ע"י ה שAPI ומתועד ב API Documentation.

ועל מנת לבצע שאילתה של listOrderProviders נגשים ל"אובייקט" ה OrderProviders, כמובן:

אם ביצעתי קריאת GET להזמנה שאינה קיימת אקבל כנראה שגיאת HTTP 404, המוגדר בפרוטוקול HTTP כ "Not Found". אם ביצעתי קריאת POST (הוספת ערך חדש) אצפה באופן טבעי לתשובת HTTP 201 המוגדרת בפרוטוקול HTTP כ "Created". עבור ביצוע אסינכרוני אצפה ל 202 "Accepted" וכו'

כפי שאתם מבינים על מנת לעבוד ב REST אני צריך להשתמש נכון בפרוטוקול HTTP, אבל מי שצריך להבין את הפרוטוקול הוא מי שמגדיר את ה REST API – המשתמש ב API פשוט עוקב אחרי התיעוד ושולח / מצפה למה שנאמר בתיעוד.

קשה לי לתאר במילים כמה פרוטוקול REST פשוט יחסית לפרוטוקול WS-*. אחד הטיעונים נגדו היה שזהו Hack שלא יחזיק מים במערכות גדולות ומורכבות (טעות). פעם בקורס על Web Services ב SAP ישבנו שעתיים רק לסקור את סוגי השירותים השונים של WS-* וזה היה על קצה המזלג.

חשוב להבין ש RESTful API (כלומר, מימוש נכון של REST) משפיע רבות על המבנה הפנימי של המערכת. עצם העובדה שכל instance של אובייקט הוא נגיש החוצה וניתן לבצע עליו סט סגור של פעולות הוא עיקרון שיצליח אם מערכת בנויה בצורה X אך יכול להיכשל אם מערכת בנויה בצורה Y.

המשמעות של הוספת RESTful API למערכת קיימת שאינה בנויה בצורה REST-friendly היא לרוב להוסיף שכבת Adapting עשירה או לא להצליח להנות בפועל מיתרונות ה REST. ייתכן וכל מה שאתם מחפשים הוא לחשוף API בצורה שהמשתמשים רגילים (REST like) ולכן יתרונות ה REST האחרים הם לא חשובים. לגיטימי.

הקשר בין REST לפרוטוקול ה HTTP וארכיטקטורה של Resource-Based Distributed System אינו מקרי.
בפוסט ההמשך ארחיב על נושאים אלו יותר לעומק.

[1] Open Data. מכיוון שהצלחה נראתה כיעד אסטרטגי מהותי – היא שיחקה עם גוגל במגרש שלה, פתחה את התקן כ Open Source ושחררה ספריות התומכות ב .NET כמובן, אך גם PHP, Java ו JavaScript.

[2] היסטורית מקובל לחלק מערכות מבוזרות ל 4 סוגים:

  • Object-Based Systems: מערכות שמאפשרות לגשת מרחוק ולהפעיל אובייקטים עשירים בפרדיגמת Object-Oriented. דוגמאות הן Corba, DCOM או EJB כאשר עובדים עם Remote Interface.
  • Distributed Database / Storage System – כאן נכנסים כל מערכות ה NoSQL שתיארתי בפוסט על Big Data או מערכות קבצים מבוזרות נוסח Gopher, WebDAV או HDFS ו GFS המודרניים.
  • Distributed Coordination-Based Systems: מערכות תיאום מבוזר כמו Rendezvous או Jini של ג'אווה שנכשל ונולד מחדש כ Apache River. דוגמה מודרנית יכולה להיות פרוטוקול Gossip או מערכות peer 2 peer.
  • ומה שחשוב לפוסט זה: מערכות Resource-Based (לעתים נקראים גם "Document-Based") מבוזרות אשר ניגשים ל Resources ("מסמכים") אחד אחד לצורך פעולות קריאה, כתיבה וכו'. דוגמה אחת: האינטרנט (מסמכי HTML). דוגמה שנייה: מערכות REST.

[3] זה הסיפור העיקרי עליו מבוססת מכירת מערכות ERP של חברות כמו SAP או Oracle: "אין לנו את ה CRM הטוב ביותר או ה SCM הטוב ביותר – אבל אתה קונה את האינטגרציה built-in".

[4] שנים רבות Object Oriented Programming נחשב לשם נרדף לקדמה ומקצועיות, אבל בפועל הוא לא היה Silver Bullet – כלומר לא הביא לשיפור חד משמעי בעולם התוכנה. לתכנות פרוצדורלי יש הרבה יתרונות ונראה שיש לו עוד מקום של כבוד בעולם התוכנה בשנים הבאות. כמובן שעדיף להבין את היתרונות והחסרונות המעשיים של כל גישה ולבחור בחירה מודעת. סימן אחד לכוחה של הפרדיגמה הפרוצדורלית היא שהרבה מאוד פרויקטים שניסו לייצר מודל OO כשלו וגמרו עם מודל פרוצדורלי. כלומר: OO הוא קשה למימוש, פרוצדורלי הוא קל. חישבו על כך – זהו יתרון משמעותי.
תכנון מונחה עצמים Object Oriented Design, לעומת זאת, הוכיח את עצמו יפה והוא מוצלח משמעותית מכל מיני פרדיגמות עתיקות כמו DFD (השם ירחם!) או ERD שנהגו להשתמש בהם בשנות השמונים (או בסוף שנות התשעים באקדמיה – אותה תקופה בה למדתי את התואר הראשון). יהיה זכרם ברוך.

[5] SOA היא ארכיטקטורה טובה, הם בעצם התכוונו לצאת נגד Web Services. עקרונית REST הוא סוג של SOA.

[6] REST לא מגדיר מה פורמט ההודעה, XML נפוץ מאוד וכך גם JSON ואפשר גם להשתמש בפורמט אחר כלשהו.