Preserve calendar failure cooldown across worker restarts
GitHub Git mirror / mirror (push) Successful in 5s
Development CI / check-and-build (push) Successful in 2m14s
Development CI / deploy-development (push) Failing after 8s

This commit is contained in:
eezd committed 2026-11-05 13:32:19 +08:00
1 parent d0c2beb9b3
commit 655813d4d9
3 files changed
+29 -2

No files matched your search

+1 -1
View File
@@ -93,7 +93,7 @@ async def save_schedule(session: AsyncSession, source: str, url: str, events: li
row.stage = event.stage
if row.release_url is None and event.release_url is not None:
row.release_url = event.release_url
row.next_check_at = max(now, event.at)
row.next_check_at = max(row.next_check_at, now, event.at) if getattr(row, "collection_failures", 0) else max(now, event.at)
row.revision_no += 1
record_revision(session, row, raw_id)
# 仅撤回尚未公布的未来事件;过去记录不因官网滚动窗口而被删除。
+9 -1
View File
@@ -178,13 +178,17 @@ class CalendarWorker:
urls += [("eia", eia.SCHEDULE)]
urls += [("adp", adp.INDEX)]
async with SessionLocal() as session:
disabled = set(await session.scalars(select(Source.code).where(Source.code.in_(BOUNDED_SOURCES), Source.enabled.is_(False))))
controls = list(await session.scalars(select(Source).where(Source.code.in_(BOUNDED_SOURCES))))
disabled = {source.code for source in controls if not source.enabled}
paused = {source.code for source in controls if source.paused_until and source.paused_until > now}
urls = [(s, u) for s, u in urls if s not in disabled]
checked = {}
# 启动时明确各来源尚未就绪;月份滚动后移除不再检查的旧月状态。
self.sources = {url: self.sources.get(url, {"source": source, "checked_at": now.isoformat(), "error": "尚未同步"})
for source, url in urls}
for source, url in urls:
if source in paused:
continue
retry_at = self.sources[url].get("retry_at")
if retry_at and datetime.fromisoformat(retry_at) > datetime.now(UTC):
continue
@@ -263,9 +267,13 @@ class CalendarWorker:
source.consecutive_failures = 0
source.last_success_at = datetime.now(UTC)
source.last_error = None
source.paused_until = None
else:
source.consecutive_failures += 1
source.last_error = outcomes[0]
deadlines = [datetime.fromisoformat(state['retry_at']) for state in self.sources.values()
if state['source'] == code and state.get('retry_at')]
source.paused_until = max([datetime.now(UTC) + timedelta(hours=1), *deadlines])
if source.consecutive_failures >= 3:
source.enabled = False
await session.commit()
+19
View File
@@ -249,3 +249,22 @@ def test_dol_lagging_index_uses_only_expected_official_archive(trading):
await worker.http.aclose()
await worker.bls.aclose()
asyncio.run(run())
def test_restart_honors_persisted_source_pause(trading, monkeypatch):
from alphapulse.calendar import worker as module
session = AsyncMock()
session.__aenter__.return_value = session
session.scalars.return_value = [SimpleNamespace(code='adp', enabled=True, paused_until=datetime.now(UTC) + timedelta(hours=1))]
session.get.return_value = None
monkeypatch.setattr(module, 'SessionLocal', lambda: session)
async def run():
worker = module.CalendarWorker()
worker.fetch = AsyncMock(side_effect=httpx.ConnectError('offline'))
try:
await worker.schedules()
assert all(call.args[0] != 'adp' for call in worker.fetch.await_args_list)
finally:
await worker.http.aclose()
await worker.bls.aclose()
asyncio.run(run())