fix(cron): keep store state another still-ticked profile shares through a symlink

Two profiles whose cron/ resolves to one store share a store key; when one left, its departure dropped the degraded record the remaining profile still needed (gauge flipped to writable, a second outage notice, and the earlier outage start lost for one-shot cover). Departed stores now exclude the ones still ticked. The notices test pins it (red without the set difference).
This commit is contained in:
kshitijk4poor
2026-10-07 17:43:04 +05:30
committed by kshitij
parent 204f0b2e2f
commit af40505f87
2 changed files with 9 additions and 4 deletions

View File

@@ -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:

View File

@@ -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([])