הרחבה מהירה של אחסון מקוון לשירות יותר ממיליארד משתמשי ChatGPT (חלק ב׳)
כיצד שיפרנו את Azure Cosmos DB בפלטפורמת אחסון היישומים שלנו, Habitat, כדי להתמודד עם צמיחה חסרת תקדים.
כל מוצרי OpenAI זקוקים לגישה מהירה ואמינה לנתונים, שאותה מספקת פלטפורמת האחסון שלנו, Habitat. היא צריכה להגיב בתוך מילישניות ליותר ממיליארד משתמשים ב-40 אזורים גאוגרפיים. התשתית שלנו ניצבה בפני אתגר יוצא דופן: לתמוך בצמיחה מסחררת ובמקרי שימוש חדשים, ובו בזמן להמשיך לשפר את האמינות, הביצועים וקצב העבודה של מפתחי המוצרים. כדי לעמוד באתגר הזה נדרשה חשיבה מחדש על התכנון והתפעול של פלטפורמת האחסון המקוון שלנו. בחנו כל הזמן את הפשרות הנדרשות, לפי סדר העדיפויות הבא: קודם להיות אמינים מספיק לתעבורת מוצר קריטית, אחר כך מהירים מספיק למשתמשים ברחבי העולם, ולבסוף לפעול ביעילות ובגמישות בקנה מידה עצום.
הסדרה הזו, בשני חלקים, מסבירה כיצד פיתחנו את Habitat מספריית לקוח פשוטה ומסד נתונים לפלטפורמת אחסון מבוזרת עולמית, שמספקת גישה ליותר מ-500 פטה-בייט של נתונים. חלק א׳ התמקד בהתפתחות של Habitat מלקוח Python לשירות. הוא עסק גם בטכניקות שאפשרו לנו להרחיב שירות מבוסס Python הרבה מעבר למה שרבים היו רואים כמעשי.
בפוסט הזה, חלק ב׳, נסביר כיצד הרחבנו את רכיב האחסון שעליו המערכת מבוססת: Azure Cosmos DB. בשיתוף פעולה הדוק עם צוות Azure, הגענו לגישה מעשית לריבוי דיירים המבוססת על קונטיינרים בניהול משותף, עם ביצועי קריאה מצוינים בקנה מידה עולמי עצום.
איור 01 · מהי Habitat?
פלטפורמת אחסון מקוון
Habitat היא פלטפורמת האחסון המקוון שבנינו כדי שמוצרי OpenAI יוכלו לגשת במהירות ובאמינות למידע הדרוש להם.
- בקשה
- תגובה
- שינויים (CDC)
ככל שחברות צומחות, צוותי התשתית עוברים לעיתים קרובות מהפעלת מערכות עם דייר יחיד למערכות מרובות דיירים. במערכת עם דייר יחיד, כל צוות שאחראי על חלק במוצר מתחזק מערכת משלו. כך מצטמצם היקף הפגיעה של תקלות, קל להגדיר גבולות אחריות, ועומסי העבודה מבודדים זה מזה. ככל שהצוותים וחלקי המוצר מתרבים, התפעול נעשה יקר. גישת הדייר היחיד מטילה על כל צוות מוצר אחריות לתכנון קיבולת, ולעיתים קרובות מובילה להקצאת משאבים עודפת משמעותית.
לעומת זאת, ריבוי דיירים מעביר את השליטה לצוות מסדי נתונים מרכזי, בעל ידע וניסיון טובים יותר בהקצאת משאבים. אבל ריבוי דיירים יוצר בעיות משלו, כמו בעיית השכן הרועש: דייר אחד צורך נתח גדול באופן חריג מהמשאבים, על חשבון הדיירים האחרים.
ב-Habitat נדרשנו לשקול את כל הפשרות האלה כדי לתכנן מערכת אחסון אמינה ויעילה בקנה מידה גדול. כיום, Habitat היא שכבת מסד נתונים מרובת דיירים עבור מוצרים רבים של OpenAI. אבל עברנו מסלול מעגלי: משימוש בקונטיינר Azure Cosmos DB אחד מרובה דיירים, דרך הקצאת קונטיינרי אחסון לדייר יחיד, ועד חזרה לקונטיינר מרובה דיירים כברירת מחדל. אנחנו עדיין בתחילת הדרך בכל הקשור לריבוי דיירים ב-Habitat: אנחנו תומכים במגוון רחב של מקרי שימוש, אבל הצלחנו לדחות השקעה בתחום הזה באמצעות שימוש באבני הבניין המובנות של Azure Cosmos DB בכל מקום שאפשר.
לפני שנפרט על המסע שלנו עם Azure Cosmos DB, הנה מעט רקע בסיסי:
חשבון Azure Cosmos DB מגדיר קבוצת קונטיינרים ומדיניות שכפול
לכל קונטיינר מוקצית תפוקה מסוימת, הנמדדת ביחידות בקשה לשנייה (RU/s)
הנתונים בכל קונטיינר נשמרים בחלוקה המקובלת למקטעים לפי מפתח מחיצה לוגי מוגדר, עם מגבלות מתועדות על האחסון והתפוקה לכל מפתח מחיצה לוגי
Azure Cosmos DB מפזר את מפתחות המחיצה הלוגיים בין מספר קטן יותר של מחיצות פיזיות. הוא מפצל אותן למחיצות פיזיות נוספות כשהאחסון עובר סף מסוים, או כשהקונטיינר זקוק לעוד מחיצות פיזיות כדי לספק את התפוקה שהוקצתה
בתחילה אפשרנו לכל עומס מהלקוחות לעבור, והסתמכנו על המגבלה המוצהרת של 10K RU/s לכל מפתח מחיצה לוגי כדי ליצור לחץ חוזר שיווסת את העומס. בתחילת הדרך השתמשנו גם בקונטיינר ברירת מחדל אחד בלבד של Azure Cosmos DB בכל חשבון. זה עבד לא רע, אבל התברר כבעייתי מכמה סיבות:
עקב החלטות מוקדמות בתכנון ה-API, חשפנו ללקוחות ישירות את בחירת מפתח המחיצה הלוגי. כתוצאה מכך, יש התנגשויות תכופות במפתחות מחיצה נפוצים, כמו user_id או workspace_id, בין מקרי שימוש של מוצרים שונים. מכיוון שמקרי השימוש האלה חלקו את המכסות של Azure Cosmos DB עבור אותם מפתחות מחיצה לוגיים, התכנון הגביל את התפוקה הכוללת למשתמש נתון.
בעיה נפוצה שהתמודדנו איתה בהרחבת Postgres, לפני שעברנו ל-Habitat ול-Azure Cosmos DB, הייתה שבקשות רבות מארגון או ממפתח עמוסים במיוחד — דייר אחד שמייצר עומס גבוה באופן לא פרופורציונלי — יכלו להשבית את מסד הנתונים כולו. הושקע מאמץ משותף ניכר בשמירת המפתחות האלה במטמון ובשילוב מנגנוני חכירה בטיפול בהחטאות מטמון, כדי שהעומס שנוצר מההחטאות לא יציף את מסד הנתונים שמתחתיו.
Figure 02a · Logical partition key limits
Different clients, the same partition key
אף שהמגבלות המתועדות על נפח הנתונים והתפוקה חלות על מפתחות מחיצה לוגיים, בפועל הן נאכפות ברמת המחיצה הפיזית. בפועל, פירוש הדבר הוא שמחיצות לוגיות אינן יכולות להגיע ל-10,000 RU/s. במקום זאת, הן חולקות את מכסות המחיצה הפיזית עם כל יתר מפתחות המחיצה הלוגיים שממוקמים באותה מחיצה פיזית.
כך יכלו שכנים רועשים להשפיע על מפתחות מחיצה לוגיים רבים שאינם קשורים אליהם: עומסי עבודה על נתונים של תכונות מוצר נישתיות שיבשו תהליכי ליבה של התחברות והרשמה.
Figure 02b · Noisy neighbors
Different users, the same physical partition
גם מגבלת התפוקה של מחיצה פיזית נתונה משתנה, בהתאם לתפוקה הכוללת שהוקצתה לקונטיינר של Azure Cosmos DB. התפוקה שהוקצתה לקונטיינר מתחלקת באופן שווה בקירוב בין המחיצות הפיזיות שלו.
ככל שנפח הנתונים גדל, המחיצות הפיזיות מתפצלות ומספרן הכולל עולה, בעוד סך התפוקה המוקצית נשאר ללא שינוי. כתוצאה מכך, מגבלת התפוקה בפועל לכל מחיצה מצטמצמת ככל שנפח הנתונים גדל.
במקרי השימוש עתירי האחסון ביותר שלנו, הדומים לאחסון Blob, נוצר מצב כשל שבו הגידול באחסון לבדו מפחית את קיבולת התפוקה לעומסי עבודה פעילים, גם כשקצב הבקשות והשימוש נשארים יציבים.
Figure 02C · Weak partitions
The limit falls below demand
A partition splits when it reaches its storage limit. Each child receives half the throughput capacity.
קונטיינרים נפרדים מסייעים לתת מענה חלקי למגבלות האלה על מפתחות מחיצה ולבעיית השכנים הרועשים. בשימוש בקונטיינרים נפרדים, אפשר להקצות לכל אחד מחיצות פיזיות עצמאיות משלו. כך, רק עומסי העבודה שבאותו קונטיינר יכולים להפוך לשכנים רועשים או לחפוף במרחב מפתחות המחיצה. מצד שני, הקצאת קונטיינרים חדשים כרוכה בתקורה — כל קונטיינר דורש תכנון קיבולת פרטני. לכן אנחנו רוצים ליצור קונטיינרים רק במקום שבו הגיוני להפריד בין שירותים או מקרי שימוש.
התחלנו להשתמש בקונטיינרים נפרדים של Azure Cosmos DB למקרי השימוש החדשים והקריטיים ביותר שדרשו בידוד חזק. בשילוב סקריפט לניהול מחיצות חלשות, שהתאים את סך התפוקה המוקצית לכל קונטיינר למספר המחיצות הפיזיות שלו, הצלחנו להימנע במידה רבה מכל הבעיות שהתמודדנו איתן.
עם זאת, מכיוון שהתחלנו בקונטיינר ברירת מחדל, מקרי שימוש ותיקים וקריטיים רבים נשארו בו למרבה הצער, כל עוד לא הועברו הנתונים שלהם. כדי להגן על שירותים קריטיים שעדיין הסתמכו על נתונים בקונטיינר ברירת המחדל המשותף, המלצנו לצוותים לעיתים קרובות ליצור קונטיינרים נפרדים לשירותים חדשים או לתכונות מוצר מרכזיות. ככל שהתרחבנו, גבר העומס על קונטיינר ברירת המחדל, וגם מספר התקלות בסביבת הייצור עלה. התחלנו להקצות קונטיינרים לכל שירות חדש או תכונת מוצר גדולה, רק כדי להגן על קונטיינר ברירת המחדל מעומס נוסף.
כך נמנעו השבתות רבות של האתר כולו, והיקף הפגיעה הוגבל לתקלות בתכונה או בשירות מסוימים. אבל בסופו של דבר, התקורה של הקצאת קונטיינרים חדשים החלה להכביד:
בקונטיינר ברירת המחדל המשותף הישן, לא היה סביר שמקרה שימוש חדש מסוים שגדל מ-0 למיליון שאילתות בשנייה (QPS) ישנה משמעותית את סך השימוש. לעומת זאת, בקונטיינר מבודד, מקרה שימוש חדש שגדל מ-0 למיליון QPS עשוי לייצג עומס גדול פי 1,000 מרמת הבסיס, שנבעה רק משימוש פנימי במוצר לצורכי בדיקה.
עם קונטיינרים ייעודיים, הצוותים נדרשו כעת לזכור להקצות מראש את התפוקה הצפויה לקראת ההשקות שלהם. צוותי מוצר העלו לעיתים קרובות תכונות לסביבת הייצור ושכחו להגדיל את הקיבולת מראש. כך מצאנו את עצמנו בחדרי ניהול תקלות חמורות ביום ההשקה, בניסיון להגדיל קונטיינרים ברגע האחרון.
זהו יתרון מרכזי של שילוב עומסי עבודה במערכות מרובות דיירים, שעליו אנחנו מוותרים לטובת בידוד טוב יותר באסטרטגיית הקונטיינרים הזו.
Figure 03 · Lack of stable aggregate growth
The same workload, different headroom
1K RU/s during load testing → 1M RU/s at launch
עם הזמן, ניטור השימוש בקונטיינרים ייעודיים דרש גם היכרות מעמיקה עם המוצר כדי לדעת מתי התעבורה תשתנה או תגדל.
אנחנו יכולים לספק סקריפט בסיסי במישור הבקרה שיגדיל את התפוקה המוקצית לכל קונטיינר לפי מגמות הצמיחה. אבל ללא גידול יציב בשימוש הכולל, התאמה נכונה של הקיבולת הביאה למעשה למעורבות של כל צוות מוצר ב-OpenAI בתכנון הקיבולת.
הגישה הזו לא עמדה בצמיחה הארגונית: היו הבדלים גדולים במידת ההקפדה של כל צוות אחראי על הקצאת המשאבים לשירות שלו ב-Azure Cosmos DB.
ככל שהארגון גדל, נעשה קשה יותר להתמודד עם הבעיות התפעוליות האלה בקנה המידה החדש. בהיעדר צוות מרכזי אחד שמבצע את התכנון, צוותים רבים תכננו בצורה לקויה. לכן חזרנו בסופו של דבר לכיוון של פתרון מרובה דיירים, ועבדנו בצמוד לצוות Azure על תיקונים הנדסיים לבעיות המקוריות שלנו בריבוי דיירים, בתכנון המבוסס על תשתית משותפת:
הגבלת הקצב של המפתחות העמוסים ביותר תחילה מונעת משכנים רועשים לצרוך את כל התפוקה הזמינה של מחיצה פיזית נתונה. כך מצטמצמת השפעת השכן הרועש, שבה מפתח עמוס אחד עלול לפגוע באתר כולו.
בדומה לכך, QoS המבוסס על סיווג בקשות לרמות מונע מעומסי עבודה לא מקוונים, כגון הוספות נתונים בכמות גדולה או חילוץ נתונים, לנצל את כל התפוקה של מחיצה פיזית נתונה. כאשר מקצים לבקשות של עומסי העבודה האלה עדיפות נמוכה יותר, הקצב שלהן מוגבל תחילה. כך נמנעת הגבלת קצב של בקשות אינטראקטיביות בעדיפות גבוהה יותר, שמשרתות את משתמשי הקצה.
ל-Azure Cosmos DB כבר היה מצב ללא שרת, שהיה יכול לפתור את בעיית המחיצות החלשות שחווינו במצב של תפוקה מוקצית. אבל היה לו חיסרון משמעותי: הוא התרחב רק לפי השימוש באחסון, והגביל את צריכת ה-RU ל-5K RU/s לכל מחיצה פיזית. זה היה פותר את בעיית המחיצות החלשות, אבל מונע מאיתנו להרחיב מקרי שימוש עתירי RU.
צוות Azure Cosmos DB בנה עבורנו מצב חדש ומשופר ללא שרת, שאפשר לכל מחיצה פיזית להגיע ל-10K RU/s והוסיף פיצולים לפי צריכת RU לצד פיצולים לפי השימוש באחסון. כך יכולנו להפסיק לנטר ולהגדיל קונטיינרים באופן ידני.
בזכות השינויים האלה יכולנו לחזור לשימוש בקונטיינר משותף אחד. הדבר פישט מאוד את תכנון הקיבולת המוקדם והשוטף שנדרש מצוותי המוצר, תוך שמירה על בידוד בין עומסי העבודה. בחודשים שחלפו מאז לא חווינו תקלות הקשורות להקצאת משאבים או לשכנים רועשים, למרות ההאצה בפיתוח המוצרים.
כשחברות מגיעות לקנה המידה של OpenAI, השירותים המנוהלים נעשים פחות ״מנוהלים״. עומסי עבודה רבים הופכים לחריגים בתשתית של ספק הענן. כש-OpenAI הפכה לאחת המשתמשות הגדולות ביותר ב-Azure Cosmos DB, תפעול מוצלח דרש מתכונת אחרת: בדיקות משותפות, לוחות מחוונים משותפים, ערוצי תקשורת ישירים בין צוותי ההנדסה ואחריות משותפת למצבי כשל שמופיעים רק בקנה המידה שלנו. כיום, חשבון Azure Cosmos DB הגדול ביותר שלנו מאחסן עשרות פטה-בייט של נתונים ומטפל ביותר מ-20 מיליון שאילתות בשנייה (QPS). למיטב ידיעתנו, אנחנו החברה היחידה שהפעילה את Azure Cosmos DB בקנה מידה כזה.
בסוף 2024, השבתה אזורית הבהירה מדוע שיתוף הפעולה הזה נחוץ. באותו זמן אירעה הפסקת חשמל באזור Azure הראשי של אחד מחשבונות Azure Cosmos DB הגדולים ביותר שלנו. במהלך המעבר לאזור גיבוי בעקבות התקלה, גילינו שלחשבונות הגדולים ביותר שלנו יידרשו כמה שעות כדי לעבור לאזור משני — תהליך שלא הצוות שלנו ולא צוות Azure Cosmos DB בדקו בקנה המידה שלנו.
כחודש לאחר מכן, בעת החזרת החשבונות האלה לאזור המקורי, גרמנו להשבתה חמורה נוספת. גל חיבורים בו-זמני לאזור המקורי הציף את שער הגישה של Azure Cosmos DB באותו אזור. התנהגות חריגה באופן שבו לקוח Azure Cosmos DB במצב שער גישה טיפל במעברים בעת כשל גרמה לכל הלקוחות ליצור מיד חיבורים ולשלוח בקשות לאזור המקורי, אף שרק חלק מהמחיצות הפיזיות חזרו אליו.
בכל השבתה, גם OpenAI וגם Azure הפעילו את תהליכי ניהול התקלות שלהן, זיהו כמה גורמי שורש והטמיעו תיקונים. אבל ההשבתות האלה היו תסמינים של בעיה מערכתית רחבה יותר באופן שבו הצוות שלנו וצוות Azure Cosmos DB פעלו וביצעו בדיקות. בתחילה, השימוש בשירותים מנוהלים אפשר לנו להתקדם מהר ולהתמקד ביעדים עסקיים. בקנה מידה גדול, השירותים המנוהלים נעשים פחות מנוהלים ודורשים שילוב עמוק עם ספק השירות בכל הרמות, מהתכנון ועד לתפעול.
כיום, צוותי ההנדסה של Habitat ושל Azure מנטרים יחד כל שכבה של Habitat, משכבות השירות שלנו ועד לשכבות האחסון הפיזיות של Azure Cosmos DB. אנחנו מפעילים ובודקים שינויים מול חשבון Azure Cosmos DB בגודל של סביבת הייצור, שאינו משמש לייצור. אנחנו בודקים ומשפרים יחד ערכות SDK, וקובעים שיטות עבודה מומלצות להרחבה ולקביעת גודל החשבונות שלנו ב-Azure Cosmos DB. יש לנו גישה ישירה זה לצוות ההנדסה של זה דרך Teams, ואפילו אפשרנו להזעיק זה את זה ישירות בלי לעבור דרך דרגי התמיכה.
בשנה האחרונה נעשתה עבודה משותפת נרחבת לשיפור יכולת ההרחבה של מישור הבקרה ושל שער הגישה של Azure Cosmos DB. הוספנו הגנה מפני עומס ושמירה במטמון של סוגי מטא-נתונים שונים בשער הגישה, כדי להגן על מחיצת המטא-נתונים של Azure Cosmos DB; הפחתנו את העומס שמייצר מערך תהליכי habitat-service שלנו ב-Python באמצעות מאגרי חיבורים ושימוש חוזר נרחב יותר בחיבורים; מיקמנו עומסי עבודה יחד במידת האפשר; ובודדנו עומסי עבודה קריטיים מעומסי עבודה לא מקוונים באמצעות רשתות VPC נפרדות.
רמה כזו של שיתוף פעולה הנדסי הייתה חיונית ליציבות של Azure Cosmos DB אצלנו בשנה האחרונה. כל מערכת גדולה או קריטית מספיק מחייבת עבודת צוות כזו: הסרת מחיצות בין תחומי אחריות ועבודה מעבר לגבולות הארגוניים.
כפי שציינו בחלק א׳, העיקרון המנחה שלנו הוא: קודם שזה יעבוד, אחר כך שיהיה מהיר, ולבסוף שיהיה זול. קודם אמינות, אחר כך ביצועים, ואז יעילות. Habitat הפכה לתשתית קריטית עבור OpenAI, ולכן העדיפות הראשונה הייתה להבטיח את אמינותה. כשהמערכת התייצבה ב-2025 ו-OpenAI התרחבה ברחבי העולם, זמני ההשהיה הפכו למגבלה הבאה עבור המשתמשים וצוותי המוצר.
משאבי המחשוב והאחסון היו לעיתים קרובות באזורים שונים, ולכן אי אפשר היה להימנע מבקשות חוצות אזורים.
Figure 04 · Improving performance at a global scale
Global traffic
Compute and storage capacity were often located in different regions, making cross-region requests unavoidable.
המיקומים הגאוגרפיים של משאבי המחשוב והאחסון מוצגים להמחשה בלבד.
זמני ההשהיה השתנו מאוד בהתאם למקום שבו נשמרו הנתונים ולמקום שבו טופלה הבקשה. כדי להאיץ את הבקשות ל-Habitat, מפתחי המוצרים הסתמכו על מטמון נוסף בצד הלקוח, איגוד בקשות לאצוות, צמצום הקריאות והכתיבות למסד הנתונים והוצאת עבודה מהנתיב הקריטי בכל מקום שאפשר. אמנם כך נוצרו כמה תכנוני מוצר מעניינים שהיו עמידים יותר לבעיות במסד הנתונים, אך המוצרים גם נעשו מורכבים יותר וקצב השיפור שלהם הואט. בכך לא עמדנו ביעד של Habitat: לאפשר פיתוח מוצר מהיר ללא צורך בהבנת מסד הנתונים.
זהו מוקד מרכזי בעבודה שלנו ב-2026, ופיתחנו אסטרטגיה רב-שכבתית לשיפור ביצועי הקריאה — פעולות שמהוות יותר מ-90% מהבקשות ל-Habitat:
יש לנו כמה חשבונות Azure Cosmos DB באזורי Azure שונים ברחבי העולם, עבור משתמשים עם דרישות תושבות נתונים. גם עבור משתמשים ללא דרישות מחייבות לתושבות נתונים, מיקום הנתונים בקרבתם משפר את החוויה בזכות זמני השהיה קצרים יותר.
בפועל, איננו יכולים לעשות זאת באופן נרחב מעבר למקרי השימוש הקשורים לתושבות נתונים, משום שהמיקום מוגבל על ידי הקיבולת הפיזית בענן. בשל גודל מערכי הנתונים שלנו, אזורים רבים אינם יכולים לספק את נפח האחסון העצום שנדרש עבור חלקם היחסי בתעבורת המשתמשים.
באזורים שבהם קיבולת מרכזי הנתונים, החשמל או הקירור מוגבלת, אנחנו נאלצים לפעמים לבחור: לשמור קיבולת לאחסון מקומי ולקצר את זמני ההשהיה של Habitat, או לנצל אותה למשאבי מחשוב נוספים לטיפול בבקשות ולנתב את בקשות האחסון למקומות רחוקים יותר.
לכן, החלטות על מיקום מחייבות לעיתים קרובות פשרה מוצרית בין: א) מיטוב זמני ההשהיה למוצרים ולקבוצות משתמשים שונים, לבין ב) קיבולת האחסון והמחשוב הזמינה בפועל.
בעומסי העבודה שבהם זמן ההשהיה קריטי במיוחד, כמו מטא-נתוני אימות שנדרשים בכל בקשה, אנחנו מקצרים את זמן ההלוך ושוב (RTT) בין משאבי המחשוב ל-Habitat באמצעות חשבונות Azure Cosmos DB המשוכפלים לאזורים משניים רבים. חלק מהחשבונות משוכפלים לכל 40 אזורי Azure שבהם אנחנו מטפלים בתעבורה; אחרים משוכפלים לפחות לאזור אחד בכל יבשת.
נאלצנו לבנות ב-Habitat שכבת העברת נתונים וניתוב שקופה למשתמש מוקדם מהמתוכנן, כדי שנוכל להעביר מערכי נתונים בין חשבונות בעלי טופולוגיות שכפול שונות.
עם זאת, גם הגישה הזו מוגבלת בסופו של דבר על ידי הקיבולת, ולכן היא מיושמת רק בכמה מערכי נתונים שנבחרו בקפידה.
הרוב המכריע של עומסי העבודה ב-Habitat עתיר קריאות: היישומים שולפים ללא הרף הגדרות, מצב ומאפיינים אחרים. חלק גדול באופן לא פרופורציונלי מהתעבורה נובע מקבוצת עבודה קטנה יחסית של טיפוסי נתונים שניגשים אליהם בתדירות גבוהה. לכן המערכת מתאימה במיוחד לשימוש במטמון מבוזר. מטמון משפר ביצועים כבר בתשתית של אזור יחיד, אך התועלת גדלה עוד יותר בעבודה בין אזורים, שבה אפשר להשתמש בו כדי ״להסתיר״ את ההשהיה בין האזורים.
שילבנו ב-Habitat שכבת מטמון צדדי מבוזר (lookaside). היא שומרת עותק של אובייקטים וקשתות שניגשו אליהם לאחרונה בשרתי Valkey, הממוקמים בתשתית שלנו קרוב למקום שבו מעובדות בקשות המשתמשים. בדרך כלל, מטמונים כאלה משתמשים בזמן תפוגה (TTL) שמאזן בין שיעור הפגיעות במטמון לבין מידת ההתיישנות של הנתונים. זמני TTL קצרים מפחיתים את התיישנות הנתונים, אבל מצמצמים את התועלת של המטמון, משום שתוקף הפריטים בו פג במהירות ויש לשלוף אותם שוב בתדירות גבוהה יותר. זמני TTL ארוכים יותר משפרים את שיעור הפגיעות במטמון, אבל גורמים להגשת נתונים מיושנים לאורך זמן, וכתוצאה מכך לחוויית משתמש ירודה ולהתנהגות מוצר בלתי צפויה.
גישה אחרת היא לפסול במפורש פריטים במטמון כשהיישום מבצע פעולות כתיבה על אותם נתונים במקור. כך הנתונים במטמון נשארים עדכניים הרבה יותר, אבל קשה ליישם זאת נכון. הגישה גם נשארת חשופה למקרי קצה שונים, כגון כשלים חלקיים שמשאירים במטמון נתונים מיושנים. פריסות מרובות אזורים מחריפות את הבעיות האלה: הרשתות בין האזורים אמינות פחות, ונוצרים תנאי מרוץ בין המטמון לשכפול הנתונים בשכבת האחסון. שילוב של TTL בגישה הזו מגביל את משך ההתיישנות המרבי, אך עדיין כרוך באותה פשרה בסיסית שתוארה לעיל לגבי TTL.
בחרנו בגישה מתקדמת יותר לפסילת פריטים במטמון, בעקבות מערכות כמו TAO של Meta. המטמון המשולב של Habitat מסתמך על לכידת שינויי נתונים (CDC) דרך הזנת השינויים של Azure Cosmos DB, כדי להפיץ פסילות ברחבי העולם בתוך שניות. כך מתקבלים שיעורי פגיעה גבוהים במטמון לצד רמת התיישנות נמוכה. שירות בתשתית שלנו קורא את הזנת השינויים הזו ופוסל פריטים במטמונים המקומיים כשהנתונים במקור מתעדכנים. כך נוצרת החטאת מטמון בקריאה הבאה של הנתונים, והמטמון מתמלא בגרסה העדכנית ביותר שלהם.
קיצרנו משמעותית את זמני ההשהיה גם באמצעות הסרת תקורה משירות Habitat עצמו: כתבנו מחדש את הקוד ב-Rust, שמספקת ביצועים טובים יותר מהקוד המקורי ב-Python.
יחד, השינויים האלה הורידו את זמן ההשהיה החציוני (p50) הכולל של הקריאות ברחבי העולם מכ-120 מילישניות לכ-10 מילישניות — שיפור של פי 12 מאז שהתחלנו להתמקד בביצועים מוקדם יותר השנה.
Figure 05 · Improving performance at a global scale
p50 latency for reads
Three major design changes improved read latency
אף שהמטמון קיצר מאוד את זמן ההשהיה החציוני, הבקשות האיטיות ביותר עדיין דורשות שליפת נתונים שאינם במטמון משכבת האחסון, לעיתים קרובות מאזורים מרוחקים. ככל שתתווסף קיבולת ונשפר את יעילות האחסון, אנחנו מתכננים לטפל באתגר בסיסי יותר: למקם את הנתונים מלכתחילה קרוב יותר למקום שממנו ייגשו אליהם.
מנגנון CDC אמין וניתן להרחבה — שמפיץ שינויים ממסד הנתונים למערכות המשך — הוא רכיב קריטי לא רק באסטרטגיית המטמון של Habitat. מיליוני שינויים שנלכדים בכל שנייה, באלפי קונטיינרים ובמיליוני מחיצות קונטיינר, זורמים דרך יותר ממאה נושאי Kafka. הם מניעים גם אינדקסים משניים בעלי עקביות בסופו של דבר, צינורות להעברת נתונים, ניתוח נתונים לא מקוון ותכונות מוצר נוספות בזמן אמת. ככל שהתשתית שלנו התרחבה ברחבי העולם, ההאזנה לשינויים בשורות נעשתה מורכבת יותר ויותר. אם לקוח רוצה לרשום פונקציית callback שתופעל בכל פעם ששורות מהטיפוס שלו משתנות, עליו בין היתר לברר תחילה באילו חשבונות וקונטיינרים של Cosmos נמצאים הנתונים שלו, איזו פריסה של שירות CDC פועלת באזורים המתאימים ומאזינה לקונטיינרים האלה, ולהתאים כראוי את הקיבולת של מערך תהליכי העבודה של שירות CDC, של Kafka ושל הצרכן שלו. פלטפורמת ה-CDC שלנו נועדה להסתיר ממפתחי המוצרים את כל המורכבות הזו, ולהציע במקומה ממשק פשוט שבו הלקוחות רושמים פונקציה שתרוץ על השורות שהשתנו, ואנחנו מטפלים בכל החיבורים התשתיתיים ובהרחבת הקיבולת.
מישור בקרה פנימי מוודא באופן רציף שהשינויים בכל הקונטיינרים הנדרשים נלכדים, בוחר מאילו אזורי Cosmos לקרוא, ומחלק את העבודה ביעילות בין אלפי תהליכי עבודה בכמה אזורים גאוגרפיים. שימוש באזורי שכפול ובקריאות מהזנת השינויים בעדיפות נמוכה יותר מגן על תעבורת Habitat הרגישה להשהיה מפני עומסי לכידת השינויים ברקע. במערך גדול כל כך מתרחשים ללא הרף קריסות של תהליכי עבודה, בעיות בקונטיינרים, שגיאות במאגר החכירות, הגדרות לא תקינות וכשלים ביעדי המסירה. חיזקנו את ניהול מחזור החיים של תהליכי העבודה ב-CDC ואת הטיפול בחכירות, בודדנו כשלים בין זרמי נתונים, ובנינו מנגנוני הגנה לשינויי תצורה ולפריסת גרסאות. אנחנו משתמשים בתורים להודעות שלא נמסרו כדי למנוע חסימה בראש התור עקב כשלי מסירה קבועים. אנחנו מתמודדים עם השבתות אזוריות באמצעות העברת תהליכי הקריאה לאזורי Cosmos חלופיים. בקנה המידה שלנו, אפילו נדרשנו לשנות את ערכת ה-SDK של Azure ל-CDC כדי לייעל את מימוש נקודות הביקורת: אנחנו אוגדים אותן בזיכרון וכותבים אותן לאחסון רק במרווחי זמן.
אף שהשימוש הפנימי של Habitat ב-Azure Cosmos DB מורכב, הערך שלה היה תמיד להסתיר ממפתחי המוצרים את פרטי התשתית כדי להאיץ את הפיתוח. פלטפורמת ה-CDC שלנו פועלת לפי אותו עיקרון.
צוות קטן הצליח לתמוך בפלטפורמת אחסון שצומחת במהירות באמצעות שמירה מכוונת על API אחסון מצומצם, המשך הרחבת שירות ה-Python ומתן עדיפות לאתגרים ההנדסיים הדחופים ביותר. התמקדנו קודם באמינות עבור תעבורת מוצר קריטית, אחר כך בביצועי קריאה למשתמשים ברחבי העולם, ולבסוף ביעילות תפעולית בקנה מידה גדול. הגישה הזו אפשרה צמיחה מהירה ומקרי שימוש חדשים, לצד שיפור באמינות, בביצועים ובקצב פיתוח המוצרים.
כדי להרחיב את Habitat ואת אחסון השיחות למיליארד המשתמשים הבאים, עלינו להתמודד עם הקושי לשכפל את הנתונים בתשתית שלנו בקנה מידה גדול עוד יותר. כדי להיערך לכך מראש, אנחנו בונים מערכת אחסון מקוון ייעודית בפיתוח עצמי בקנה מידה של אקסה-בייט, המותאמת לטיפוסי הנתונים גדולי הנפח של Habitat. המערכת הזו, שנמצאת בפיתוח, משלבת את ליבת RocksDB Cloud של Rockset עם שכפול מבוסס Raft. היא מיישמת את אותו מבנה מסמכים כמו Azure Cosmos DB, אך משתמשת באחסון המופרד ממשאבי המחשוב ובשכפול פיזי כדי לצמצם את קיבולת הפלאש הנדרשת, במחיר של קצב שאילתות מרבי נמוך יותר.
אם מעניין אתכם לעבוד על תשתיות נתונים כאלה בקנה מידה שנמצא בחזית הטכנולוגיה, עיינו במשרות הפתוחות שלנו.


