במסמך הזה מוסבר איך לצמצם את ההשפעה של כשלים בעבודות בצינורות גדולים של עיבוד נתונים באצווה. לכשלים בעומסי עבודה גדולים יש השפעה משמעותית במיוחד, כי נדרשים זמן וכסף כדי להתאושש מהם ולתקן אותם. ניסיון חוזר להפעיל את צינורות הנתונים האלה מההתחלה אם הם נכשלים הוא יקר מבחינת זמן וכסף.
כדי לצמצם את מספר הכשלים היקרים בצינורות להעברת נתונים באצווה, מומלץ לפעול לפי ההנחיות שבדף הזה. אי אפשר תמיד להימנע לחלוטין מרכיבים שנכשלו ומכשלים בצינורות עיבוד נתונים. לכן, הטכניקות שמוצגות כאן מתמקדות בשיפור העמידות, בהפחתת העלויות של כשלים ובפישוט תהליך הניפוי של כשלים וההבנה שלהם כשהם מתרחשים.
שיטות מומלצות כלליות לשימוש בפייפליין מפורטות במאמר בנושא שיטות מומלצות לשימוש בפייפליין ב-Dataflow.
הפעלת ניסויים קטנים למשימות גדולות
לפני שמריצים משימה גדולה באצווה, מריצים משימה אחת או יותר על קבוצת משנה של מערך הנתונים. הטכניקה הזו יכולה לספק הערכת עלויות ולעזור לזהות נקודות שבהן עלולות להיות בעיות.
אומדן עלויות
הפעלת ניסויים יכולה לספק הערכה של העלות המינימלית הכוללת של הפעלת העבודה. בדרך כלל, החישוב של עלות העבודה הוא cost of test
job*size(full dataset)/size(test dataset). בהתאם לצינור, העלות יכולה לגדול באופן על-ליניארי או, לעיתים רחוקות יותר, באופן תת-ליניארי. עם זאת, השלב הזה לרוב מספק הערכה גסה טובה של עלות העבודה. אפשר גם לנסות גדלים שונים של נתוני קלט כדי לקבל אומדן טוב יותר של האופן שבו העלויות גדלות. המידע הזה יעזור לכם להחליט אם להמשיך להשתמש בצינור הקיים או לשנות את הארכיטקטורה שלו כדי להפחית את העלויות.
איתור נקודות כשל
הפעלת ניסויים יכולה לחשוף באגים, נקודות כשל פוטנציאליות או בעיות פוטנציאליות בהגדרות וביעילות. אפשר גם לבדוק מדדים אחרים של צינורות, כמו המדדים הבאים:
- אם צינור הנתונים משתמש כמעט בכל הזיכרון הזמין, יכול להיות שיופיעו חריגות של חוסר זיכרון (OOM) בעומס גבוה יותר או ברשומות גדולות במיוחד. יכול להיות שתצטרכו להקצות יותר זיכרון לעבודה הסופית כדי להימנע משגיאות OOM.
- אם יש ירידות בנפח התפוקה של צינור עיבוד הנתונים, כדאי לבדוק את היומנים של צינור עיבוד הנתונים כדי להבין למה. יכול להיות שתזהו רכיב תקוע או חלק ממערך הנתונים עם ביצועים גרועים במיוחד. אפשר לעבד את נקודות הנתונים האלה בנפרד, או להגדיר פסק זמן לעיבוד הרכיבים. מידע נוסף מופיע בסעיף הגדרת פסק זמן לרשומות יקרות במאמר הזה.
- אם הביצועים של צינור עיבוד הנתונים גרועים בהרבה במשימה ב-Dataflow מאשר באופן מקומי, כדאי לבדוק את הלוגיקה של צינור עיבוד הנתונים כדי להבין למה. לדוגמה, אם אתם מקבלים את אותה מהירות העברת נתונים עם שמונה ליבות ב-Dataflow כמו עם ליבה אחת באופן מקומי, יכול להיות שצוואר הבקבוק של העבודה הוא התחרות על משאב. אם הביצועים שלכם נמוכים מהצפוי, כדאי לנסות אחת או יותר מהאפשרויות הבאות:
- להריץ עוד ניסויים עם הגדרות שונות של מכונות או תוכנות.
- בודקים באופן מקומי עם כמה ליבות בו-זמנית.
- כדאי לבדוק את הקוד כדי לזהות צווארי בקבוק פוטנציאליים כשמבצעים פריסה בהיקף נרחב.
אם יש המלצות לשיפור הביצועים של צינור עיבוד הנתונים ב-Dataflow, כדאי לפעול לפיהן.
שימוש בתורים של הודעות שלא ניתן להעביר כדי לטפל בנתונים פגומים לא צפויים
לרוב, צינורות העיבוד מצליחים לעבד את רוב רכיבי הקלט, אבל נכשלים בעיבוד של קבוצת משנה קטנה של הקלט. יכול להיות שלא תזהו את הבעיה הזו כשמריצים ניסויים קטנים, כי הניסויים האלה בודקים רק קבוצת משנה של הקלט. כברירת מחדל, Dataflow מנסה שוב לבצע את המשימות שנכשלו ארבע פעמים במצב אצווה ומספר בלתי מוגבל של פעמים במצב סטרימינג. במצב אצווה, אחרי שמגיעים למגבלת הניסיונות החוזרים, כל העבודה נכשלת. במצב סטרימינג, יכול להיות שההפעלה תיעצר לזמן בלתי מוגבל.
בהרבה משימות, אפשר להחריג את האלמנטים שנכשלו מצינור העיבוד ולהשלים את שאר המשימה באמצעות תור של הודעות שלא עברו עיבוד (dead-letter queue). תור ההודעות שלא נמסרו מעביר רשומות שנכשלו לפלט נפרד PCollection, שאפשר לנהל אותו בנפרד מהפלט הראשי. ההגדרה הזו מאפשרת לכם לעצב מדיניות עבור הרשומות האלה. לדוגמה, אפשר לכתוב אותם ל-Pub/Sub באופן ידני, לבדוק ולנקות אותם, ואז לעבד מחדש את הרשומות.
הרבה טרנספורמציות של Apache Beam כוללות תמיכה מובנית בתורי הודעות שלא ניתן להעביר.
ב-Java, אפשר לגשת אליהם באמצעות אובייקט ErrorHandler
ב-Python, אפשר לגשת אליהם באמצעות ה-method with_exception_handling. לחלק מהטרנספורמציות יש דרכים מותאמות אישית להגדרת תורי הודעות שלא ניתן למסור, ואפשר לקרוא על כך במסמכי התיעוד של הטרנספורמציה. מידע נוסף מופיע במאמר שימוש בתורי הודעות שלא נמסרו לטיפול בשגיאות.
כדי לבדוק אם העבודה שלכם עומדת בקריטריונים לתור הודעות שלא נמסרו, אפשר לעיין בקטע מגבלות במסמך הזה.
מגבלות של תור הודעות שלא ניתן להעביר
בתרחישים הבאים, תור של הודעות שלא ניתן להעביר לא יכול לעזור:
- כשלים במחזור החיים של העובד או של
DoFn. אם העיבוד נכשל עבור כל העובד או החבילה, תור של הודעות שלא ניתן להעביר לא יכול לתפוס את הכשל. לדוגמה, אם בצינור העברת הנתונים מתרחש חריג של חוסר זיכרון (OOM), כל המשימות הפעילות במכונה הווירטואלית נכשלות ומבוצע ניסיון חוזר להפעיל אותן, בלי לשלוח משהו לתור של הודעות שלא ניתן להעביר. - שילובים או צבירות אחרות. אם צינור הנתונים מבצע חישובים שדורשים שכל רכיבי הקלט יהיו נוכחים ויעברו עיבוד כחלק מהתוצאה, צריך להיזהר כשמשתמשים בתור של הודעות שלא ניתן להעביר לפני השלב הזה. השימוש בתור הודעות שלא ניתן להעביר מוציא חלק מנתוני הקלט מהתוצאה. הוספה של תור הודעות שלא ניתן למסור עשויה להוביל לכך שהמערכת תהיה סובלנית יותר לתקלות, אבל לא תמיד תפעל בצורה נכונה.
- כשלים בנתיב של תור ההודעות המתות. אם רכיב נכשל בזמן שהוא נשלח ליעד של תור ההודעות המתות, יכול להיות שכל צינור העיבוד ייכשל.
כדי להימנע מכשל כזה, כדאי לשמור על לוגיקה בסיסית ככל האפשר בתור להודעות שלא ניתן להעביר. אפשר להוסיף שלב המתנה (ראו
wait class) כדי לוודא שהקלט הראשי יסתיים לפני הכתיבה של רכיבי תור ההודעות שלא ניתן למסור. ההגדרה הזו עלולה לפגוע בביצועים ולגרום לעיכוב באיתותים של שגיאות מצינור הנתונים. - רכיבים שעברו שינוי חלקי. אם מוסיפים תור של הודעות שלא ניתן להעביר (dead-letter queue) באמצע הצינור, יכול להיות שהתור הזה יוציא את הרכיב שעבר טרנספורמציה חלקית ולא תהיה לו גישה לרכיב המקורי. כתוצאה מכך, אי אפשר לנקות את הרכיב ולהפעיל מחדש את צינור הנתונים מולו. במקום זאת, יכול להיות שתצטרכו להחיל לוגיקה שונה כדי ליצור קורלציה בין הפלט בתור להודעות שלא ניתן להעביר לבין הרכיב המקורי, או שתצטרכו לפרש ולעבד את הרכיב שעבר טרנספורמציה חלקית. יכול להיות שגם התוצאות יהיו לא עקביות. לדוגמה, אם רכיבים נשלחים בשני ענפים של צינור, וכל ענף שולח רכיבים שגורמים לחריגה לתור של הודעות שלא נמסרו, יכול להיות שרכיב קלט יגיע לאחד מהענפים, לשני הענפים, לשניהם או לאף אחד מהם.
הגדרת פסק זמן לרשומות יקרות
יכול להיות שצינורות עיבוד הנתונים יפסיקו להגיב בזמן עיבוד של קבוצת משנה קטנה של רכיבים שהעלות שלהם גבוהה יותר או שהם הגיעו למגבלה שגורמת לחוסר תגובה, כמו מצב של קיפאון. כדי לצמצם את הבעיה, חלק מהטרנספורמציות מאפשרות להגדיר זמן קצוב לתפוגה ולגרום לכשל של רכיבים שהגיעו לזמן קצוב לתפוגה בכל רכיבי DoFn של קוד משתמש שנתקלים בבעיה הזו. לדוגמה, אפשר להשתמש בשיטה with_exception_handling של Python. כשמשתמשים בערכי timeout עם תור של הודעות שלא ניתן להעביר, הצינור יכול להמשיך לעבד רכיבים תקינים ולהתקדם, ואפשר לעבד מחדש את הרכיבים היקרים בנפרד. התצורה הזו עלולה לפגוע בביצועים.
כדי לקבוע אילו פעולות של DoFn צפויות לדרוש זמן קצוב לתפוגה, מומלץ להריץ ניסויים קטנים לפני שמפעילים את צינור העיבוד המלא.
הפעלת שינוי גודל אוטומטי אנכי
אם אתם לא בטוחים כמה זיכרון נדרש לעבודה שלכם או חושבים שיש סיכון שהזיכרון ייגמר, כדאי להפעיל את התכונה 'שינוי גודל אוטומטי אנכי'. התכונה הזו עוזרת להימנע מכשלים של OOM כשצינורות פועלים בקנה מידה גדול יותר או כשהם נתקלים ברכיבים גדולים במיוחד.
יכול להיות שהתאמה אוטומטית לעומס אנכית תגדיל את העלות של העבודה, והיא לא מונעת את כל הכשלים שקשורים לזיכרון. לכן, עדיין צריך לטפל בבעיות של צריכת זיכרון מוגזמת. התאמה אוטומטית לעומס (automatic scaling) גם מחייבת שימוש ב-Dataflow Prime, שיש לו מגבלות נוספות ומודל תמחור שונה.
שימוש בהרצה ספקולטיבית כדי להימנע מבעיות של תהליכים שמתעכבים
בצינורות להרצת אצווה, אפשר להפעיל ביצוע ספקולטיבי, תכונה שמיועדת לצמצום ההשפעה של משימות שרצות לאט או נתקעות. משימות כאלה שמתעכבות או נתקעות נקראות גם משימות שמתעכבות. התכונה הזו מפעילה משימות מיותרות או גיבויים של משימות שנמשכות יותר מדי זמן. המשימה הראשונה שמסתיימת היא זו שמשמשת, והשנייה מבוטלת. כך אפשר לשפר את זמן ההשלמה הכולל של צינור הנתונים.
ביצוע ספקולטיבי יכול לעזור להשלים צינורות מהר יותר על ידי מתן נתיב ביצוע חלופי לפריטי עבודה שחווים עיכובים בגלל מכונות עבודה איטיות או בעיות זמניות אחרות, כמו באגים לא דטרמיניסטיים, הגבלת קצב העברת נתונים או בעיות בקישוריות.
מגבלות ושיקולים
לפני שמפעילים ביצוע ספקולטיבי, כדאי לשים לב לנקודות הבאות:
- צינורות עיבוד נתונים של סטרימינג: אין תמיכה בהרצה ספקולטיבית בצינורות עיבוד נתונים של סטרימינג.
- שינוי פוטנציאלי בעלות: קשה להעריך את ההשפעה של התכונה הזו על העלות, כי קשה לחזות את מספר המשימות שמתעכבות ואת הקצאת המשימות לגיבוי. לדוגמה, פריט עבודה של גיבוי צורך משאבים נוספים, ולכן יכול להגדיל את העלות. עם זאת, השלמתו מוקדם יותר יכולה להוביל לחיסכון במשאבים ולהפחתת העלות. בשני התרחישים, ההשפעה הכוללת צפויה להיות מינימלית.
- פריטי עבודה שפועלים לאורך זמן באופן עקבי: יכול להיות שהביצוע הספקולטיבי לא יעזור באופן משמעותי לפריטי עבודה שפועלים לאורך זמן באופן עקבי, כמו מקשי קיצור, כי הבעיה הבסיסית שגורמת להאטה עדיין תהיה קיימת.
מידע נוסף על משימות שמתעכבות בעבודות אצווה זמין במאמר פתרון בעיות של משימות שמתעכבות בעבודות אצווה.
הפעלת ביצוע ספקולטיבי
כדי להפעיל ביצוע ספקולטיבי, משתמשים באפשרות map_task_backup_mode Dataflow service. יש שני מצבים:
Java
--dataflowServiceOptions=map_task_backup_mode=ON--dataflowServiceOptions=map_task_backup_mode=CAUTIOUS
Python / Go
--dataflow_service_options=map_task_backup_mode=ON--dataflow_service_options=map_task_backup_mode=CAUTIOUS
במצב ON, מתוזמנת משימת גיבוי אם זמן הריצה הצפוי של המשימה המקורית ארוך בכ-20% מזמן הריצה הצפוי של משימה חדשה.
במצב CAUTIOUS, מתוזמנת משימת גיבוי אם זמן הריצה הצפוי של המשימה המקורית ארוך בכ-70% מזמן הריצה הצפוי של משימה חדשה.
כדי לוודא שהביצוע הספקולטיבי מופעל, בודקים את ההודעות ביומן. חפשו רשומות שבהן מוצגות משימות גיבוי שהופעלו. האישור הזה מעיד שההפעלה הספקולטיבית מופעלת. כדי לראות את היומנים האלה, עוברים לחלונית Job Logs (יומני משימות) של צינור הנתונים (Jobs > בחירת המשימה > הקטע Logs (יומנים) > Job logs). הודעת היומן מופיעה כך:
Backup issued in step STEP_NAME. ADDITIONAL_INFORMATION.
פתרונות עקיפים לצינורות עיבוד נתונים שמועדים לכישלון
חלק מהצינורות נוטים במיוחד לשגיאות. עדיף לטפל במקור השגיאות האלה, אבל כדי לצמצם את העלות של הכשלים, אפשר לשקול את האפשרויות הבאות.
יצירת תוצאות ביניים
יכול להיות שיהיו צינורות עם טרנספורמציות יקרות במיוחד שמשפיעות על זמן הביצוע של הצינור. כשלים בצינורות אחרי הטרנספורמציה הזו עלולים להיות בעייתיים במיוחד, כי כל העבודה שכבר הושלמה תאבד. כדי להימנע מהתרחיש הזה, כדאי לשקול לכתוב את נתוני הביניים PCollections שנוצרו על ידי שלבים יקרים ל-Sink כמו Cloud Storage. ההגדרה הזו מפחיתה את העלות של כשל. כדאי לשקול את היתרון הזה מול העלות של ביצוע הכתיבה הנוספת. אפשר להשתמש בתוצאה המגובשת הזו באחת מהדרכים הבאות:
- מפצלים את צינור עיבוד הנתונים המקורי לשני צינורות עיבוד נתונים: אחד שכותב את התוצאה הזמנית ואחד שקורא אותה.
- רק אם הצינור נכשל, קוראים את התוצאות מהמקור המקורי ומהאוסף הזמני שנוצר, ומיישרים אותן.
כדי לוודא שהחומרים האלה נכתבים לפני עיבוד נוסף, מוסיפים שלב המתנה (ראו wait
class) לפני כל שלבי העיבוד הבאים.