diff --git a/cron/scheduler_ownership.py b/cron/scheduler_ownership.py index fc92a52531..bc0027d918 100644 --- a/cron/scheduler_ownership.py +++ b/cron/scheduler_ownership.py @@ -40,7 +40,7 @@ def register_ticked_homes(homes) -> None: live until process exit, so serving (or churning) many profiles leaks one ThreadPoolExecutor and its worker threads per home ever ticked. """ - from cron.store_health import forget_homes, store_key + from cron.store_health import forget_stores, store_key resolved, stores = {}, {} for home in homes: @@ -50,7 +50,8 @@ def register_ticked_homes(homes) -> None: stores[key] = store_key(path) with _ticked_lock: departed = set(_ticked_homes) - set(resolved) - departed_stores = [_ticked_stores[k] for k in departed if k in _ticked_stores] + # A store another still-ticked profile reaches (cron/ symlinked to one place) keeps its state. + departed_stores = {_ticked_stores[k] for k in departed if k in _ticked_stores} - set(stores.values()) _ticked_homes.clear() _ticked_homes.update(resolved) _ticked_stores.clear() @@ -59,7 +60,7 @@ def register_ticked_homes(homes) -> None: # Late import: cron.scheduler imports this module. from cron.scheduler import discard_parallel_pools discard_parallel_pools(departed) - forget_homes(departed_stores) + forget_stores(departed_stores) def ticked_homes() -> dict: diff --git a/tests/cron/test_due_scan_save_failure.py b/tests/cron/test_due_scan_save_failure.py index 3663d2768e..85e365d966 100644 --- a/tests/cron/test_due_scan_save_failure.py +++ b/tests/cron/test_due_scan_save_failure.py @@ -456,7 +456,11 @@ def test_unwritable_store_is_shown_in_cron_status_and_announced_once(cron_store, assert any(r.levelname == "WARNING" and "unwritable notice for" in r.getMessage() for r in caplog.records) from cron import scheduler_ownership # the profile leaves this gateway, its home already deleted monkeypatch.setattr(scheduler_ownership, "_ticked_homes", {}) - scheduler_ownership.register_ticked_homes([profile]) + sibling = cron_store / "sibling" # a second profile whose cron/ reaches the same store + sibling.mkdir() + (sibling / "cron").symlink_to(cron_dir, target_is_directory=True) + scheduler_ownership.register_ticked_homes([profile, sibling]) + scheduler_ownership.register_ticked_homes([profile]) # sibling leaves; the store is still ticked assert store_health.degraded_record(cron_dir) is not None shutil.rmtree(profile) # removes the cron/ symlink, never the store it points at scheduler_ownership.register_ticked_homes([])