feat: add entitlement_dirty_watch_task for fast entitlement cache refresh
Polls a marker file bumped by the Whop webhook (srebot-web) so new purchases show up in /diagnose and /unlock within seconds instead of waiting on the hourly entitlement_cache_task.
This commit is contained in:
@@ -343,6 +343,49 @@ async def before_entitlement_cache():
|
|||||||
await get_bot().wait_until_ready()
|
await get_bot().wait_until_ready()
|
||||||
|
|
||||||
|
|
||||||
|
# ── Entitlement cache refresh (webhook-triggered, fast path) ────────────────
|
||||||
|
# The Whop webhook lands in the separate srebot-webhook/srebot-web (Node)
|
||||||
|
# process, which only writes to entitlements.db — it has no direct channel
|
||||||
|
# into this process. It signals us by bumping the mtime of a marker file;
|
||||||
|
# we poll that file cheaply (a single stat call) every few seconds and do a
|
||||||
|
# full cache reload only when it actually changes, so new purchases show up
|
||||||
|
# in /diagnose and /unlock within seconds instead of waiting on the hourly
|
||||||
|
# entitlement_cache_task or an incidental refresh from another loop.
|
||||||
|
ENTITLEMENTS_DIRTY_PATH: Path = STORAGE_DIR / "entitlements.dirty"
|
||||||
|
_entitlements_dirty_mtime: float = 0.0
|
||||||
|
|
||||||
|
|
||||||
|
@tasks.loop(seconds=5)
|
||||||
|
async def entitlement_dirty_watch_task():
|
||||||
|
global _entitlements_dirty_mtime
|
||||||
|
try:
|
||||||
|
mtime = ENTITLEMENTS_DIRTY_PATH.stat().st_mtime
|
||||||
|
except FileNotFoundError:
|
||||||
|
return
|
||||||
|
if mtime <= _entitlements_dirty_mtime:
|
||||||
|
return
|
||||||
|
_entitlements_dirty_mtime = mtime
|
||||||
|
try:
|
||||||
|
await refresh_entitled_guilds(force=True)
|
||||||
|
await _record("entitlement_dirty_watch", True)
|
||||||
|
except Exception as e:
|
||||||
|
await _record("entitlement_dirty_watch", False, str(e))
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
@entitlement_dirty_watch_task.before_loop
|
||||||
|
async def before_entitlement_dirty_watch():
|
||||||
|
await get_bot().wait_until_ready()
|
||||||
|
# Seed with the current mtime (if any) so a stale flag left over from
|
||||||
|
# before startup doesn't trigger a redundant refresh on the first tick —
|
||||||
|
# on_ready already does a forced refresh.
|
||||||
|
global _entitlements_dirty_mtime
|
||||||
|
try:
|
||||||
|
_entitlements_dirty_mtime = ENTITLEMENTS_DIRTY_PATH.stat().st_mtime
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# POINTS ALARM TASK
|
# POINTS ALARM TASK
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
@@ -693,6 +736,7 @@ async def start_all_tasks():
|
|||||||
"""
|
"""
|
||||||
# Phase 1: Lightweight time-check tasks
|
# Phase 1: Lightweight time-check tasks
|
||||||
entitlement_cache_task.start()
|
entitlement_cache_task.start()
|
||||||
|
entitlement_dirty_watch_task.start()
|
||||||
ldb_alarm_task.start()
|
ldb_alarm_task.start()
|
||||||
squadron_stats_boundary_task.start()
|
squadron_stats_boundary_task.start()
|
||||||
points_alarm_task.start()
|
points_alarm_task.start()
|
||||||
|
|||||||
Reference in New Issue
Block a user