better lifecycle visibility implementation.
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user