RandomZ / app /ingest /schedule.py
StormShadow308's picture
feat: enhance document upload and processing capabilities with new personalisation features
0136798
Raw
History Blame Contribute Delete
1.67 kB
"""Queue document ingestion with a concurrency limit so large batches do not overwhelm the server.
For batch uploads of thousands of files, tasks queue behind the semaphore and
execute as slots free up — the server stays responsive for status polls and new
requests throughout.
"""
import asyncio
import logging
from pathlib import Path
from app.config import settings
logger = logging.getLogger(__name__)
_background_tasks: set[asyncio.Task] = set() # type: ignore[type-arg]
_sem: asyncio.Semaphore | None = None
def _ingest_semaphore() -> asyncio.Semaphore:
global _sem
if _sem is None:
_sem = asyncio.Semaphore(max(1, settings.max_concurrent_ingests))
return _sem
async def _ingest_worker(doc_id: str, file_path: Path) -> None:
async with _ingest_semaphore():
from app.ingest import pipeline
await pipeline.ingest_document(doc_id=doc_id, file_path=file_path)
def schedule_ingest(doc_id: str, file_path: Path) -> None:
"""Fire-and-forget background ingestion for a committed Document row."""
task = asyncio.create_task(_ingest_worker(doc_id, file_path))
_background_tasks.add(task)
def _done(t: asyncio.Task) -> None: # type: ignore[type-arg]
_background_tasks.discard(t)
try:
t.result()
except Exception:
logger.exception("Background ingest task failed doc_id=%s", doc_id)
task.add_done_callback(_done)
logger.info("Queued ingestion doc=%s path=%s (queue_size=%d)", doc_id, file_path, len(_background_tasks))
def pending_count() -> int:
"""How many ingest tasks are currently queued or running."""
return len(_background_tasks)