fix(clock): dedup tasks lock bug removal

This commit is contained in:
2026-10-16 01:08:50 +02:00
parent 27d3c08a72
commit 1ca4643774
5 changed files with 113 additions and 22 deletions
+18 -9
View File
@@ -1,4 +1,5 @@
from datetime import timedelta
from uuid import uuid4
from django.conf import settings
from django.core.cache import cache
@@ -10,7 +11,9 @@ from .services.steam_store import refresh_steam_games
# Set while a periodic refresh is queued, so there's only ever one loop running,
# whatever the number of workers/containers trying to start it. It expires on
# its own if the loop dies (worker killed mid-job, Redis queue flushed, ...),
# letting the next schedule_steam_refresh() start a new one.
# letting the next schedule_steam_refresh() start a new one. The guard holds the
# loop id, so if it expires while the old loop is just late, the old one stops
# at its next run (instead of 2 loops running side by side).
STEAM_REFRESH_GUARD_KEY = "games:steam_refresh:scheduled"
@@ -25,28 +28,34 @@ def _guard_timeout() -> int:
def schedule_steam_refresh() -> bool:
"""Start the periodic refresh loop right away, unless one is already queued.
Safe to call from anywhere, any number of times."""
if not cache.add(STEAM_REFRESH_GUARD_KEY, 1, timeout=_guard_timeout()):
loop_id = uuid4().hex
if not cache.add(STEAM_REFRESH_GUARD_KEY, loop_id, timeout=_guard_timeout()):
return False
refresh_steam_games_task.enqueue()
refresh_steam_games_task.enqueue(loop_id=loop_id)
return True
@task
def refresh_steam_games_task(
game_ids: list[int] | None = None, reschedule: bool = True
game_ids: list[int] | None = None, loop_id: str | None = None
):
"""Refresh Steam store info, then re-enqueue itself every
STEAM_STORE_REFRESH_MINUTES (same self-rescheduling idea as
sync_event_game_scopes_task, no external scheduler needed).
reschedule=False is a one-off run (e.g. the admin action) that leaves the
periodic loop alone."""
Without loop_id it's a one-off run (e.g. the admin action) that leaves the
periodic loop alone. With loop_id, it only continues while the guard still
holds this loop_id."""
if loop_id is not None and cache.get(STEAM_REFRESH_GUARD_KEY) != loop_id:
# Another loop owns the guard (or it expired), stop this one
return
try:
refresh_steam_games(game_ids)
finally:
if reschedule:
cache.set(STEAM_REFRESH_GUARD_KEY, 1, timeout=_guard_timeout())
if loop_id is not None:
cache.set(STEAM_REFRESH_GUARD_KEY, loop_id, timeout=_guard_timeout())
refresh_steam_games_task.using(
run_after=timezone.now() + steam_refresh_interval()
).enqueue()
).enqueue(loop_id=loop_id)