התשתית של Google מתוכננת לפעול בצורה גמישה בקנה מידה גדול: רוב השכבות יכולות להתאים את עצמן לדרישות של נפח תנועה מוגבר, עד לקנה מידה עצום. דפוס עיצוב מרכזי שמאפשר זאת הוא שכבות אדפטיביות – רכיבי תשתית שמקצים מחדש את העומס באופן דינמי על סמך דפוסי התנועה. עם זאת, לוקח זמן להסתגל. מכיוון ש-Cloud Tasks מאפשר לשלוח נפחי תנועה גדולים מאוד, הוא חושף סיכונים בסביבת הייצור במצבים שבהם נפח התנועה יכול לגדול מהר יותר מהיכולת של התשתית להסתגל.
סקירה כללית
במסמך הזה מפורטות הנחיות לגבי השיטות המומלצות לשמירה על רמת ביצועים גבוהה של Cloud Tasks בתורים עם נפח תנועה גבוה. תור עם TPS גבוה הוא תור שבו נוצרות או נשלחות 500 משימות לשנייה (TPS) או יותר. קבוצת תורים עם TPS גבוה היא קבוצה רציפה של תורים, למשל [queue0001, queue0002, …, queue0099], שנוצרו או נשלחו בה לפחות 2,000 משימות בסך הכול. אפשר לראות את ה-TPS ההיסטורי של תור או קבוצת תורים באמצעות מדדי Cloud Monitoring, api/request_count עבור פעולות CreateTask ו-queue/task_attempt_count עבור ניסיונות של משימות. תורים עמוסים וקבוצות של תורים עלולים לסבול משני סוגים שונים של כשלים:
עומס יתר בתור מתרחש כשקצב יצירת המשימות וההקצאה שלהן לתור ספציפי או לקבוצת תורים עולה מהר יותר מהקצב שבו התשתית של התור יכולה להסתגל. באופן דומה, עומס יתר על היעד מתרחש כשהקצב שבו המשימות נשלחות גורם לעליות חדות בתנועה בתשתית היעד במורד הזרם. בשני המקרים, מומלץ לפעול לפי תבנית של 500/50/5: כשמגדילים את נפח התנועה מעבר ל-500 TPS, מגדילים את נפח התנועה ב-50% לכל היותר כל 5 דקות. במאמר הזה מפורטים תרחישים שונים שעלולים להוביל לסיכוני התרחבות, ומוצגות דוגמאות לאופן השימוש בתבנית הזו.
עומס יתר בתור
תורים או קבוצות של תורים יכולים להיות עמוסים מדי בכל פעם שיש עלייה פתאומית בתנועת הגולשים. כתוצאה מכך, יכולות להיות בעיות בתורים האלה:
- הארכת זמן האחזור של יצירת משימות
- שיעור השגיאות ביצירת משימות עלה
- שיעור משלוחים מופחת
כדי להתגונן מפני מצב כזה, מומלץ להגדיר אמצעי בקרה בכל מצב שבו קצב היצירה או השליחה של תור או קבוצת תורים עלול לעלות בפתאומיות. מומלץ להגדיר עד 500 פעולות בשנייה לתור קר או לקבוצת תורים, ואז להגדיל את נפח התנועה ב-50% כל 5 דקות. באופן תיאורטי, אפשר להגיע ל-740,000 פעולות בשנייה אחרי 90 דקות באמצעות תוכנית ההגדלה הזו. יש כמה נסיבות שבהן זה יכול לקרות.
לדוגמה:
- השקת תכונות חדשות שמשתמשות באופן נרחב ב-Cloud Tasks
- העברת תנועה בין תורים
- איזון מחדש של תנועת הגולשים בין יותר תורים או פחות תורים
- הרצת משימות אצווה שמזריקות מספרים גדולים של משימות
במקרים האלה ובמקרים אחרים, כדאי לפעול לפי התבנית 500/50/5.
הגדלה הדרגתית של התור הראשוני
הדפוס 500/50/5 חל על תורים שכבר פועלים במצב יציב. ההתחלה של תור קר היא תהליך עצמאי שנועד להגיע למצב היציב הראשוני הזה בצורה בטוחה.
כשמדובר בתור חדש או בתור שלא פעיל, המערכת לא מקצה מיידית משימות בקצב maxDispatchesPerSecond שהוגדר לו. כדי להגן על המערכת מפני עליות פתאומיות בתנועה, Cloud Tasks מגדיל בהדרגה את קצב השליחה.
אם יש לכם קצב יצירה קבוע של משימות, המערכת תתאים את עצמה בסופו של דבר לקצב השליחה שהגדרתם. עם זאת, אם התנועה שלכם לא יציבה, עם פרצי משימות שמרווחים ביניהם יותר משבוע של חוסר פעילות, יכול להיות שתבחינו בהתנהגות הזו של עלייה הדרגתית. זו התנהגות צפויה והיא מבטיחה יציבות.
שימוש בחלוקת תנועה ב-App Engine
אם המשימות נוצרות על ידי אפליקציית App Engine, אפשר לנצל את היתרונות של פיצול התנועה ב-App Engine (Standard/Flex) כדי למתן את העלייה בתנועה. חלוקת התנועה בין הגרסאות (רגילה/גמישה) מאפשרת להגדיל בהדרגה את מספר הבקשות שצריך לנהל את הקצב שלהן, כדי לשמור על תקינות התור. לדוגמה, נניח שרוצים להגדיל את נפח התעבורה לקבוצת תורים שהורחבה לאחרונה: נניח ש-[queue0000, queue0199] היא סדרה של תורים עם TPS גבוה שמקבלים בסך הכול 100,000 יצירות TPS בשיא.
Let [queue0200, queue0399] be a sequence of new queues. אחרי שכל התנועה מועברת, מספר התורים ברצף מוכפל וטווח התורים החדש מקבל 50% מהתנועה הכוללת של הרצף.
כשפורסים את הגרסה שמגדילה את מספר התורים, מגדילים בהדרגה את התנועה לגרסה החדשה, וכך גם לתורים החדשים, באמצעות פיצול תנועה:
- מתחילים להעביר 1% מהתנועה לגרסה החדשה. לדוגמה, 50% מ-1% מ-100,000 TPS הם 500 TPS לקבוצת התורים החדשה.
- כל 5 דקות, מגדילים ב-50% את נפח התנועה שנשלח לגרסה החדשה, כמו שמפורט בטבלה הבאה:
| מספר הדקות מאז תחילת הפריסה | % מנפח התנועה הכולל שהועבר לגרסה החדשה | אחוז נפח התנועה הכולל לתורים החדשים | % מתוך נפח התנועה הכולל לתורים הישנים |
|---|---|---|---|
| 0 | 1.0 | 0.5 | 99.5 |
| 5 | 1.5 | 0.75 | 99.25 |
| 10 | 2.3 | 1.15 | 98.85 |
| 15 | 3.4 | 1.7 | 98.3 |
| 20 | 5.1 | 2.55 | 97.45 |
| 25 | 7.6 | 3.8 | 96.2 |
| 30 | 11.4 | 5.7 | 94.3 |
| 35 | 17.1 | 8.55 | 91.45 |
| 40 | 25.6 | 12.8 | 87.2 |
| 45 | 38.4 | 19.2 | 80.8 |
| 50 | 57.7 | 28.85 | 71.15 |
| 55 | 86.5 | 43.25 | 56.75 |
| 60 | 100 | 50 | 50 |
עליות חדות בתנועת הגולשים בעקבות הפצה
כשמשיקים גרסה שגורמת לעלייה משמעותית בתנועת הגולשים בתור או בקבוצת תורים, פריסה הדרגתית היא שוב מנגנון חשוב להחלקת העליות. כדאי להשיק את המופעים בהדרגה כך שההשקה הראשונית לא תעלה על 500 פעולות כוללות בתורים החדשים, ולהגדיל את מספר הפעולות ב-50% לכל היותר כל 5 דקות.
תורים חדשים עם TPS גבוה או קבוצות תורים
תורים חדשים חשופים במיוחד. קבוצות של תורים, למשל [queue0000, queue0001, …, queue0199], רגישות בדיוק כמו תורים בודדים בשלבי ההשקה הראשוניים. במקרים כאלה, חשוב להשתמש באסטרטגיה של השקה הדרגתית. השקת שירותים חדשים או מעודכנים שיוצרים תורים או קבוצות תורים עם TPS גבוה, בשלבים כך שהעומס הראשוני יהיה מתחת ל-500 TPS והגידולים של 50% או פחות יהיו בהפרש של 5 דקות או יותר.
קבוצות תורים חדשות שהורחבו
כשמגדילים את הקיבולת הכוללת של קבוצת תורים, למשל כשמרחיבים את [queue0000-queue0199 ל-queue0000-queue0399], צריך לפעול לפי התבנית 500/50/5. חשוב לציין שבתהליכי השקה, קבוצות חדשות של תורים לא מתנהגות באופן שונה מתורים בודדים. להחיל את התבנית 500/50/5 על הקבוצה החדשה כולה, ולא רק על תורים ספציפיים בתוך הקבוצה. גם בהרחבות של קבוצות התורים האלה, חשוב להשתמש באסטרטגיה של השקה הדרגתית. אם המקור של התנועה הוא App Engine, אפשר להשתמש בפיצול תנועה (ראו זינוקים בתנועה שנובעים מהשקות). כשמעבירים את השירות כדי להוסיף משימות למספר המוגדל של התורים, צריך להפעיל את המופעים בהדרגה כך שההשקה הראשונית לא תעלה על 500 פעולות בסך הכול בתורים החדשים, וכל 5 דקות להגדיל את מספר הפעולות ב-50% לכל היותר.
הרחבה של קבוצת התור להפעלה במקרה חירום
לפעמים כדאי להרחיב קבוצת תורים קיימת, למשל אם צפוי שיוספו לקבוצת התורים משימות בקצב מהיר יותר מהקצב שבו הקבוצה יכולה להקצות אותן. אם השמות של התורים החדשים מפוזרים באופן שווה בין השמות של התורים הקיימים כשממיינים אותם לפי סדר מילוני, אפשר לשלוח תנועה לתורים האלה באופן מיידי, בתנאי שאין יותר מ-50% תורים חדשים שמשולבים בתורים הקיימים, והתנועה לכל תור היא פחות מ-500 TPS. השיטה הזו היא חלופה לשימוש בפיצול תנועה ובהשקה הדרגתית, כפי שמתואר בסעיפים הקודמים.
כדי להשיג שמות משולבים כאלה, אפשר להוסיף סיומת לתורים שמסתיימים במספרים זוגיים. לדוגמה, אם יש לכם 200 תורים קיימים [queue0000-queue0199] ואתם רוצים ליצור 100 תורים חדשים, כדאי לבחור [queue0000a, queue0002a, queue0004a, …, queue0198a] כשמות של התורים החדשים, במקום [queue0200-queue0299].
אם אתם צריכים להגדיל את המכסה עוד יותר, אתם יכולים לשלב עד 50% יותר תורים כל 5 דקות.
הוספה לתור של משימות בכמות גדולה או בקנה מידה גדול
כשצריך להוסיף מספר גדול של משימות, למשל מיליונים או מיליארדים, כדאי להשתמש בתבנית של הזרקה כפולה. במקום ליצור משימות מעבודה אחת, כדאי להשתמש בתור של מזין. כל משימה שמוסיפים לתור של מנגנון ההזרקה מתפצלת ומוסיפה 100 משימות לתור הרלוונטי או לקבוצת התורים הרלוונטית. אפשר להגדיל את מהירות התור של מנגנון ההזרקה לאורך זמן. לדוגמה, להתחיל ב-5 TPS ואז להגדיל ב-50% כל 5 דקות.
משימות עם שם
כשיוצרים משימה חדשה, Cloud Tasks מקצה למשימה שם ייחודי כברירת מחדל. אפשר להקצות שם משלכם למשימה באמצעות הפרמטר name. עם זאת, הפעולה הזו יוצרת תקורה משמעותית בביצועים, וכתוצאה מכך זמני האחזור מתארכים ושיעורי השגיאות עולים, במיוחד במשימות עם שמות. העלויות האלה יכולות להיות גבוהות במיוחד אם המשימות נקראות ברצף, למשל באמצעות חותמות זמן. לכן, אם אתם מקצים שמות משלכם, מומלץ להשתמש בתוספת לשם של המאפיין שמפוזרת היטב בשמות המשימות, כמו גיבוב של התוכן. מידע נוסף על מתן שם למשימה
עומס יתר על היעד
אם השליחות מתור ארוך מדי, יכול להיות ש-Cloud Tasks יעמיס יתר על המידה על שירותים אחרים שבהם אתם משתמשים, כמו App Engine, Datastore והשימוש ברשת. אם הצטברות של משימות ממתינה, ביטול ההשהיה של התורים האלה עלול לגרום לעומס יתר על השירותים האלה. ההגנה המומלצת היא אותה תבנית של 500/50/5 שהוצעה לעומס יתר בתור: אם תור שולח יותר מ-500 TPS, צריך להגדיל את התנועה שהופעלה על ידי התור ב-50% לכל היותר כל 5 דקות. כדי לעקוב באופן יזום אחרי העלייה בתנועת הגולשים, אפשר להשתמש במדדים של Cloud Monitoring. אתם יכולים להשתמש בהתראות של Cloud Monitoring כדי לזהות מצבים שעלולים להיות מסוכנים.
ביטול ההשהיה או חידוש של תורים עם TPS גבוה
כשמבטלים את ההשהיה של תור או סדרה של תורים, או מפעילים אותם מחדש, התורים ממשיכים להקצות פניות. אם יש הרבה משימות בתור, קצב השליחה של התור שהופעל עכשיו יכול לעלות באופן משמעותי מ-0 TPS לקיבולת המלאה של התור. כדי להגדיל את נפח התעבורה, אפשר להשהות את התורים לסירוגין או לשלוט בקצב השליחה של התורים באמצעות maxDispatchesPerSecond של Cloud Tasks.
משימות מתוזמנות בכמות גדולה
גם מספר גדול של משימות שמתוזמנות להפצה באותו הזמן עלול לגרום לעומס יתר על היעד. אם אתם צריכים להתחיל מספר גדול של משימות בבת אחת, כדאי להשתמש באמצעי בקרה על קצב התור כדי להגדיל את קצב השליחה בהדרגה, או להגדיר מראש את קיבולת היעד.
הגדלת fan-out
כשמעדכנים שירותים שמופעלים דרך Cloud Tasks, הגדלת מספר הקריאות מרחוק עלולה ליצור סיכונים בסביבת הייצור. לדוגמה, נניח שהמשימות בתור עם TPS גבוה קוראות ל-handler /task-foo. גרסה חדשה יכולה להגדיל באופן משמעותי את עלות הקריאה /task-foo
אם, לדוגמה, הגרסה החדשה מוסיפה כמה קריאות יקרות ל-Datastore ל-handler. התוצאה הסופית של גרסה כזו תהיה עלייה משמעותית בתנועת הנתונים ב-Datastore, שקשורה באופן מיידי לשינויים בתנועת המשתמשים. כדי לנהל את ההשקה, אפשר להשתמש בהשקה הדרגתית או בפיצול תנועה.
ניסיונות חוזרים
הקוד יכול לנסות שוב במקרה של כשל כשמבצעים קריאות ל-API של Cloud Tasks. עם זאת, אם חלק משמעותי מהבקשות נכשלות בגלל שגיאות בצד השרת, שיעור גבוה של ניסיונות חוזרים עלול לגרום לעומס יתר בתורים שלכם ואפילו להאט את ההתאוששות שלהם. לכן, אם הלקוח מזהה שחלק משמעותי מהבקשות נכשלות עם שגיאות בצד השרת, מומלץ להגביל את כמות התנועה היוצאת. לדוגמה, אפשר להשתמש באלגוריתם של ויסות דינמי שמתואר בפרק Handling Overload בספר Site Reliability Engineering. ספריות הלקוח של gRPC של Google מטמיעות וריאציה של האלגוריתם הזה.