vs-queue 0.1.0__tar.gz → 0.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {vs_queue-0.1.0 → vs_queue-0.1.2}/PKG-INFO +43 -13
- {vs_queue-0.1.0 → vs_queue-0.1.2}/README.md +42 -12
- {vs_queue-0.1.0 → vs_queue-0.1.2}/pyproject.toml +1 -1
- {vs_queue-0.1.0 → vs_queue-0.1.2}/vs_queue/base/vs_base_consumer.py +2 -2
- {vs_queue-0.1.0 → vs_queue-0.1.2}/vs_queue/provider/vs_rabbitmq_queue.py +4 -0
- {vs_queue-0.1.0 → vs_queue-0.1.2}/vs_queue/provider/vs_redis_queue.py +4 -0
- {vs_queue-0.1.0 → vs_queue-0.1.2}/vs_queue.egg-info/PKG-INFO +43 -13
- {vs_queue-0.1.0 → vs_queue-0.1.2}/setup.cfg +0 -0
- {vs_queue-0.1.0 → vs_queue-0.1.2}/vs_queue/__init__.py +0 -0
- {vs_queue-0.1.0 → vs_queue-0.1.2}/vs_queue/base/__init__.py +0 -0
- {vs_queue-0.1.0 → vs_queue-0.1.2}/vs_queue/base/vs_base_queue.py +0 -0
- {vs_queue-0.1.0 → vs_queue-0.1.2}/vs_queue/decorator/__init__.py +0 -0
- {vs_queue-0.1.0 → vs_queue-0.1.2}/vs_queue/decorator/vs_queue_listener.py +0 -0
- {vs_queue-0.1.0 → vs_queue-0.1.2}/vs_queue/factory/__init__.py +0 -0
- {vs_queue-0.1.0 → vs_queue-0.1.2}/vs_queue/factory/vs_queue_factory.py +0 -0
- {vs_queue-0.1.0 → vs_queue-0.1.2}/vs_queue/manager/__init__.py +0 -0
- {vs_queue-0.1.0 → vs_queue-0.1.2}/vs_queue/manager/vs_queue_manager.py +0 -0
- {vs_queue-0.1.0 → vs_queue-0.1.2}/vs_queue/provider/__init__.py +0 -0
- {vs_queue-0.1.0 → vs_queue-0.1.2}/vs_queue/registry/__init__.py +0 -0
- {vs_queue-0.1.0 → vs_queue-0.1.2}/vs_queue/registry/vs_queue_registry.py +0 -0
- {vs_queue-0.1.0 → vs_queue-0.1.2}/vs_queue/retry/__init__.py +0 -0
- {vs_queue-0.1.0 → vs_queue-0.1.2}/vs_queue/retry/vs_retry_policy.py +0 -0
- {vs_queue-0.1.0 → vs_queue-0.1.2}/vs_queue/schema/__init__.py +0 -0
- {vs_queue-0.1.0 → vs_queue-0.1.2}/vs_queue/schema/vs_message.py +0 -0
- {vs_queue-0.1.0 → vs_queue-0.1.2}/vs_queue.egg-info/SOURCES.txt +0 -0
- {vs_queue-0.1.0 → vs_queue-0.1.2}/vs_queue.egg-info/dependency_links.txt +0 -0
- {vs_queue-0.1.0 → vs_queue-0.1.2}/vs_queue.egg-info/requires.txt +0 -0
- {vs_queue-0.1.0 → vs_queue-0.1.2}/vs_queue.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: vs-queue
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: Pluggable async message queue library for Viveka Sutra — Redis Streams, RabbitMQ, retry, dead letter, and annotation-based consumers
|
|
5
5
|
Project-URL: Homepage, https://vivekasutra.com/
|
|
6
6
|
Project-URL: Source, https://github.com/vivekasutra/viveka-mula
|
|
@@ -315,13 +315,13 @@ Abstract base class for all consumers. Extend this and implement `handle` to pro
|
|
|
315
315
|
| Method | Description |
|
|
316
316
|
|---|---|
|
|
317
317
|
| `handle(message)` | Called for every message received. Raise any exception to trigger retry. |
|
|
318
|
-
| `start()` | Called once when the consumer begins. Use for setup. |
|
|
319
|
-
| `stop()` | Called once on graceful shutdown. Use for cleanup. |
|
|
320
318
|
|
|
321
|
-
**Optional override
|
|
319
|
+
**Optional overrides** (concrete no-ops by default — override only if you need them):
|
|
322
320
|
|
|
323
321
|
| Method | Description |
|
|
324
322
|
|---|---|
|
|
323
|
+
| `start()` | Called once when the consumer begins. Use for setup. |
|
|
324
|
+
| `stop()` | Called once on graceful shutdown. Use for cleanup. |
|
|
325
325
|
| `on_error(message, error)` | Called when `handle` raises an exception, before retry logic runs. Use for logging or alerting. |
|
|
326
326
|
|
|
327
327
|
**Example with error hook:**
|
|
@@ -374,9 +374,6 @@ Decorator that registers a `VsBaseConsumer` subclass as a listener. `VsQueueMana
|
|
|
374
374
|
class SimpleConsumer(VsBaseConsumer):
|
|
375
375
|
async def handle(self, message: VsMessage) -> None:
|
|
376
376
|
print(message.payload)
|
|
377
|
-
|
|
378
|
-
async def start(self) -> None: pass
|
|
379
|
-
async def stop(self) -> None: pass
|
|
380
377
|
```
|
|
381
378
|
|
|
382
379
|
**Example — full configuration:**
|
|
@@ -543,9 +540,6 @@ class DlqConsumer(VsBaseConsumer):
|
|
|
543
540
|
f"Message permanently failed | id={message.id} retries={message.retry_count}",
|
|
544
541
|
payload=message.payload,
|
|
545
542
|
)
|
|
546
|
-
|
|
547
|
-
async def start(self) -> None: pass
|
|
548
|
-
async def stop(self) -> None: pass
|
|
549
543
|
```
|
|
550
544
|
|
|
551
545
|
**Example — replaying a DLQ message:**
|
|
@@ -560,6 +554,42 @@ await queue.publish("orchestrator:tasks", dlq_message)
|
|
|
560
554
|
|
|
561
555
|
---
|
|
562
556
|
|
|
557
|
+
## Logging
|
|
558
|
+
|
|
559
|
+
Both backends log consumer lifecycle and per-message activity on the provider logger
|
|
560
|
+
(`vs_queue.provider.vs_redis_queue` / `vs_queue.provider.vs_rabbitmq_queue`). Every line uses a
|
|
561
|
+
`Label | key=value` format.
|
|
562
|
+
|
|
563
|
+
| Level | Line | When |
|
|
564
|
+
|---|---|---|
|
|
565
|
+
| `INFO` | `Subscribed \| queue=<q> consumer=<ClassName>` | `consumer.start()` has run and the queue / consumer group is ready — the loop is now listening |
|
|
566
|
+
| `INFO` | `Message received \| queue=<q> id=<msg_id> retry_count=<n>` | Each message pulled off the queue, before `handle()` is called |
|
|
567
|
+
| `DEBUG` | `Message processed \| queue=<q> id=<msg_id>` | `handle()` returned and the message was acked |
|
|
568
|
+
| `INFO` | `Unsubscribed \| queue=<q> consumer=<ClassName>` | The subscribe loop exited (graceful shutdown or permanent failure) |
|
|
569
|
+
| `ERROR` | `Message failed, no retry policy \| id=<msg_id> error=<e>` | `handle()` raised and no `VsRetryPolicy` was supplied |
|
|
570
|
+
|
|
571
|
+
`VsQueueManager` logs lifecycle events on its own logger (`vs_queue.manager.vs_queue_manager`):
|
|
572
|
+
|
|
573
|
+
| Level | Line |
|
|
574
|
+
|---|---|
|
|
575
|
+
| `INFO` | `Listener registered \| queue=<q> uuid=<uid>` |
|
|
576
|
+
| `INFO` | `Listeners registered \| queue=<q> concurrency=<n>` |
|
|
577
|
+
| `INFO` | `Listener stopped \| queue=<q> uuid=<uid>` |
|
|
578
|
+
| `ERROR` | `Consumer crashed, restarting in <n>s \| queue=<q> attempt=<n> error=<e>` |
|
|
579
|
+
| `CRITICAL` | `Consumer permanently failed after <n> restarts \| queue=<q> error=<e>` |
|
|
580
|
+
|
|
581
|
+
`Subscribed` / `Message received` land only once the loop is actually consuming, so the absence of
|
|
582
|
+
`Subscribed` after `Listener registered` means `subscribe()` is failing before it starts.
|
|
583
|
+
|
|
584
|
+
Turn on the debug lines:
|
|
585
|
+
|
|
586
|
+
```python
|
|
587
|
+
import logging
|
|
588
|
+
logging.getLogger("vs_queue").setLevel(logging.DEBUG)
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
---
|
|
592
|
+
|
|
563
593
|
## Redis Streams Backend
|
|
564
594
|
|
|
565
595
|
`VsRedisQueue` implements the queue using Redis Streams (`XADD` / `XREADGROUP`). Redis Streams provide persistent, ordered, consumer-group-aware message delivery.
|
|
@@ -902,9 +932,9 @@ Abstract base. Extend and implement `handle` to process messages.
|
|
|
902
932
|
| Method | Required | Description |
|
|
903
933
|
|---|---|---|
|
|
904
934
|
| `handle(message)` | Yes | Process a message. Raise to trigger retry. |
|
|
905
|
-
| `start()` |
|
|
906
|
-
| `stop()` |
|
|
907
|
-
| `on_error(message, error)` | No | Called on `handle` failure before retry |
|
|
935
|
+
| `start()` | No — no-op by default | Setup before consuming begins |
|
|
936
|
+
| `stop()` | No — no-op by default | Cleanup on shutdown — called on graceful cancel |
|
|
937
|
+
| `on_error(message, error)` | No — no-op by default | Called on `handle` failure before retry |
|
|
908
938
|
|
|
909
939
|
---
|
|
910
940
|
|
|
@@ -284,13 +284,13 @@ Abstract base class for all consumers. Extend this and implement `handle` to pro
|
|
|
284
284
|
| Method | Description |
|
|
285
285
|
|---|---|
|
|
286
286
|
| `handle(message)` | Called for every message received. Raise any exception to trigger retry. |
|
|
287
|
-
| `start()` | Called once when the consumer begins. Use for setup. |
|
|
288
|
-
| `stop()` | Called once on graceful shutdown. Use for cleanup. |
|
|
289
287
|
|
|
290
|
-
**Optional override
|
|
288
|
+
**Optional overrides** (concrete no-ops by default — override only if you need them):
|
|
291
289
|
|
|
292
290
|
| Method | Description |
|
|
293
291
|
|---|---|
|
|
292
|
+
| `start()` | Called once when the consumer begins. Use for setup. |
|
|
293
|
+
| `stop()` | Called once on graceful shutdown. Use for cleanup. |
|
|
294
294
|
| `on_error(message, error)` | Called when `handle` raises an exception, before retry logic runs. Use for logging or alerting. |
|
|
295
295
|
|
|
296
296
|
**Example with error hook:**
|
|
@@ -343,9 +343,6 @@ Decorator that registers a `VsBaseConsumer` subclass as a listener. `VsQueueMana
|
|
|
343
343
|
class SimpleConsumer(VsBaseConsumer):
|
|
344
344
|
async def handle(self, message: VsMessage) -> None:
|
|
345
345
|
print(message.payload)
|
|
346
|
-
|
|
347
|
-
async def start(self) -> None: pass
|
|
348
|
-
async def stop(self) -> None: pass
|
|
349
346
|
```
|
|
350
347
|
|
|
351
348
|
**Example — full configuration:**
|
|
@@ -512,9 +509,6 @@ class DlqConsumer(VsBaseConsumer):
|
|
|
512
509
|
f"Message permanently failed | id={message.id} retries={message.retry_count}",
|
|
513
510
|
payload=message.payload,
|
|
514
511
|
)
|
|
515
|
-
|
|
516
|
-
async def start(self) -> None: pass
|
|
517
|
-
async def stop(self) -> None: pass
|
|
518
512
|
```
|
|
519
513
|
|
|
520
514
|
**Example — replaying a DLQ message:**
|
|
@@ -529,6 +523,42 @@ await queue.publish("orchestrator:tasks", dlq_message)
|
|
|
529
523
|
|
|
530
524
|
---
|
|
531
525
|
|
|
526
|
+
## Logging
|
|
527
|
+
|
|
528
|
+
Both backends log consumer lifecycle and per-message activity on the provider logger
|
|
529
|
+
(`vs_queue.provider.vs_redis_queue` / `vs_queue.provider.vs_rabbitmq_queue`). Every line uses a
|
|
530
|
+
`Label | key=value` format.
|
|
531
|
+
|
|
532
|
+
| Level | Line | When |
|
|
533
|
+
|---|---|---|
|
|
534
|
+
| `INFO` | `Subscribed \| queue=<q> consumer=<ClassName>` | `consumer.start()` has run and the queue / consumer group is ready — the loop is now listening |
|
|
535
|
+
| `INFO` | `Message received \| queue=<q> id=<msg_id> retry_count=<n>` | Each message pulled off the queue, before `handle()` is called |
|
|
536
|
+
| `DEBUG` | `Message processed \| queue=<q> id=<msg_id>` | `handle()` returned and the message was acked |
|
|
537
|
+
| `INFO` | `Unsubscribed \| queue=<q> consumer=<ClassName>` | The subscribe loop exited (graceful shutdown or permanent failure) |
|
|
538
|
+
| `ERROR` | `Message failed, no retry policy \| id=<msg_id> error=<e>` | `handle()` raised and no `VsRetryPolicy` was supplied |
|
|
539
|
+
|
|
540
|
+
`VsQueueManager` logs lifecycle events on its own logger (`vs_queue.manager.vs_queue_manager`):
|
|
541
|
+
|
|
542
|
+
| Level | Line |
|
|
543
|
+
|---|---|
|
|
544
|
+
| `INFO` | `Listener registered \| queue=<q> uuid=<uid>` |
|
|
545
|
+
| `INFO` | `Listeners registered \| queue=<q> concurrency=<n>` |
|
|
546
|
+
| `INFO` | `Listener stopped \| queue=<q> uuid=<uid>` |
|
|
547
|
+
| `ERROR` | `Consumer crashed, restarting in <n>s \| queue=<q> attempt=<n> error=<e>` |
|
|
548
|
+
| `CRITICAL` | `Consumer permanently failed after <n> restarts \| queue=<q> error=<e>` |
|
|
549
|
+
|
|
550
|
+
`Subscribed` / `Message received` land only once the loop is actually consuming, so the absence of
|
|
551
|
+
`Subscribed` after `Listener registered` means `subscribe()` is failing before it starts.
|
|
552
|
+
|
|
553
|
+
Turn on the debug lines:
|
|
554
|
+
|
|
555
|
+
```python
|
|
556
|
+
import logging
|
|
557
|
+
logging.getLogger("vs_queue").setLevel(logging.DEBUG)
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
---
|
|
561
|
+
|
|
532
562
|
## Redis Streams Backend
|
|
533
563
|
|
|
534
564
|
`VsRedisQueue` implements the queue using Redis Streams (`XADD` / `XREADGROUP`). Redis Streams provide persistent, ordered, consumer-group-aware message delivery.
|
|
@@ -871,9 +901,9 @@ Abstract base. Extend and implement `handle` to process messages.
|
|
|
871
901
|
| Method | Required | Description |
|
|
872
902
|
|---|---|---|
|
|
873
903
|
| `handle(message)` | Yes | Process a message. Raise to trigger retry. |
|
|
874
|
-
| `start()` |
|
|
875
|
-
| `stop()` |
|
|
876
|
-
| `on_error(message, error)` | No | Called on `handle` failure before retry |
|
|
904
|
+
| `start()` | No — no-op by default | Setup before consuming begins |
|
|
905
|
+
| `stop()` | No — no-op by default | Cleanup on shutdown — called on graceful cancel |
|
|
906
|
+
| `on_error(message, error)` | No — no-op by default | Called on `handle` failure before retry |
|
|
877
907
|
|
|
878
908
|
---
|
|
879
909
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "vs-queue"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.2"
|
|
8
8
|
description = "Pluggable async message queue library for Viveka Sutra — Redis Streams, RabbitMQ, retry, dead letter, and annotation-based consumers"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -13,12 +13,12 @@ class VsBaseConsumer(ABC):
|
|
|
13
13
|
async def handle(self, message: VsMessage) -> None:
|
|
14
14
|
pass
|
|
15
15
|
|
|
16
|
-
@abstractmethod
|
|
17
16
|
async def start(self) -> None:
|
|
17
|
+
"""Called once before consuming begins. Override for setup."""
|
|
18
18
|
pass
|
|
19
19
|
|
|
20
|
-
@abstractmethod
|
|
21
20
|
async def stop(self) -> None:
|
|
21
|
+
"""Called once on graceful shutdown. Override for cleanup."""
|
|
22
22
|
pass
|
|
23
23
|
|
|
24
24
|
async def on_error(self, message: VsMessage, error: Exception) -> None:
|
|
@@ -62,6 +62,7 @@ class VsRabbitMQQueue(VsBaseQueue):
|
|
|
62
62
|
async def subscribe(self, queue: str, consumer: VsBaseConsumer, retry_policy: Optional[VsRetryPolicy] = None) -> None:
|
|
63
63
|
q = await self._channel.declare_queue(queue, durable=True)
|
|
64
64
|
await consumer.start()
|
|
65
|
+
_logger.info(f"Subscribed | queue={queue} consumer={type(consumer).__name__}")
|
|
65
66
|
try:
|
|
66
67
|
async with q.iterator() as iterator:
|
|
67
68
|
async for amqp_message in iterator:
|
|
@@ -73,9 +74,11 @@ class VsRabbitMQQueue(VsBaseQueue):
|
|
|
73
74
|
headers=data["headers"],
|
|
74
75
|
payload=data["payload"],
|
|
75
76
|
)
|
|
77
|
+
_logger.info(f"Message received | queue={queue} id={message.id} retry_count={message.retry_count}")
|
|
76
78
|
try:
|
|
77
79
|
await consumer.handle(message)
|
|
78
80
|
await amqp_message.ack()
|
|
81
|
+
_logger.debug(f"Message processed | queue={queue} id={message.id}")
|
|
79
82
|
except Exception as e:
|
|
80
83
|
await consumer.on_error(message, e)
|
|
81
84
|
if retry_policy:
|
|
@@ -89,3 +92,4 @@ class VsRabbitMQQueue(VsBaseQueue):
|
|
|
89
92
|
_logger.error(f"Message failed, no retry policy | id={message.id} error={e}")
|
|
90
93
|
finally:
|
|
91
94
|
await consumer.stop()
|
|
95
|
+
_logger.info(f"Unsubscribed | queue={queue} consumer={type(consumer).__name__}")
|
|
@@ -68,6 +68,7 @@ class VsRedisQueue(VsBaseQueue):
|
|
|
68
68
|
pass
|
|
69
69
|
|
|
70
70
|
await consumer.start()
|
|
71
|
+
_logger.info(f"Subscribed | queue={queue} consumer={type(consumer).__name__}")
|
|
71
72
|
try:
|
|
72
73
|
while True:
|
|
73
74
|
results = await self._client.xreadgroup(
|
|
@@ -89,9 +90,11 @@ class VsRedisQueue(VsBaseQueue):
|
|
|
89
90
|
headers=json.loads(data["headers"]),
|
|
90
91
|
payload=json.loads(data["payload"]),
|
|
91
92
|
)
|
|
93
|
+
_logger.info(f"Message received | queue={queue} id={message.id} retry_count={message.retry_count}")
|
|
92
94
|
try:
|
|
93
95
|
await consumer.handle(message)
|
|
94
96
|
await self._client.xack(queue, group, msg_id)
|
|
97
|
+
_logger.debug(f"Message processed | queue={queue} id={message.id}")
|
|
95
98
|
except Exception as e:
|
|
96
99
|
await consumer.on_error(message, e)
|
|
97
100
|
if retry_policy:
|
|
@@ -102,3 +105,4 @@ class VsRedisQueue(VsBaseQueue):
|
|
|
102
105
|
_logger.error(f"Message failed, no retry policy | id={message.id} error={e}")
|
|
103
106
|
finally:
|
|
104
107
|
await consumer.stop()
|
|
108
|
+
_logger.info(f"Unsubscribed | queue={queue} consumer={type(consumer).__name__}")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: vs-queue
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: Pluggable async message queue library for Viveka Sutra — Redis Streams, RabbitMQ, retry, dead letter, and annotation-based consumers
|
|
5
5
|
Project-URL: Homepage, https://vivekasutra.com/
|
|
6
6
|
Project-URL: Source, https://github.com/vivekasutra/viveka-mula
|
|
@@ -315,13 +315,13 @@ Abstract base class for all consumers. Extend this and implement `handle` to pro
|
|
|
315
315
|
| Method | Description |
|
|
316
316
|
|---|---|
|
|
317
317
|
| `handle(message)` | Called for every message received. Raise any exception to trigger retry. |
|
|
318
|
-
| `start()` | Called once when the consumer begins. Use for setup. |
|
|
319
|
-
| `stop()` | Called once on graceful shutdown. Use for cleanup. |
|
|
320
318
|
|
|
321
|
-
**Optional override
|
|
319
|
+
**Optional overrides** (concrete no-ops by default — override only if you need them):
|
|
322
320
|
|
|
323
321
|
| Method | Description |
|
|
324
322
|
|---|---|
|
|
323
|
+
| `start()` | Called once when the consumer begins. Use for setup. |
|
|
324
|
+
| `stop()` | Called once on graceful shutdown. Use for cleanup. |
|
|
325
325
|
| `on_error(message, error)` | Called when `handle` raises an exception, before retry logic runs. Use for logging or alerting. |
|
|
326
326
|
|
|
327
327
|
**Example with error hook:**
|
|
@@ -374,9 +374,6 @@ Decorator that registers a `VsBaseConsumer` subclass as a listener. `VsQueueMana
|
|
|
374
374
|
class SimpleConsumer(VsBaseConsumer):
|
|
375
375
|
async def handle(self, message: VsMessage) -> None:
|
|
376
376
|
print(message.payload)
|
|
377
|
-
|
|
378
|
-
async def start(self) -> None: pass
|
|
379
|
-
async def stop(self) -> None: pass
|
|
380
377
|
```
|
|
381
378
|
|
|
382
379
|
**Example — full configuration:**
|
|
@@ -543,9 +540,6 @@ class DlqConsumer(VsBaseConsumer):
|
|
|
543
540
|
f"Message permanently failed | id={message.id} retries={message.retry_count}",
|
|
544
541
|
payload=message.payload,
|
|
545
542
|
)
|
|
546
|
-
|
|
547
|
-
async def start(self) -> None: pass
|
|
548
|
-
async def stop(self) -> None: pass
|
|
549
543
|
```
|
|
550
544
|
|
|
551
545
|
**Example — replaying a DLQ message:**
|
|
@@ -560,6 +554,42 @@ await queue.publish("orchestrator:tasks", dlq_message)
|
|
|
560
554
|
|
|
561
555
|
---
|
|
562
556
|
|
|
557
|
+
## Logging
|
|
558
|
+
|
|
559
|
+
Both backends log consumer lifecycle and per-message activity on the provider logger
|
|
560
|
+
(`vs_queue.provider.vs_redis_queue` / `vs_queue.provider.vs_rabbitmq_queue`). Every line uses a
|
|
561
|
+
`Label | key=value` format.
|
|
562
|
+
|
|
563
|
+
| Level | Line | When |
|
|
564
|
+
|---|---|---|
|
|
565
|
+
| `INFO` | `Subscribed \| queue=<q> consumer=<ClassName>` | `consumer.start()` has run and the queue / consumer group is ready — the loop is now listening |
|
|
566
|
+
| `INFO` | `Message received \| queue=<q> id=<msg_id> retry_count=<n>` | Each message pulled off the queue, before `handle()` is called |
|
|
567
|
+
| `DEBUG` | `Message processed \| queue=<q> id=<msg_id>` | `handle()` returned and the message was acked |
|
|
568
|
+
| `INFO` | `Unsubscribed \| queue=<q> consumer=<ClassName>` | The subscribe loop exited (graceful shutdown or permanent failure) |
|
|
569
|
+
| `ERROR` | `Message failed, no retry policy \| id=<msg_id> error=<e>` | `handle()` raised and no `VsRetryPolicy` was supplied |
|
|
570
|
+
|
|
571
|
+
`VsQueueManager` logs lifecycle events on its own logger (`vs_queue.manager.vs_queue_manager`):
|
|
572
|
+
|
|
573
|
+
| Level | Line |
|
|
574
|
+
|---|---|
|
|
575
|
+
| `INFO` | `Listener registered \| queue=<q> uuid=<uid>` |
|
|
576
|
+
| `INFO` | `Listeners registered \| queue=<q> concurrency=<n>` |
|
|
577
|
+
| `INFO` | `Listener stopped \| queue=<q> uuid=<uid>` |
|
|
578
|
+
| `ERROR` | `Consumer crashed, restarting in <n>s \| queue=<q> attempt=<n> error=<e>` |
|
|
579
|
+
| `CRITICAL` | `Consumer permanently failed after <n> restarts \| queue=<q> error=<e>` |
|
|
580
|
+
|
|
581
|
+
`Subscribed` / `Message received` land only once the loop is actually consuming, so the absence of
|
|
582
|
+
`Subscribed` after `Listener registered` means `subscribe()` is failing before it starts.
|
|
583
|
+
|
|
584
|
+
Turn on the debug lines:
|
|
585
|
+
|
|
586
|
+
```python
|
|
587
|
+
import logging
|
|
588
|
+
logging.getLogger("vs_queue").setLevel(logging.DEBUG)
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
---
|
|
592
|
+
|
|
563
593
|
## Redis Streams Backend
|
|
564
594
|
|
|
565
595
|
`VsRedisQueue` implements the queue using Redis Streams (`XADD` / `XREADGROUP`). Redis Streams provide persistent, ordered, consumer-group-aware message delivery.
|
|
@@ -902,9 +932,9 @@ Abstract base. Extend and implement `handle` to process messages.
|
|
|
902
932
|
| Method | Required | Description |
|
|
903
933
|
|---|---|---|
|
|
904
934
|
| `handle(message)` | Yes | Process a message. Raise to trigger retry. |
|
|
905
|
-
| `start()` |
|
|
906
|
-
| `stop()` |
|
|
907
|
-
| `on_error(message, error)` | No | Called on `handle` failure before retry |
|
|
935
|
+
| `start()` | No — no-op by default | Setup before consuming begins |
|
|
936
|
+
| `stop()` | No — no-op by default | Cleanup on shutdown — called on graceful cancel |
|
|
937
|
+
| `on_error(message, error)` | No — no-op by default | Called on `handle` failure before retry |
|
|
908
938
|
|
|
909
939
|
---
|
|
910
940
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|