You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
טסטים מחליפים את השעון של כל התהליך (monkeypatch על mod.time ו-freeze_time) — וזה דולף לתהליכוני רקע #3487
הרבה טסטים שולטים בזמן דרך הכינוי של מודול, למשל monkeypatch.setattr(limits.time, "monotonic", ...). זה נראה כאילו רק השעון של limits משתנה, אבל limits.time הוא המודול time הגלובלי (ב-mcp_server/limits.py יש import time). כלומר לאורך הטסט time.monotonic מוחלף לכל התהליך, בכל התהליכונים. freeze_time עושה אותו דבר מעצם הגדרתו: ב-freezegun 1.5.5, _freeze_time.start ב-freezegun/api.py מציב time.monotonic = fake_monotonic, וגם את time.time, time.perf_counter ועוד.
תהליכון שקורא את השעון בתוך החלון מקבל את הערך המזויף, והשחזור בסוף הטסט לא מתקן את זה. אם התהליכון חישב דדליין או שמר חותמת זמן, הערך השגוי נשאר גם אחרי שהטסט נגמר.
הרצה מקומית, על הקוד האמיתי:limits.time is time, sticky_notes_api.time is time ו-backend_mod._time is time מחזירים שלושתם True. התקנתי שעון שמקדים ב-600 שניות דרך limits.time.monotonic, והסרתי אותו בדיוק כמו ש-monkeypatch עושה. 20 שניות אחרי שהשעון כבר שוחזר, _DB_INIT_LOCK עדיין היה תפוס, ולדדליין של pymongo נותרו 585 שניות.
פגיעה קודמת באותה משפחה כבר מתועדת בריפו:tests/test_http_sync_new.py, שורות 35–38: "this patches the *global* time module, so any extra perf_counter() call (an error path, metrics, logging) drains the list and raises IndexError". הצורה השבירה שההערה מזהירה ממנה עדיין נמצאת ב-tests/test_http_sync_logging.py:30, עם lambda: counters.pop(0).
מתי ערך מזויף מסוכן
השעון המונוטוני ב-Linux סופר מאז שהמכונה עלתה. כאן מדדתי time.monotonic() = 301.5, ובאותו רגע /proc/uptime = 301.53.
ערך מזויף שמקדים את השעון האמיתי, כמו 1000.0 על מכונה שעלתה לפני פחות מ-1000 שניות, יוצר דדליין שנמשך ההפרש ביניהם בשניות.
freezegun תמיד מקדים, כי הוא מחזיר שניות מאז 1970.
time.sleep שהוחלף בפונקציה ריקה גורם לכל לולאת רקע שקוראת לו בתוך החלון לרוץ בלי הפסקה.
לא בדקתי כמה זמן מכונות ה-CI דולקות כשהטסטים רצים, ולכן אני לא יודע אם 1000.0 ו-500.0 שבטסטים הקדימו שם את השעון האמיתי.
תפר שעון לכל מודול. המודול קורא לשעון דרך הפניה משלו, למשל _monotonic = time.monotonic ברמת המודול ו-_monotonic() בקוד, והטסט מחליף את limits._monotonic. אז השינוי נשאר בתוך המודול ולא נוגע בתהליכונים אחרים. זה שינוי קטן בקוד הייצור, מודול אחרי מודול.
freezegun.configure(extend_ignore_list=[...]) מגן רק חלקית._should_use_real_time מחזיר זמן אמיתי רק כשמודול מרשימת ההתעלמות נמצא עד 5 מסגרות מעל הקריאה (call_stack_inspection_limit = 5 ב-freezegun/api.py). למשל "pymongo" יגן על הקריאות של pymongo עצמו, אבל לא על קוד הייצור שלנו שרץ באותו תהליכון.
מה הבעיה
הרבה טסטים שולטים בזמן דרך הכינוי של מודול, למשל
monkeypatch.setattr(limits.time, "monotonic", ...). זה נראה כאילו רק השעון שלlimitsמשתנה, אבלlimits.timeהוא המודולtimeהגלובלי (ב-mcp_server/limits.pyישimport time). כלומר לאורך הטסטtime.monotonicמוחלף לכל התהליך, בכל התהליכונים.freeze_timeעושה אותו דבר מעצם הגדרתו: ב-freezegun 1.5.5,_freeze_time.startב-freezegun/api.pyמציבtime.monotonic = fake_monotonic, וגם אתtime.time,time.perf_counterועוד.תהליכון שקורא את השעון בתוך החלון מקבל את הערך המזויף, והשחזור בסוף הטסט לא מתקן את זה. אם התהליכון חישב דדליין או שמר חותמת זמן, הערך השגוי נשאר גם אחרי שהטסט נגמר.
ראיות שזה נושך
push-senderחישב את הדדליין של pymongo בתוךfreeze_timeוהחזיק את_DB_INIT_LOCKלתמיד, כלומר לדדליין שרחוק 57 שנה. התוצאה: 7 שגיאות ב-CI.limits.time is time,sticky_notes_api.time is timeו-backend_mod._time is timeמחזירים שלושתםTrue. התקנתי שעון שמקדים ב-600 שניות דרךlimits.time.monotonic, והסרתי אותו בדיוק כמו ש-monkeypatch עושה. 20 שניות אחרי שהשעון כבר שוחזר,_DB_INIT_LOCKעדיין היה תפוס, ולדדליין של pymongo נותרו 585 שניות.tests/test_http_sync_new.py, שורות 35–38: "this patches the *global* time module, so any extra perf_counter() call (an error path, metrics, logging) drains the list and raises IndexError". הצורה השבירה שההערה מזהירה ממנה עדיין נמצאת ב-tests/test_http_sync_logging.py:30, עםlambda: counters.pop(0).מתי ערך מזויף מסוכן
השעון המונוטוני ב-Linux סופר מאז שהמכונה עלתה. כאן מדדתי
time.monotonic()= 301.5, ובאותו רגע/proc/uptime= 301.53.time.sleepשהוחלף בפונקציה ריקה גורם לכל לולאת רקע שקוראת לו בתוך החלון לרוץ בלי הפסקה.לא בדקתי כמה זמן מכונות ה-CI דולקות כשהטסטים רצים, ולכן אני לא יודע אם 1000.0 ו-500.0 שבטסטים הקדימו שם את השעון האמיתי.
הרשימה (מצב נכון ל-29.09.2026)
monotonic(4)tests/test_mcp_limits.py:612,tests/test_mcp_notes_handlers.py:619,tests/test_note_boards_api.py:1107,tests/test_distributed_lock_heartbeat_and_wait.py:70sleep(2)tests/test_distributed_lock_heartbeat_and_wait.py:71,tests/test_google_drive_retries.py:180perf_counter(3)tests/test_http_sync_logging.py:30,tests/test_http_sync_new.py:42,tests/test_observability_api.py:283time(20, ב-7 קבצים)tests/test_cache_manager_fallback_local.py:46,tests/test_github_rate_limit_resources.py:13,45,tests/test_github_rate_limit_more_resources.py:130,tests/test_main_maintenance_gate.py:130,172,tests/test_observability_log_aggregator_processor.py:69,80,82,84,tests/test_single_error_alerts.py:51,70,74,78,89,110,121,tests/test_webserver_observability_extras.py:231,269,318freeze_time(2)tests/test_israel_timezone_display.py:80,97בדקתי שכל מודול שהשעון שלו מוחלף טוען את
timeהגלובלי:import time:cache_manager.py,github_menu_handler.py,http_sync.py,main.py,monitoring/log_analyzer.py,observability.py,services/webserver.py,services/google_drive_service.py,mcp_server/limits.py,webapp/sticky_notes_api.py.import time as _time:mcp_server/backend.py,webapp/app.py.הרשימה היא צילום מצב. כדי לרענן אותה:
grep -rnE "setattr\(\s*[A-Za-z_][A-Za-z_0-9.]*\b_?time\s*,\s*['\"](monotonic|time|sleep|perf_counter|monotonic_ns|time_ns)['\"]|freeze_time" tests/כיוון לתיקון (לא מומש, להכרעה)
_monotonic = time.monotonicברמת המודול ו-_monotonic()בקוד, והטסט מחליף אתlimits._monotonic. אז השינוי נשאר בתוך המודול ולא נוגע בתהליכונים אחרים. זה שינוי קטן בקוד הייצור, מודול אחרי מודול.nowמפורש, או להשאיר את freezegun ולוודא שאין בתהליך הטסטים תהליכון ייצור שרץ ברקע (push-sender עולה בכל תהליך שמייבא את webapp.app — ב-CI הוא נתקע עם _DB_INIT_LOCK ומפיל קובץ טסטים שלם #3486).freezegun.configure(extend_ignore_list=[...])מגן רק חלקית._should_use_real_timeמחזיר זמן אמיתי רק כשמודול מרשימת ההתעלמות נמצא עד 5 מסגרות מעל הקריאה (call_stack_inspection_limit = 5ב-freezegun/api.py). למשל"pymongo"יגן על הקריאות של pymongo עצמו, אבל לא על קוד הייצור שלנו שרץ באותו תהליכון.קשור