הבעיה
שחזור גיבוי אישי משייך ישויות לפי שם, ושם אינו מפתח ייחודי בשום מקום במערכת. כשקיימים שני לוחות או שני אוספים באותו שם, השחזור ממזג אותם לאחד — בשקט, בלי שגיאה, ובלי שהמשתמש יידע.
זה נמצא בסבב ריוויוו על PR #3306 ולא תוקן שם, כי התיקון המלא דורש שינוי סכימה שחורג מהסקופ של אותו PR. פירוט למטה.
למה שני לוחות באותו שם הם מצב חוקי
אין ולידציית ייחודיות ב-create_board ולא ב-rename_board, ואין אינדקס ייחודי על השם. האינדקס היחיד על note_boards הוא one_default_per_user — ייחודי-חלקי על is_default: True בלבד.
בנוסף, normalize_board_name("") מחזיר את DEFAULT_BOARD_NAME, כך שאפשר להגיע לשני לוחות באותו שם גם בלי כוונה.
שתי שכבות, ולא אחת
לוחות — _restore_note_boards בונה קבוצת שמות קיימים ומדלג על לוח שהשם שלו כבר קיים; _restore_board_note מוצא את הלוח לפי find_one({user_id, name}). כששני לוחות חולקים שם, השאילתה מחזירה אחד מהם באופן שרירותי וכל הפתקים של שניהם נוחתים עליו.
אוספים — אותו דפוס, וכאן הוא מעניין יותר: ב-_restore_collections המזהה המקורי של האוסף (old_id) כן נמצא בגיבוי, ובכל זאת existing_collections הוא מילון ממופתח בשם. כשבחשבון היעד יש שני אוספים באותו שם, המילון שומר את האחרון מביניהם — ו-old_to_new_id[old_id] מצביע על אוסף שנבחר שרירותית. כל הקבצים של האוסף מהגיבוי נכנסים לאוסף הלא נכון.
לוחות הם המקרה החמור יותר (אין old_id בכלל בגיבוי), אבל האוספים מראים שהשורש אינו חוסר מידע — אלא בחירה לשייך לפי שם.
למה זה לא תוקן ב-PR
התיקון שנראה מתבקש — לשמור את המזהה המקורי בגיבוי ולשייך לפיו — שובר אידמפוטנטיות. במסמך היעד אין שדה שמתעד מאיזה לוח בגיבוי הוא הגיע, ולכן שחזור שני של אותו גיבוי לא יזהה שהלוח כבר נוצר וייצור כפילות. כדי לשמור אידמפוטנטיות צריך שדה מוצא מתמיד על מסמך הלוח — כלומר שינוי סכימה, מחוץ לסקופ של PR שעוסק ב-created_at.
שתי דרכים אפשריות
בלי המלצה — שתיהן דורשות החלטה:
- אכיפת ייחודיות שם (אינדקס ייחודי + ולידציה ב-
create_board/rename_board). פותר את השורש, אבל משנה התנהגות קיימת ודורש טיפול בכפילויות שכבר קיימות אצל משתמשים.
- שדה מוצא + מיפוי
old_id ← new_id, כמו שכבר קיים לאוספים. משנה את פורמט הגיבוי ומוסיף שדה לסכימה, ומצריך תאימות לאחור לגיבויים ישנים.
הערה על תיקון חלקי
מיפוי old_id ← new_id בלי שדה מוצא היה משפר את השיוך רק ללוחות שהשחזור עצמו יצר. לוח שהותאם ללוח קיים עדיין נבחר לפי שם, ולכן שני לוחות קיימים באותו שם היו נשארים בעיה. שווה לדעת לפני שבוחרים מסלול שנראה זול.
הבעיה
שחזור גיבוי אישי משייך ישויות לפי שם, ושם אינו מפתח ייחודי בשום מקום במערכת. כשקיימים שני לוחות או שני אוספים באותו שם, השחזור ממזג אותם לאחד — בשקט, בלי שגיאה, ובלי שהמשתמש יידע.
זה נמצא בסבב ריוויוו על PR #3306 ולא תוקן שם, כי התיקון המלא דורש שינוי סכימה שחורג מהסקופ של אותו PR. פירוט למטה.
למה שני לוחות באותו שם הם מצב חוקי
אין ולידציית ייחודיות ב-
create_boardולא ב-rename_board, ואין אינדקס ייחודי על השם. האינדקס היחיד עלnote_boardsהואone_default_per_user— ייחודי-חלקי עלis_default: Trueבלבד.בנוסף,
normalize_board_name("")מחזיר אתDEFAULT_BOARD_NAME, כך שאפשר להגיע לשני לוחות באותו שם גם בלי כוונה.שתי שכבות, ולא אחת
לוחות —
_restore_note_boardsבונה קבוצת שמות קיימים ומדלג על לוח שהשם שלו כבר קיים;_restore_board_noteמוצא את הלוח לפיfind_one({user_id, name}). כששני לוחות חולקים שם, השאילתה מחזירה אחד מהם באופן שרירותי וכל הפתקים של שניהם נוחתים עליו.אוספים — אותו דפוס, וכאן הוא מעניין יותר: ב-
_restore_collectionsהמזהה המקורי של האוסף (old_id) כן נמצא בגיבוי, ובכל זאתexisting_collectionsהוא מילון ממופתח בשם. כשבחשבון היעד יש שני אוספים באותו שם, המילון שומר את האחרון מביניהם — ו-old_to_new_id[old_id]מצביע על אוסף שנבחר שרירותית. כל הקבצים של האוסף מהגיבוי נכנסים לאוסף הלא נכון.לוחות הם המקרה החמור יותר (אין
old_idבכלל בגיבוי), אבל האוספים מראים שהשורש אינו חוסר מידע — אלא בחירה לשייך לפי שם.למה זה לא תוקן ב-PR
התיקון שנראה מתבקש — לשמור את המזהה המקורי בגיבוי ולשייך לפיו — שובר אידמפוטנטיות. במסמך היעד אין שדה שמתעד מאיזה לוח בגיבוי הוא הגיע, ולכן שחזור שני של אותו גיבוי לא יזהה שהלוח כבר נוצר וייצור כפילות. כדי לשמור אידמפוטנטיות צריך שדה מוצא מתמיד על מסמך הלוח — כלומר שינוי סכימה, מחוץ לסקופ של PR שעוסק ב-
created_at.שתי דרכים אפשריות
בלי המלצה — שתיהן דורשות החלטה:
create_board/rename_board). פותר את השורש, אבל משנה התנהגות קיימת ודורש טיפול בכפילויות שכבר קיימות אצל משתמשים.old_id ← new_id, כמו שכבר קיים לאוספים. משנה את פורמט הגיבוי ומוסיף שדה לסכימה, ומצריך תאימות לאחור לגיבויים ישנים.הערה על תיקון חלקי
מיפוי
old_id ← new_idבלי שדה מוצא היה משפר את השיוך רק ללוחות שהשחזור עצמו יצר. לוח שהותאם ללוח קיים עדיין נבחר לפי שם, ולכן שני לוחות קיימים באותו שם היו נשארים בעיה. שווה לדעת לפני שבוחרים מסלול שנראה זול.