redis-message-queue 7.0.1__tar.gz → 8.0.1__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.
- {redis_message_queue-7.0.1 → redis_message_queue-8.0.1}/PKG-INFO +77 -23
- {redis_message_queue-7.0.1 → redis_message_queue-8.0.1}/README.md +76 -22
- {redis_message_queue-7.0.1 → redis_message_queue-8.0.1}/pyproject.toml +1 -1
- {redis_message_queue-7.0.1 → redis_message_queue-8.0.1}/redis_message_queue/_config.py +14 -0
- {redis_message_queue-7.0.1 → redis_message_queue-8.0.1}/redis_message_queue/_redis_gateway.py +157 -25
- {redis_message_queue-7.0.1 → redis_message_queue-8.0.1}/redis_message_queue/asyncio/_redis_gateway.py +140 -24
- {redis_message_queue-7.0.1 → redis_message_queue-8.0.1}/redis_message_queue/asyncio/redis_message_queue.py +64 -34
- {redis_message_queue-7.0.1 → redis_message_queue-8.0.1}/redis_message_queue/redis_message_queue.py +70 -42
- {redis_message_queue-7.0.1 → redis_message_queue-8.0.1}/LICENSE +0 -0
- {redis_message_queue-7.0.1 → redis_message_queue-8.0.1}/redis_message_queue/__init__.py +0 -0
- {redis_message_queue-7.0.1 → redis_message_queue-8.0.1}/redis_message_queue/_abstract_redis_gateway.py +0 -0
- {redis_message_queue-7.0.1 → redis_message_queue-8.0.1}/redis_message_queue/_callable_utils.py +0 -0
- {redis_message_queue-7.0.1 → redis_message_queue-8.0.1}/redis_message_queue/_event.py +0 -0
- {redis_message_queue-7.0.1 → redis_message_queue-8.0.1}/redis_message_queue/_exceptions.py +0 -0
- {redis_message_queue-7.0.1 → redis_message_queue-8.0.1}/redis_message_queue/_queue_key_manager.py +0 -0
- {redis_message_queue-7.0.1 → redis_message_queue-8.0.1}/redis_message_queue/_redis_cluster.py +0 -0
- {redis_message_queue-7.0.1 → redis_message_queue-8.0.1}/redis_message_queue/_stored_message.py +0 -0
- {redis_message_queue-7.0.1 → redis_message_queue-8.0.1}/redis_message_queue/asyncio/__init__.py +0 -0
- {redis_message_queue-7.0.1 → redis_message_queue-8.0.1}/redis_message_queue/asyncio/_abstract_redis_gateway.py +0 -0
- {redis_message_queue-7.0.1 → redis_message_queue-8.0.1}/redis_message_queue/interrupt_handler/__init__.py +0 -0
- {redis_message_queue-7.0.1 → redis_message_queue-8.0.1}/redis_message_queue/interrupt_handler/_implementation.py +0 -0
- {redis_message_queue-7.0.1 → redis_message_queue-8.0.1}/redis_message_queue/interrupt_handler/_interface.py +0 -0
- {redis_message_queue-7.0.1 → redis_message_queue-8.0.1}/redis_message_queue/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: redis-message-queue
|
|
3
|
-
Version:
|
|
3
|
+
Version: 8.0.1
|
|
4
4
|
Summary: Python message queuing with Redis and message deduplication
|
|
5
5
|
License: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -26,7 +26,7 @@ Description-Content-Type: text/markdown
|
|
|
26
26
|
|
|
27
27
|
# redis-message-queue
|
|
28
28
|
|
|
29
|
-
[](https://pypi.org/project/redis-message-queue)
|
|
30
30
|
[](https://pypistats.org/packages/redis-message-queue)
|
|
31
31
|
[](LICENSE)
|
|
32
32
|
[](https://github.com/Elijas/redis-message-queue/issues)
|
|
@@ -37,7 +37,7 @@ Description-Content-Type: text/markdown
|
|
|
37
37
|
**Lightweight Python message queuing with Redis and built-in publish-side deduplication.** Deduplicate publishes within a TTL window, with optional crash recovery — across any number of producers and consumers.
|
|
38
38
|
|
|
39
39
|
```bash
|
|
40
|
-
pip install "redis-message-queue>=
|
|
40
|
+
pip install "redis-message-queue>=8.0.0,<9.0.0"
|
|
41
41
|
```
|
|
42
42
|
|
|
43
43
|
Requires Redis server >= 6.2.
|
|
@@ -52,11 +52,16 @@ from redis import Redis
|
|
|
52
52
|
from redis_message_queue import RedisMessageQueue
|
|
53
53
|
|
|
54
54
|
client = Redis.from_url("redis://localhost:6379/0", decode_responses=True)
|
|
55
|
-
queue = RedisMessageQueue(
|
|
56
|
-
|
|
55
|
+
queue = RedisMessageQueue(
|
|
56
|
+
"quickstart",
|
|
57
|
+
client=client,
|
|
58
|
+
deduplication=True,
|
|
59
|
+
get_deduplication_key=lambda msg: msg["id"],
|
|
60
|
+
)
|
|
61
|
+
queue.publish({"id": "msg-1", "text": "hello"})
|
|
57
62
|
with queue.process_message() as message:
|
|
58
63
|
if message is not None:
|
|
59
|
-
print(f"got {message}")
|
|
64
|
+
print(f"got {message['text']}")
|
|
60
65
|
# Expected output: got hello
|
|
61
66
|
```
|
|
62
67
|
|
|
@@ -72,10 +77,15 @@ from redis_message_queue.asyncio import RedisMessageQueue
|
|
|
72
77
|
|
|
73
78
|
async def main():
|
|
74
79
|
client = Redis.from_url("redis://localhost:6379/0", decode_responses=True)
|
|
75
|
-
queue = RedisMessageQueue(
|
|
76
|
-
|
|
80
|
+
queue = RedisMessageQueue(
|
|
81
|
+
"quickstart",
|
|
82
|
+
client=client,
|
|
83
|
+
deduplication=True,
|
|
84
|
+
get_deduplication_key=lambda msg: msg["id"],
|
|
85
|
+
)
|
|
86
|
+
await queue.publish({"id": "msg-1", "text": "hello"})
|
|
77
87
|
async with queue.process_message() as message:
|
|
78
|
-
print(f"got {message}")
|
|
88
|
+
print(f"got {message['text']}")
|
|
79
89
|
await client.aclose()
|
|
80
90
|
|
|
81
91
|
asyncio.run(main()) # Expected output: got hello
|
|
@@ -89,7 +99,7 @@ asyncio.run(main()) # Expected output: got hello
|
|
|
89
99
|
|
|
90
100
|
| Feature | Details |
|
|
91
101
|
|---------|---------|
|
|
92
|
-
| **Deduplicated publish** | Lua-scripted atomic SET NX + LPUSH prevents duplicate enqueues within a configurable TTL window (default: 1 hour), even with producer retries.
|
|
102
|
+
| **Deduplicated publish** | Lua-scripted atomic SET NX + LPUSH prevents duplicate enqueues within a configurable TTL window (default: 1 hour), even with producer retries. Requires an explicit `get_deduplication_key` callable so your application defines what counts as a duplicate. Note: deduplication is publish-side only and does not prevent duplicate *delivery* under at-least-once visibility-timeout reclaim |
|
|
93
103
|
| **Visibility-timeout redelivery** | Crashed or stalled consumers' messages are reclaimed and redelivered when a visibility timeout is configured |
|
|
94
104
|
| **Success & failure logs** | Optional completed/failed queues for auditing and reprocessing, with configurable max length to prevent unbounded growth |
|
|
95
105
|
| **Dead-letter queue** | Poison messages that exceed a configurable delivery count are automatically routed to a dead-letter queue instead of being redelivered indefinitely |
|
|
@@ -114,10 +124,7 @@ See [Crash recovery with visibility timeout](#crash-recovery-with-visibility-tim
|
|
|
114
124
|
### Deduplication
|
|
115
125
|
|
|
116
126
|
```python
|
|
117
|
-
#
|
|
118
|
-
queue = RedisMessageQueue("q", client=client, deduplication=True)
|
|
119
|
-
|
|
120
|
-
# Custom dedup key (e.g., deduplicate by order ID only)
|
|
127
|
+
# Deduplicate by order ID for a 1-hour TTL
|
|
121
128
|
queue = RedisMessageQueue(
|
|
122
129
|
"q", client=client,
|
|
123
130
|
deduplication=True,
|
|
@@ -128,12 +135,13 @@ queue = RedisMessageQueue(
|
|
|
128
135
|
queue = RedisMessageQueue("q", client=client, deduplication=False)
|
|
129
136
|
```
|
|
130
137
|
|
|
131
|
-
####
|
|
138
|
+
#### Dedup key callable must return a non-empty, high-cardinality, tenant-scoped string
|
|
132
139
|
|
|
133
|
-
When `get_deduplication_key` is
|
|
134
|
-
must return a `str` that uniquely represents the
|
|
135
|
-
message. Returning `None` or `""` raises
|
|
136
|
-
returning a non-`str` value raises
|
|
140
|
+
When `deduplication=True`, `get_deduplication_key` is required. The callable is
|
|
141
|
+
called once per publish and must return a `str` that uniquely represents the
|
|
142
|
+
deduplication scope for that message. Returning `None` or `""` raises
|
|
143
|
+
`ConfigurationError` at publish time; returning a non-`str` value raises
|
|
144
|
+
`TypeError`.
|
|
137
145
|
|
|
138
146
|
Use stable, high-cardinality keys that include any tenant or account boundary
|
|
139
147
|
needed by your system:
|
|
@@ -551,9 +559,9 @@ avoids the parent-construct hazard entirely.
|
|
|
551
559
|
### Redis memory sizing for deduplication and replay metadata
|
|
552
560
|
|
|
553
561
|
When deduplication is enabled, each distinct dedup key creates one Redis string
|
|
554
|
-
for `message_deduplication_log_ttl_seconds` (default: 3600 seconds). The
|
|
555
|
-
|
|
556
|
-
|
|
562
|
+
for `message_deduplication_log_ttl_seconds` (default: 3600 seconds). The dedup
|
|
563
|
+
key is whatever your `get_deduplication_key` callable returns, so choose a
|
|
564
|
+
short, stable logical ID and size Redis for:
|
|
557
565
|
|
|
558
566
|
```text
|
|
559
567
|
peak_unique_publish_rate_per_second
|
|
@@ -758,6 +766,52 @@ For a full analysis, see [docs/production-readiness.md](docs/production-readines
|
|
|
758
766
|
|
|
759
767
|
## Upgrading
|
|
760
768
|
|
|
769
|
+
### v7 to v8 migration
|
|
770
|
+
|
|
771
|
+
v8.0.0 removes implicit dedup key generation. Deduplication is opt-in and
|
|
772
|
+
`deduplication=True` now requires `get_deduplication_key`. If you were relying
|
|
773
|
+
on v7's automatic content hash, provide the equivalent callable explicitly.
|
|
774
|
+
|
|
775
|
+
Before:
|
|
776
|
+
|
|
777
|
+
```python
|
|
778
|
+
queue = RedisMessageQueue(
|
|
779
|
+
"orders",
|
|
780
|
+
client=client,
|
|
781
|
+
deduplication=True,
|
|
782
|
+
)
|
|
783
|
+
```
|
|
784
|
+
|
|
785
|
+
After, prefer a stable logical ID:
|
|
786
|
+
|
|
787
|
+
```python
|
|
788
|
+
queue = RedisMessageQueue(
|
|
789
|
+
"orders",
|
|
790
|
+
client=client,
|
|
791
|
+
deduplication=True,
|
|
792
|
+
get_deduplication_key=lambda msg: msg["order_id"],
|
|
793
|
+
)
|
|
794
|
+
```
|
|
795
|
+
|
|
796
|
+
To preserve v7 content-hash behavior mechanically:
|
|
797
|
+
|
|
798
|
+
```python
|
|
799
|
+
import hashlib
|
|
800
|
+
import json
|
|
801
|
+
|
|
802
|
+
queue = RedisMessageQueue(
|
|
803
|
+
"orders",
|
|
804
|
+
client=client,
|
|
805
|
+
deduplication=True,
|
|
806
|
+
get_deduplication_key=lambda msg: hashlib.sha256(
|
|
807
|
+
json.dumps(msg, sort_keys=True).encode()
|
|
808
|
+
).hexdigest(),
|
|
809
|
+
)
|
|
810
|
+
```
|
|
811
|
+
|
|
812
|
+
If you do not need publish-side deduplication, omit `deduplication` or set
|
|
813
|
+
`deduplication=False`.
|
|
814
|
+
|
|
761
815
|
### v6 to v7 migration
|
|
762
816
|
|
|
763
817
|
v7.0.0 has four breaking changes to check during upgrade.
|
|
@@ -845,7 +899,7 @@ Users on redis-py 7.x and earlier are unaffected. If you installed a redis-py
|
|
|
845
899
|
- **Do not toggle visibility timeout in either direction with messages in processing.** Messages claimed by non-VT consumers have no lease metadata, so VT-enabled consumers cannot reclaim them. Disabling VT later orphans existing lease deadline, lease token, and delivery count metadata and removes crash recovery for those in-flight messages. Drain the processing queue first.
|
|
846
900
|
- **Reducing `max_delivery_count` retroactively DLQs messages.** The delivery count hash persists across restarts. Messages whose accumulated count exceeds the new limit are immediately dead-lettered on next claim.
|
|
847
901
|
- **Changing `max_delivery_count` from a number to `None` leaves delivery metadata behind.** The delivery count hash continues to exist but is no longer consulted. Use this only after draining or after planning manual cleanup of the delivery-count hash.
|
|
848
|
-
- **Changing `get_deduplication_key` changes the dedup keyspace.** Existing dedup records become inert for the duration of their TTL. Drain the queue or clear the old deduplication keys before switching
|
|
902
|
+
- **Changing `get_deduplication_key` changes the dedup keyspace.** Existing dedup records become inert for the duration of their TTL. Drain the queue or clear the old deduplication keys before switching key functions.
|
|
849
903
|
- **Disabling `deduplication` has a retention-window overlap.** Existing dedup records remain in Redis until their TTL expires, but new publishes bypass them. Republishes that would have been suppressed under the old setting can enqueue during that window.
|
|
850
904
|
- **Disabling `enable_failed_queue` stops recording handler failures.** Existing failed entries remain in Redis, but new failures are removed from `processing` without being appended to the failed queue. If `max_delivery_count=None` is also set, repeated handler failures can be dropped with no DLQ or failed-queue record; see [Dead-letter queue](#dead-letter-queue).
|
|
851
905
|
- **Lowering `max_completed_length` or `max_failed_length` trims existing history.** The next completed or failed move calls `LTRIM`, so changing `None` to `N` or lowering `N` can immediately reduce historical entries to the new cap.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# redis-message-queue
|
|
2
2
|
|
|
3
|
-
[](https://pypi.org/project/redis-message-queue)
|
|
4
4
|
[](https://pypistats.org/packages/redis-message-queue)
|
|
5
5
|
[](LICENSE)
|
|
6
6
|
[](https://github.com/Elijas/redis-message-queue/issues)
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
**Lightweight Python message queuing with Redis and built-in publish-side deduplication.** Deduplicate publishes within a TTL window, with optional crash recovery — across any number of producers and consumers.
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
-
pip install "redis-message-queue>=
|
|
14
|
+
pip install "redis-message-queue>=8.0.0,<9.0.0"
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
Requires Redis server >= 6.2.
|
|
@@ -26,11 +26,16 @@ from redis import Redis
|
|
|
26
26
|
from redis_message_queue import RedisMessageQueue
|
|
27
27
|
|
|
28
28
|
client = Redis.from_url("redis://localhost:6379/0", decode_responses=True)
|
|
29
|
-
queue = RedisMessageQueue(
|
|
30
|
-
|
|
29
|
+
queue = RedisMessageQueue(
|
|
30
|
+
"quickstart",
|
|
31
|
+
client=client,
|
|
32
|
+
deduplication=True,
|
|
33
|
+
get_deduplication_key=lambda msg: msg["id"],
|
|
34
|
+
)
|
|
35
|
+
queue.publish({"id": "msg-1", "text": "hello"})
|
|
31
36
|
with queue.process_message() as message:
|
|
32
37
|
if message is not None:
|
|
33
|
-
print(f"got {message}")
|
|
38
|
+
print(f"got {message['text']}")
|
|
34
39
|
# Expected output: got hello
|
|
35
40
|
```
|
|
36
41
|
|
|
@@ -46,10 +51,15 @@ from redis_message_queue.asyncio import RedisMessageQueue
|
|
|
46
51
|
|
|
47
52
|
async def main():
|
|
48
53
|
client = Redis.from_url("redis://localhost:6379/0", decode_responses=True)
|
|
49
|
-
queue = RedisMessageQueue(
|
|
50
|
-
|
|
54
|
+
queue = RedisMessageQueue(
|
|
55
|
+
"quickstart",
|
|
56
|
+
client=client,
|
|
57
|
+
deduplication=True,
|
|
58
|
+
get_deduplication_key=lambda msg: msg["id"],
|
|
59
|
+
)
|
|
60
|
+
await queue.publish({"id": "msg-1", "text": "hello"})
|
|
51
61
|
async with queue.process_message() as message:
|
|
52
|
-
print(f"got {message}")
|
|
62
|
+
print(f"got {message['text']}")
|
|
53
63
|
await client.aclose()
|
|
54
64
|
|
|
55
65
|
asyncio.run(main()) # Expected output: got hello
|
|
@@ -63,7 +73,7 @@ asyncio.run(main()) # Expected output: got hello
|
|
|
63
73
|
|
|
64
74
|
| Feature | Details |
|
|
65
75
|
|---------|---------|
|
|
66
|
-
| **Deduplicated publish** | Lua-scripted atomic SET NX + LPUSH prevents duplicate enqueues within a configurable TTL window (default: 1 hour), even with producer retries.
|
|
76
|
+
| **Deduplicated publish** | Lua-scripted atomic SET NX + LPUSH prevents duplicate enqueues within a configurable TTL window (default: 1 hour), even with producer retries. Requires an explicit `get_deduplication_key` callable so your application defines what counts as a duplicate. Note: deduplication is publish-side only and does not prevent duplicate *delivery* under at-least-once visibility-timeout reclaim |
|
|
67
77
|
| **Visibility-timeout redelivery** | Crashed or stalled consumers' messages are reclaimed and redelivered when a visibility timeout is configured |
|
|
68
78
|
| **Success & failure logs** | Optional completed/failed queues for auditing and reprocessing, with configurable max length to prevent unbounded growth |
|
|
69
79
|
| **Dead-letter queue** | Poison messages that exceed a configurable delivery count are automatically routed to a dead-letter queue instead of being redelivered indefinitely |
|
|
@@ -88,10 +98,7 @@ See [Crash recovery with visibility timeout](#crash-recovery-with-visibility-tim
|
|
|
88
98
|
### Deduplication
|
|
89
99
|
|
|
90
100
|
```python
|
|
91
|
-
#
|
|
92
|
-
queue = RedisMessageQueue("q", client=client, deduplication=True)
|
|
93
|
-
|
|
94
|
-
# Custom dedup key (e.g., deduplicate by order ID only)
|
|
101
|
+
# Deduplicate by order ID for a 1-hour TTL
|
|
95
102
|
queue = RedisMessageQueue(
|
|
96
103
|
"q", client=client,
|
|
97
104
|
deduplication=True,
|
|
@@ -102,12 +109,13 @@ queue = RedisMessageQueue(
|
|
|
102
109
|
queue = RedisMessageQueue("q", client=client, deduplication=False)
|
|
103
110
|
```
|
|
104
111
|
|
|
105
|
-
####
|
|
112
|
+
#### Dedup key callable must return a non-empty, high-cardinality, tenant-scoped string
|
|
106
113
|
|
|
107
|
-
When `get_deduplication_key` is
|
|
108
|
-
must return a `str` that uniquely represents the
|
|
109
|
-
message. Returning `None` or `""` raises
|
|
110
|
-
returning a non-`str` value raises
|
|
114
|
+
When `deduplication=True`, `get_deduplication_key` is required. The callable is
|
|
115
|
+
called once per publish and must return a `str` that uniquely represents the
|
|
116
|
+
deduplication scope for that message. Returning `None` or `""` raises
|
|
117
|
+
`ConfigurationError` at publish time; returning a non-`str` value raises
|
|
118
|
+
`TypeError`.
|
|
111
119
|
|
|
112
120
|
Use stable, high-cardinality keys that include any tenant or account boundary
|
|
113
121
|
needed by your system:
|
|
@@ -525,9 +533,9 @@ avoids the parent-construct hazard entirely.
|
|
|
525
533
|
### Redis memory sizing for deduplication and replay metadata
|
|
526
534
|
|
|
527
535
|
When deduplication is enabled, each distinct dedup key creates one Redis string
|
|
528
|
-
for `message_deduplication_log_ttl_seconds` (default: 3600 seconds). The
|
|
529
|
-
|
|
530
|
-
|
|
536
|
+
for `message_deduplication_log_ttl_seconds` (default: 3600 seconds). The dedup
|
|
537
|
+
key is whatever your `get_deduplication_key` callable returns, so choose a
|
|
538
|
+
short, stable logical ID and size Redis for:
|
|
531
539
|
|
|
532
540
|
```text
|
|
533
541
|
peak_unique_publish_rate_per_second
|
|
@@ -732,6 +740,52 @@ For a full analysis, see [docs/production-readiness.md](docs/production-readines
|
|
|
732
740
|
|
|
733
741
|
## Upgrading
|
|
734
742
|
|
|
743
|
+
### v7 to v8 migration
|
|
744
|
+
|
|
745
|
+
v8.0.0 removes implicit dedup key generation. Deduplication is opt-in and
|
|
746
|
+
`deduplication=True` now requires `get_deduplication_key`. If you were relying
|
|
747
|
+
on v7's automatic content hash, provide the equivalent callable explicitly.
|
|
748
|
+
|
|
749
|
+
Before:
|
|
750
|
+
|
|
751
|
+
```python
|
|
752
|
+
queue = RedisMessageQueue(
|
|
753
|
+
"orders",
|
|
754
|
+
client=client,
|
|
755
|
+
deduplication=True,
|
|
756
|
+
)
|
|
757
|
+
```
|
|
758
|
+
|
|
759
|
+
After, prefer a stable logical ID:
|
|
760
|
+
|
|
761
|
+
```python
|
|
762
|
+
queue = RedisMessageQueue(
|
|
763
|
+
"orders",
|
|
764
|
+
client=client,
|
|
765
|
+
deduplication=True,
|
|
766
|
+
get_deduplication_key=lambda msg: msg["order_id"],
|
|
767
|
+
)
|
|
768
|
+
```
|
|
769
|
+
|
|
770
|
+
To preserve v7 content-hash behavior mechanically:
|
|
771
|
+
|
|
772
|
+
```python
|
|
773
|
+
import hashlib
|
|
774
|
+
import json
|
|
775
|
+
|
|
776
|
+
queue = RedisMessageQueue(
|
|
777
|
+
"orders",
|
|
778
|
+
client=client,
|
|
779
|
+
deduplication=True,
|
|
780
|
+
get_deduplication_key=lambda msg: hashlib.sha256(
|
|
781
|
+
json.dumps(msg, sort_keys=True).encode()
|
|
782
|
+
).hexdigest(),
|
|
783
|
+
)
|
|
784
|
+
```
|
|
785
|
+
|
|
786
|
+
If you do not need publish-side deduplication, omit `deduplication` or set
|
|
787
|
+
`deduplication=False`.
|
|
788
|
+
|
|
735
789
|
### v6 to v7 migration
|
|
736
790
|
|
|
737
791
|
v7.0.0 has four breaking changes to check during upgrade.
|
|
@@ -819,7 +873,7 @@ Users on redis-py 7.x and earlier are unaffected. If you installed a redis-py
|
|
|
819
873
|
- **Do not toggle visibility timeout in either direction with messages in processing.** Messages claimed by non-VT consumers have no lease metadata, so VT-enabled consumers cannot reclaim them. Disabling VT later orphans existing lease deadline, lease token, and delivery count metadata and removes crash recovery for those in-flight messages. Drain the processing queue first.
|
|
820
874
|
- **Reducing `max_delivery_count` retroactively DLQs messages.** The delivery count hash persists across restarts. Messages whose accumulated count exceeds the new limit are immediately dead-lettered on next claim.
|
|
821
875
|
- **Changing `max_delivery_count` from a number to `None` leaves delivery metadata behind.** The delivery count hash continues to exist but is no longer consulted. Use this only after draining or after planning manual cleanup of the delivery-count hash.
|
|
822
|
-
- **Changing `get_deduplication_key` changes the dedup keyspace.** Existing dedup records become inert for the duration of their TTL. Drain the queue or clear the old deduplication keys before switching
|
|
876
|
+
- **Changing `get_deduplication_key` changes the dedup keyspace.** Existing dedup records become inert for the duration of their TTL. Drain the queue or clear the old deduplication keys before switching key functions.
|
|
823
877
|
- **Disabling `deduplication` has a retention-window overlap.** Existing dedup records remain in Redis until their TTL expires, but new publishes bypass them. Republishes that would have been suppressed under the old setting can enqueue during that window.
|
|
824
878
|
- **Disabling `enable_failed_queue` stops recording handler failures.** Existing failed entries remain in Redis, but new failures are removed from `processing` without being appended to the failed queue. If `max_delivery_count=None` is also set, repeated handler failures can be dropped with no DLQ or failed-queue record; see [Dead-letter queue](#dead-letter-queue).
|
|
825
879
|
- **Lowering `max_completed_length` or `max_failed_length` trims existing history.** The next completed or failed move calls `LTRIM`, so changing `None` to `N` or lowering `N` can immediately reduce historical entries to the new cap.
|
|
@@ -32,6 +32,11 @@ DEFAULT_PENDING_OVERLOAD_BLOCK_TIMEOUT_SECONDS = 1.0
|
|
|
32
32
|
INTERRUPTIBLE_RETRY_SLEEP_POLL_SECONDS = 0.05
|
|
33
33
|
PENDING_OVERLOAD_LUA_SENTINEL = -1
|
|
34
34
|
PENDING_OVERLOAD_POLICIES = ("raise", "drop_oldest", "block")
|
|
35
|
+
DEDUPLICATION_REQUIRES_KEY_MESSAGE = (
|
|
36
|
+
"deduplication=True requires get_deduplication_key (callable returning a non-empty str). "
|
|
37
|
+
"Pass a callable like `lambda msg: msg['id']` (recommended: a stable logical ID), "
|
|
38
|
+
"or set deduplication=False."
|
|
39
|
+
)
|
|
35
40
|
|
|
36
41
|
|
|
37
42
|
def is_redis_retryable_exception(exception):
|
|
@@ -301,6 +306,15 @@ def validate_gateway_parameters(
|
|
|
301
306
|
)
|
|
302
307
|
|
|
303
308
|
|
|
309
|
+
def validate_dedup_configuration(
|
|
310
|
+
*,
|
|
311
|
+
deduplication: bool,
|
|
312
|
+
get_deduplication_key: object,
|
|
313
|
+
) -> None:
|
|
314
|
+
if deduplication and get_deduplication_key is None:
|
|
315
|
+
raise ConfigurationError(DEDUPLICATION_REQUIRES_KEY_MESSAGE)
|
|
316
|
+
|
|
317
|
+
|
|
304
318
|
def validate_pending_backpressure_parameters(
|
|
305
319
|
max_pending_length: int | None,
|
|
306
320
|
pending_overload_policy: str,
|