הגדרת בדיקות אוטומטיות למיגרציות מסד נתונים ב-CI/CD

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

היכולות שלנו:

שאלות נפוצות

העבודות האחרונות

  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1359
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1330
  • עיצוב לוגו לחברת B2B Advance
    עיצוב לוגו לחברת B2B Advance
    776
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1077
  • פיתוח לוגו חברת AIDER
    פיתוח לוגו חברת AIDER
    1022
  • פיתוח CRM עבור Chasseurs
    פיתוח CRM עבור Chasseurs
    1113

בדיקה אוטומטית של מיגרציות מסד נתונים: היכן נשברת הפריסה

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

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

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

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

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

מה נותנת בדיקת שינויי סכימה לפני פריסה: זמן וסוג כשלים

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

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

בהמשך נבדקות תכונות שכמעט לא בודקים ידנית. תאימות לאחור של הסכימה — אפליקציה מהגרסה הקודמת ממשיכה לעבוד עם הסכימה החדשה, כלומר rollback של שחרור אינו דורש rollback של מיגרציה. אידמפוטנטיות של מיגרציות — הרצה חוזרת נותנת אותה תוצאה, ומיגרציה שנפלה באמצע יכולה לרוץ שוב בבטחה.

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

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

גישות להרצת שינויי סכימה: מסד ריק, עותק, רפליקה

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

לכן ב-CI/CD בדרך כלל מחזיקים לא וריאנט אחד אלא כמה — לשלבים שונים ולרמות סיכון שונות.

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

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

איך לבנות בדיקת מיגרציות בצינור: שלבי העבודה

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

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

  2. סכימת ייחוס ותיעוד גרסאות של הסכימה. מורידים dump של המצב העדכני ושומרים אותו יחד עם הקוד — כל מיגרציה חייבת להביא את המבנה לייחוס. נוצרת בדיקה דטרמיניסטית במקום «על מסד הבדיקות זה עבד כאילו».

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

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

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

  6. נוהל. מיגרציה נכנסת לשחרור רק עם בדיקות ירוקות, והחלה ידנית לסביבת הייצור — דרך בקשה נפרדת עם תיעוד הסיבה.

  7. מסירה. מוסרים תיאור של הצינור, סדר תורנויות וניתוח כשלים אופייניים, כדי שהצוות ילווה את הסכימה בהמשך בעצמו.

מה הלקוח מקבל לידיו: קונפיגורציות, בדיקות, נהלים

העבודה מסתיימת לא בדוח, אלא בערכה שהצוות שלכם מריץ ומתחזק בלעדינו. כל מה שמוגדר בסביבת הבדיקות נמסר באותה צורה שבה הוא עובד בסביבה הראשית — עם תיאור למה נבחרו ספים כאלה ואיך לשנות אותם.

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

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

מקרה בוחן: איך הסכימה הפסיקה לשבור את הפריסה לאחר הטמעת בדיקות

לפני: הסכימה הייתה שונה בין סביבות

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

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

מה שינו

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

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

מה קרה בסופו של דבר

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

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

במה שונה הרצה על מסד ריק מהרצה על עותק של הייצור?

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

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

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

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

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

ספקות של מהנדס: שינויים מורכבים בסכימה, דריפט, rollback-ים

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

הסכימה בייצור כבר סטתה מהמיגרציות — הבדיקות יראו זאת?

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

שינוי סכימה נמשך זמן רב וחוסם טבלה — מה עושים עם זה?

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

איך נבדקת תאימות לאחור והאם rollback אפשרי?

הבדיקות מרימות זו לצד זו את הגרסה הקודמת והחדשה של האפליקציה ובודקות ששתיהן עובדות על אותה סכימה — אז rollback של הפריסה אינו דורש rollback של מסד הנתונים. הסקריפט ההפוך מורץ על נתונים שנותרו אחרי הישיר; אם rollback בלי אובדן אינו אפשרי, זה מתועד בשלב האישור מראש.

הנתונים רגישים — איך בודקים אם אסור להעתיק אותם?

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

נדון במשימה שלכם: מה נדייק ובמה נחזור

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

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

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