fix(#5130): serialize profile-scoped snapshot repair

This commit is contained in:
Rod Boev committed 2026-06-28 16:03:25 -04:00
1 parent fd139132b6
commit 2eeb0f0f53
2 files changed
+59 -11

No files matched your search

+13 -11
View File
@@ -235,6 +235,7 @@ except Exception:
# Track job IDs currently being executed so the frontend can poll status. # Track job IDs currently being executed so the frontend can poll status.
_RUNNING_CRON_JOBS: dict[str, float] = {} # job_id → start_timestamp _RUNNING_CRON_JOBS: dict[str, float] = {} # job_id → start_timestamp
_RUNNING_CRON_LOCK = threading.Lock() _RUNNING_CRON_LOCK = threading.Lock()
_CRON_CREATE_SNAPSHOT_LOCK = threading.Lock()
_MANUAL_COMPRESSION_JOBS: dict[str, dict] = {} _MANUAL_COMPRESSION_JOBS: dict[str, dict] = {}
_MANUAL_COMPRESSION_JOBS_LOCK = threading.Lock() _MANUAL_COMPRESSION_JOBS_LOCK = threading.Lock()
_MANUAL_COMPRESSION_JOB_TTL_SECONDS = 10 * 60 _MANUAL_COMPRESSION_JOB_TTL_SECONDS = 10 * 60
@@ -18256,17 +18257,18 @@ def _selected_profile_snapshot_updates(
return {} return {}
try: try:
with profile_env_for_background_worker( with _CRON_CREATE_SNAPSHOT_LOCK:
selected_profile, with profile_env_for_background_worker(
"cron create snapshot", selected_profile,
logger_override=logger, "cron create snapshot",
): logger_override=logger,
provider_snapshot, model_snapshot = _compute_provider_model_snapshots( ):
provider=provider, provider_snapshot, model_snapshot = _compute_provider_model_snapshots(
model=model, provider=provider,
base_url=None, model=model,
no_agent=False, base_url=None,
) no_agent=False,
)
except Exception: except Exception:
logger.warning( logger.warning(
"Selected-profile cron snapshot repair failed for %s; saving ambient snapshots", "Selected-profile cron snapshot repair failed for %s; saving ambient snapshots",
@@ -451,3 +451,49 @@ def test_selected_profile_snapshot_helper_never_repoints_cron_store_globals(monk
original_paths[2], original_paths[2],
), ),
] ]
def test_selected_profile_snapshot_helper_holds_lock_across_profile_env_and_compute(monkeypatch):
import api.profiles as profiles
import api.routes as routes
events = []
cron_jobs = types.ModuleType("cron.jobs")
class RecorderLock:
def __enter__(self):
events.append("lock-enter")
def __exit__(self, exc_type, exc, tb):
events.append("lock-exit")
def compute_snapshots(*, provider, model, base_url, no_agent):
events.append(("compute", provider, model, base_url, no_agent))
return "openai-codex", "gpt-5.4"
@contextmanager
def fake_profile_env(profile, purpose, logger_override=None):
events.append(("profile-enter", profile, purpose, logger_override is routes.logger))
yield
events.append(("profile-exit", profile, purpose))
cron_jobs._compute_provider_model_snapshots = compute_snapshots
monkeypatch.setattr(routes, "_CRON_CREATE_SNAPSHOT_LOCK", RecorderLock())
monkeypatch.setattr(profiles, "profile_env_for_background_worker", fake_profile_env)
_install_fake_cron_modules(monkeypatch, cron_jobs)
updates = routes._selected_profile_snapshot_updates(
"research",
provider=None,
model="gpt-5.4",
)
assert updates == {"provider_snapshot": "openai-codex"}
assert events == [
"lock-enter",
("profile-enter", "research", "cron create snapshot", True),
("compute", None, "gpt-5.4", None, False),
("profile-exit", "research", "cron create snapshot"),
"lock-exit",
]