|
2 | 2 | import os |
3 | 3 | import threading |
4 | 4 | import time |
5 | | -from queue import Empty, Queue |
| 5 | +from queue import Empty, Full, Queue |
6 | 6 | from typing import Any, List, Optional |
7 | 7 |
|
8 | 8 | import backoff |
|
17 | 17 |
|
18 | 18 | MAX_EVENT_SIZE_BYTES = int(os.environ.get("LANGFUSE_MAX_EVENT_SIZE_BYTES", 1_000_000)) |
19 | 19 | MAX_BATCH_SIZE_BYTES = int(os.environ.get("LANGFUSE_MAX_BATCH_SIZE_BYTES", 2_500_000)) |
| 20 | +_SHUTDOWN_SENTINEL = object() |
20 | 21 |
|
21 | 22 |
|
22 | 23 | class ScoreIngestionMetadata(BaseModel): |
@@ -71,6 +72,10 @@ def _next(self) -> list: |
71 | 72 | block=True, timeout=self._flush_interval - elapsed |
72 | 73 | ) |
73 | 74 |
|
| 75 | + if event is _SHUTDOWN_SENTINEL: |
| 76 | + self._ingestion_queue.task_done() |
| 77 | + break |
| 78 | + |
74 | 79 | # convert pydantic models to dicts |
75 | 80 | if "body" in event and isinstance(event["body"], BaseModel): |
76 | 81 | event["body"] = event["body"].model_dump(exclude_none=True) |
@@ -139,6 +144,12 @@ def upload(self) -> None: |
139 | 144 | def pause(self) -> None: |
140 | 145 | """Pause the consumer.""" |
141 | 146 | self.running = False |
| 147 | + try: |
| 148 | + self._ingestion_queue.put(_SHUTDOWN_SENTINEL, block=False) |
| 149 | + except Full: |
| 150 | + # If the queue is full, the consumer will wake up naturally while |
| 151 | + # draining items, so a dedicated shutdown signal is not required. |
| 152 | + pass |
142 | 153 |
|
143 | 154 | def _upload_batch(self, batch: List[Any]) -> None: |
144 | 155 | logger.debug( |
|
0 commit comments