fix: address final review findings for sync-scheduler-web plan

Close the leaked httpx.AsyncClient in MyWhoosh sync runs, ensure hard
failures finish sync_runs as FAILED instead of leaving them stuck at
RUNNING, log (non-benign) exceptions surfaced by sync_all_enabled during
scheduled ticks, classify GarminImportRejected as a non-retryable
per-activity failure, fix a bug where a live Garmin-class action_required
state could be silently cleared by a run that did no Garmin work, use the
activity's DB primary key instead of the unsanitized remote id for
filesystem paths, add regression/coverage tests for the health-state fix
and MFA code threading through the real SyncManager, add idempotency
coverage to the two-user acceptance test, and note the Dockerfile's
single-process assumption.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Bastian Wagner
2026-08-15 17:09:55 +02:00
parent aed9d6bb48
commit 990a55af14
5 changed files with 457 additions and 195 deletions

View File

@@ -2,6 +2,8 @@ import asyncio
import logging
from datetime import datetime, timedelta, timezone
from app.sync.manager import SyncAlreadyRunning
logger = logging.getLogger(__name__)
@@ -17,7 +19,20 @@ class SyncScheduler:
async def run_once(self) -> None:
self.last_tick = datetime.now(timezone.utc)
try:
await self.manager.sync_all_enabled()
results = await self.manager.sync_all_enabled()
for result in results:
if not isinstance(result, Exception):
continue
# SyncAlreadyRunning is an expected, benign outcome when a
# manual sync and a scheduler tick overlap for the same user
# -- not an error worth logging.
if isinstance(result, SyncAlreadyRunning):
continue
logger.warning(
"sync_all_enabled: user sync failed during scheduled tick: %s: %s",
type(result).__name__,
str(result)[:200],
)
except Exception:
logger.exception("sync_all_enabled failed during scheduled tick")
finally: