Coverage for src/qdrant_loader/webhooks/worker.py: 47%
34 statements
« prev ^ index » next coverage.py v7.15.0, created at 2026-07-20 10:15 +0000
« prev ^ index » next coverage.py v7.15.0, created at 2026-07-20 10:15 +0000
1"""Background worker that processes queued webhook events."""
3from __future__ import annotations
5import asyncio
6import os
8from qdrant_loader.core.worker.job_types import JobType
9from qdrant_loader.utils.logging import LoggingConfig
10from qdrant_loader.webhooks.event_processor import process_change_event
11from qdrant_loader.webhooks.queue_backend import (
12 FULL_SCAN,
13 QueueBackendManager,
14 parse_job_payload,
15)
17logger = LoggingConfig.get_logger(__name__)
19WEBHOOK_WORKER_POLL_SECONDS = float(os.getenv("WEBHOOK_WORKER_POLL_SECONDS", "0.5"))
20WEBHOOK_WORKER_LEASE_SECONDS = int(os.getenv("WEBHOOK_WORKER_LEASE_SECONDS", "120"))
22# Job types handled exclusively by the webhook worker.
23# The ingestion QueueWorkerPool claims BULK_INGEST / INCREMENTAL_PULL; these two
24# sets must remain disjoint so neither worker steals the other's jobs.
25WEBHOOK_JOB_TYPES = [
26 JobType.SINGLE_UPSERT.value,
27 JobType.SINGLE_DELETE.value,
28 FULL_SCAN,
29]
32async def run_webhook_worker(stop_event: asyncio.Event) -> None:
33 """Poll the durable queue and process webhook jobs until stopped."""
34 job_queue = QueueBackendManager.get_job_queue()
35 logger.info(
36 "Webhook worker started",
37 poll_seconds=WEBHOOK_WORKER_POLL_SECONDS,
38 lease_seconds=WEBHOOK_WORKER_LEASE_SECONDS,
39 )
41 while not stop_event.is_set():
42 job = await job_queue.claim_next(
43 lease_seconds=WEBHOOK_WORKER_LEASE_SECONDS,
44 job_types=WEBHOOK_JOB_TYPES,
45 )
46 if job is None:
47 try:
48 await asyncio.wait_for(
49 stop_event.wait(),
50 timeout=WEBHOOK_WORKER_POLL_SECONDS,
51 )
52 except TimeoutError:
53 pass
54 continue
56 try:
57 event = parse_job_payload(job)
58 logger.info(
59 "Processing webhook job",
60 job_id=job.id,
61 operation=event.operation,
62 source=event.source,
63 entity_id=event.entity_id,
64 )
65 await process_change_event(event)
66 await job_queue.mark_done(job.id, claim_attempt=job.attempts)
67 except Exception as exc:
68 logger.exception(
69 "Webhook job failed",
70 job_id=job.id,
71 error=str(exc),
72 )
73 try:
74 await job_queue.mark_failed(
75 job.id,
76 error_message=str(exc),
77 claim_attempt=job.attempts,
78 )
79 except Exception as mark_exc:
80 logger.exception(
81 "Failed to mark webhook job as failed",
82 job_id=job.id,
83 error=str(mark_exc),
84 )
86 logger.info("Webhook worker stopped")