הממצא
ב-webapp/templates/settings.html, בלוק ה-JS מקונן בתוך בלוק התוכן:
9: {% block content %}
1935: {% block extra_js %} ← כ-2300 שורות JS
4250: {% endblock %}
4251: {% endblock %}
ב-Jinja, בלוק שמוגדר בתוך בלוק אחר בתבנית-בת מרונדר פעמיים: פעם אחת במקומו בתוך content כשהתבנית-האם מרנדרת את {% block content %} (base.html:2172), ופעם שנייה בחריץ {% block extra_js %} שב-base.html:5175.
התוצאה: כל פונקציה, כל addEventListener וכל אתחול בבלוק הזה קיימים ורצים פעמיים.
הראיות
מדידה על העמוד המוגש בפועל (Flask מקומי, משתמש מחובר):
id="stickyFontsCard" 1 ← ה-markup מרונדר פעם אחת
id="noteFontsMsg" 1
── גופן הפתקים 2 ← ה-JS מרונדר פעמיים
function persist( 2
function notify( 2
והתוצאה המעשית, נמדדה בכרומיום עם יירוט בקשות — שינוי בורר אחד:
{ "posts_for_one_change": 2,
"bodies": ["{\"note_fonts\":{...},\"note_fonts_scope\":\"global\"}",
"{\"note_fonts\":{...},\"note_fonts_scope\":\"global\"}"] }
שתי בקשות POST /api/ui_prefs זהות, על שינוי אחד.
היקף — לא רק כרטיס אחד
הכפילות חלה על כל הבלוק. דוגמה חלקית מהפונקציות שמוגדרות פעמיים בעמוד המוגש:
exportBackup handleRestoreFile diskBackupNow driveBackupNow
disconnectDrive loadDriveStatus loadDiskStatus loadThemesList
persistThemeScope openPopover closePopover markSeen resetSeen
וספירת קריאות רשת בעמוד המוגש: fetch('/api/drive/backup ×4, fetch('/api/disk ×6, fetch('/api/backup/export ×2, fetch('/api/backup/restore ×2.
שמירת העדפות היא idempotent ולכן הנזק שם הוא בזבוז בלבד. מה שדורש בדיקה הוא הפעולות שאינן בהכרח כאלה — גיבוי לדיסק, גיבוי ל-Drive, ייצוא ושחזור. לא בדקתי אותן, ואיני טוען שהן שבורות; אני מדווח שהן יושבות באותו בלוק כפול ולכן צריך לבדוק כל אחת.
התיקון המוצע
הבלוק כבר יושב במקום סביר בתוך content. הדרך הקצרה היא להסיר את תגי הבלוק המקוננים (שורות 1935 ו-4250) ולהשאיר את ה-JS בדיוק היכן שהוא — אז הוא מרונדר פעם אחת, במיקום שבו הוא כבר רץ ראשון היום. החריץ {% block extra_js %} של base.html יישאר ריק, כפי שהוא בתבניות אחרות.
זה לא PR של שתי שורות. הסרת ההרצה השנייה משנה את ההתנהגות של כל מה שבבלוק: קוד שהסתמך בלי דעת על כך שהעותק השני דורס משתנה גלובלי, או שהמאזין הכפול "כיסה" משהו, ישתנה. זה דורש מעבר על הבלוק ובדיקה בדפדפן, ולכן סבב נפרד.
איך זה התגלה
בזמן אימות תיקון ב-#3293. מדידת ההנפשה בדפדפן הראתה שתי קריאות ל-ckToast על שינוי אחד, מה שלא הסתדר עם הקוד. המעקב הוביל לכפילות הרינדור.
בדרך התברר גם שהשרת המקומי מחזיק את התבנית מקומפלת בזיכרון, כך שעריכה על הדיסק אינה משפיעה על מה שמוגש בלי הפעלה מחדש — שווה לזכור בכל בדיקת תבנית ידנית.
הממצא
ב-
webapp/templates/settings.html, בלוק ה-JS מקונן בתוך בלוק התוכן:ב-Jinja, בלוק שמוגדר בתוך בלוק אחר בתבנית-בת מרונדר פעמיים: פעם אחת במקומו בתוך
contentכשהתבנית-האם מרנדרת את{% block content %}(base.html:2172), ופעם שנייה בחריץ{% block extra_js %}שב-base.html:5175.התוצאה: כל פונקציה, כל
addEventListenerוכל אתחול בבלוק הזה קיימים ורצים פעמיים.הראיות
מדידה על העמוד המוגש בפועל (Flask מקומי, משתמש מחובר):
והתוצאה המעשית, נמדדה בכרומיום עם יירוט בקשות — שינוי בורר אחד:
{ "posts_for_one_change": 2, "bodies": ["{\"note_fonts\":{...},\"note_fonts_scope\":\"global\"}", "{\"note_fonts\":{...},\"note_fonts_scope\":\"global\"}"] }שתי בקשות
POST /api/ui_prefsזהות, על שינוי אחד.היקף — לא רק כרטיס אחד
הכפילות חלה על כל הבלוק. דוגמה חלקית מהפונקציות שמוגדרות פעמיים בעמוד המוגש:
וספירת קריאות רשת בעמוד המוגש:
fetch('/api/drive/backup×4,fetch('/api/disk×6,fetch('/api/backup/export×2,fetch('/api/backup/restore×2.שמירת העדפות היא idempotent ולכן הנזק שם הוא בזבוז בלבד. מה שדורש בדיקה הוא הפעולות שאינן בהכרח כאלה — גיבוי לדיסק, גיבוי ל-Drive, ייצוא ושחזור. לא בדקתי אותן, ואיני טוען שהן שבורות; אני מדווח שהן יושבות באותו בלוק כפול ולכן צריך לבדוק כל אחת.
התיקון המוצע
הבלוק כבר יושב במקום סביר בתוך
content. הדרך הקצרה היא להסיר את תגי הבלוק המקוננים (שורות 1935 ו-4250) ולהשאיר את ה-JS בדיוק היכן שהוא — אז הוא מרונדר פעם אחת, במיקום שבו הוא כבר רץ ראשון היום. החריץ{% block extra_js %}שלbase.htmlיישאר ריק, כפי שהוא בתבניות אחרות.זה לא PR של שתי שורות. הסרת ההרצה השנייה משנה את ההתנהגות של כל מה שבבלוק: קוד שהסתמך בלי דעת על כך שהעותק השני דורס משתנה גלובלי, או שהמאזין הכפול "כיסה" משהו, ישתנה. זה דורש מעבר על הבלוק ובדיקה בדפדפן, ולכן סבב נפרד.
איך זה התגלה
בזמן אימות תיקון ב-#3293. מדידת ההנפשה בדפדפן הראתה שתי קריאות ל-
ckToastעל שינוי אחד, מה שלא הסתדר עם הקוד. המעקב הוביל לכפילות הרינדור.בדרך התברר גם שהשרת המקומי מחזיק את התבנית מקומפלת בזיכרון, כך שעריכה על הדיסק אינה משפיעה על מה שמוגש בלי הפעלה מחדש — שווה לזכור בכל בדיקת תבנית ידנית.