רינדור שנכשל לא צריך למחוק פלט תקין

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

פורסם 2026-10-04. תגיות: Reliability, Media, Testing.

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

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

הבעיה: יעד קבוע הוא גם הבטחה

לעורך יש כתובת הורדה קבועה, למשל:

output/sample.mp4

הכתובת הזאת נוחה למשתמש ולממשק. אבל אם Remotion כותב אליה ישירות, הקובץ הקיים מפסיק להיות "הגרסה התקינה האחרונה" כבר בתחילת הרינדור. הוא יכול להיחתך, להתחלף חלקית או להפוך לקובץ שלא ניתן לפתוח.

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

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

שלב ראשון: לרנדר לקובץ מועמד

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

output/.sample.candidate-<unique-id>.mp4

הקובץ הקבוע אינו משתנה בזמן הרינדור. אם כבר קיים `sample.mp4` תקין, קישור ההורדה ממשיך להגיש אותו גם כאשר ניסיון חדש עדיין רץ.

המיקום באותה מערכת קבצים חשוב. הוא מאפשר לשלב הקידום להשתמש בפעולת `rename` יחידה במקום להעתיק קובץ גדול אל היעד. הקובץ המועמד נשאר פרטי עד שהמערכת מוכנה להתחייב אליו.

זה דומה לכתיבה טרנזקציונית קטנה:

Render candidate
      ↓
Validate candidate
      ↓
Promote candidate
      ↓
Write matching manifest

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

שלב שני: הצלחת התהליך אינה הוכחת תקינות

קוד יציאה `0` ממנוע הרינדור הוא סימן חשוב, אבל הוא אינו מספר מה באמת נמצא בקובץ. לפני הקידום, המערכת מריצה `ffprobe` ובודקת שה־candidate הוא קובץ רגיל ולא ריק ושיש בו זרם וידאו.

לאחר מכן היא משווה את התוצאה לחוזה הפרויקט:

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

שלב שלישי: קידום במקום כתיבה ישירה

רק candidate שעבר אימות מקודם אל `sample.mp4`. הקידום מתבצע באמצעות `rename` מאותה תיקייה, כך שהחלפת הזהות של הקובץ היא שלב קצר ונפרד מהרינדור הארוך.

אם הרינדור נכשל, האימות דוחה את הקובץ או פעולת הקידום עצמה נכשלת, הפלט הרשמי הקודם נשאר במקומו. הקובץ החלקי נמחק ב־cleanup.

כך המשתמש מקבל התנהגות פשוטה יותר:

לפני הניסיון: פלט A זמין
בזמן הרינדור: פלט A עדיין זמין
כשל: פלט A עדיין זמין
הצלחה ואימות: פלט B מחליף את פלט A

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

גם למניפסט יש אמת משלו

לצד ה־MP4 נשמר manifest שמתאר את מקור הרינדור ואת מאפייני הפלט שאומתו. גם כאן סדר הפעולות משנה.

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

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

Timeout וביטול הם מסלולי כשל רגילים

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

בכל אחד מהם תהליך הרינדור נעצר, ה־candidate החלקי מנוקה, מצב `rendering` משתחרר והפלט התקין האחרון נשמר.

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

הבדיקות שהיו חשובות באמת

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

  1. כשל באמצע הרינדור משאיר את הפלט ואת המניפסט הקודמים ללא שינוי.
  2. candidate פגום נדחה ואינו מחליף את הקובץ הרשמי.
  3. כשל בקידום משאיר את הפלט הישן ומנקה את הקובץ הזמני.
  4. timeout וביטול משמרים את הפלט ומשחררים את המערכת לניסיון הבא.
  5. הצלחה מקדמת את הקובץ רק אחרי האימות וכותבת manifest שמתאים לו.

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

מה למדתי

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

הדפוס שאקח למערכות אחרות הוא:

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