better lifecycle visibility implementation.

This commit is contained in:
Bryan Van Deusen
2026-01-27 17:33:25 -05:00
parent d2e4d6c588
commit 13b4da5ca0
5 changed files with 132 additions and 24 deletions
+66 -13
View File
@@ -41,11 +41,12 @@ async def _cleanup_engine(engine):
await engine.dispose()
async def _download_source_async(source_id: int) -> dict:
async def _download_source_async(source_id: int, download_id: int = None) -> dict:
"""Async implementation of source download.
Args:
source_id: ID of the source to download
download_id: Optional ID of a pre-created Download record (QUEUED state)
Returns:
Dict with download results
@@ -68,19 +69,42 @@ async def _download_source_async(source_id: int) -> dict:
if not source.enabled:
logger.info(f"Source {source.subscription.name}/{source.platform} is disabled, skipping")
# If there's a queued download record, mark it as skipped
if download_id:
dl_result = await session.execute(
select(Download).where(Download.id == download_id)
)
dl = dl_result.scalar()
if dl:
dl.status = DownloadStatus.SKIPPED
await session.commit()
return {"source_id": source_id, "status": "skipped", "reason": "disabled"}
subscription_name = source.subscription.name
logger.info(f"Starting download for {subscription_name}/{source.platform}")
# Create download record
download = Download(
source_id=source.id,
url=source.url,
status=DownloadStatus.RUNNING,
started_at=utcnow(),
)
session.add(download)
# Use existing download record or create a new one
if download_id:
result = await session.execute(
select(Download).where(Download.id == download_id)
)
download = result.scalar()
if download:
download.status = DownloadStatus.RUNNING
download.started_at = utcnow()
else:
logger.warning(f"Download {download_id} not found, creating new record")
download = None
if not download_id or download is None:
download = Download(
source_id=source.id,
url=source.url,
status=DownloadStatus.RUNNING,
started_at=utcnow(),
)
session.add(download)
await session.commit()
await session.refresh(download)
@@ -261,13 +285,41 @@ async def _scheduled_check_async() -> dict:
logger.info(f"Found {len(due_sources)} sources due for checking")
# Queue download tasks for each source
# Get source IDs that already have a queued or running download
due_source_ids = [s.id for s in due_sources]
active_result = await session.execute(
select(Download.source_id).where(
Download.source_id.in_(due_source_ids),
Download.status.in_([DownloadStatus.QUEUED, DownloadStatus.RUNNING])
)
)
active_source_ids = set(active_result.scalars().all())
# Create download records and queue tasks for each eligible source
queued = 0
skipped = 0
for source in due_sources:
download_source.delay(source.id)
if source.id in active_source_ids:
skipped += 1
logger.debug(f"Skipping {source.subscription.name}/{source.platform} - already active")
continue
download = Download(
source_id=source.id,
url=source.url,
status=DownloadStatus.QUEUED,
)
session.add(download)
await session.commit()
await session.refresh(download)
download_source.delay(source.id, download_id=download.id)
queued += 1
logger.debug(f"Queued download for {source.subscription.name}/{source.platform}")
if skipped:
logger.info(f"Skipped {skipped} sources with active downloads")
return {
"checked": len(sources),
"queued": queued,
@@ -327,14 +379,15 @@ async def _cleanup_old_downloads_async() -> dict:
# Celery tasks that wrap async functions
@celery_app.task(bind=True, max_retries=3)
def download_source(self, source_id: int):
def download_source(self, source_id: int, download_id: int = None):
"""Download new content from a single source.
Args:
source_id: ID of the source to download from
download_id: Optional ID of a pre-created Download record (QUEUED state)
"""
try:
return asyncio.run(_download_source_async(source_id))
return asyncio.run(_download_source_async(source_id, download_id=download_id))
except Exception as e:
logger.exception(f"Task failed for source {source_id}: {e}")
# Retry with exponential backoff