|
6 | 6 | pip install litestar-saq |
7 | 7 | ``` |
8 | 8 |
|
| 9 | +For OpenTelemetry support: |
| 10 | + |
| 11 | +```shell |
| 12 | +pip install litestar-saq[otel] |
| 13 | +``` |
| 14 | + |
9 | 15 | ## Usage |
10 | 16 |
|
11 | 17 | Here is a basic application that demonstrates how to use the plugin. |
12 | 18 |
|
13 | 19 | ```python |
14 | | -from __future__ import annotations |
15 | | - |
16 | 20 | from litestar import Litestar |
17 | | - |
18 | 21 | from litestar_saq import QueueConfig, SAQConfig, SAQPlugin |
19 | 22 |
|
20 | | -saq = SAQPlugin(config=SAQConfig(use_server_lifespan=True, queue_configs=[QueueConfig(name="samples", dsn="redis://localhost:6379/0")])) |
| 23 | +saq = SAQPlugin( |
| 24 | + config=SAQConfig( |
| 25 | + use_server_lifespan=True, |
| 26 | + queue_configs=[ |
| 27 | + QueueConfig(name="samples", dsn="redis://localhost:6379/0") |
| 28 | + ], |
| 29 | + ) |
| 30 | +) |
21 | 31 | app = Litestar(plugins=[saq]) |
22 | 32 | ``` |
23 | 33 |
|
@@ -47,16 +57,59 @@ If you are starting the process for only specific queues and still want to read |
47 | 57 | import os |
48 | 58 | from saq import Queue |
49 | 59 |
|
50 | | - |
51 | 60 | def get_queue_directly(queue_name: str, redis_url: str) -> Queue: |
52 | 61 | return Queue.from_url(redis_url, name=queue_name) |
53 | 62 |
|
54 | 63 | redis_url = os.getenv("REDIS_URL") |
55 | 64 | queue = get_queue_directly("queue-in-other-process", redis_url) |
| 65 | + |
56 | 66 | # Get queue info |
57 | 67 | info = await queue.info(jobs=True) |
| 68 | + |
58 | 69 | # Enqueue new task |
59 | | -queue.enqueue( |
60 | | - .... |
| 70 | +await queue.enqueue("task_name", arg1="value1") |
| 71 | +``` |
| 72 | + |
| 73 | +## Monitored Jobs |
| 74 | + |
| 75 | +For long-running tasks, use the `monitored_job` decorator to automatically send heartbeats and prevent SAQ from marking jobs as stuck: |
| 76 | + |
| 77 | +```python |
| 78 | +from litestar_saq import monitored_job |
| 79 | + |
| 80 | +@monitored_job() # Auto-calculates interval from job.heartbeat |
| 81 | +async def long_running_task(ctx): |
| 82 | + await process_large_dataset() |
| 83 | + return {"status": "complete"} |
| 84 | + |
| 85 | +@monitored_job(interval=30.0) # Explicit 30-second interval |
| 86 | +async def train_model(ctx, model_id: str): |
| 87 | + for epoch in range(100): |
| 88 | + await train_epoch(model) |
| 89 | + return {"model_id": model_id} |
| 90 | +``` |
| 91 | + |
| 92 | +## OpenTelemetry Integration |
| 93 | + |
| 94 | +litestar-saq supports optional OpenTelemetry instrumentation for distributed tracing. |
| 95 | + |
| 96 | +### Configuration |
| 97 | + |
| 98 | +```python |
| 99 | +from litestar_saq import SAQConfig, QueueConfig |
| 100 | + |
| 101 | +config = SAQConfig( |
| 102 | + queue_configs=[QueueConfig(dsn="redis://localhost:6379/0")], |
| 103 | + enable_otel=None, # Auto-detect (default) - enabled if OTEL installed AND Litestar OpenTelemetryPlugin is active |
| 104 | + # enable_otel=True, # Force enable (raises error if not installed) |
| 105 | + # enable_otel=False, # Force disable |
61 | 106 | ) |
62 | 107 | ``` |
| 108 | + |
| 109 | +When enabled, the plugin creates: |
| 110 | + |
| 111 | +- **CONSUMER spans** for job processing |
| 112 | +- **PRODUCER spans** for job enqueue operations |
| 113 | +- **Automatic context propagation** across process boundaries |
| 114 | + |
| 115 | +Spans follow [OpenTelemetry messaging semantic conventions](https://opentelemetry.io/docs/specs/semconv/messaging/) with `messaging.system = "saq"`. |
0 commit comments