redqueue 0.13.0__tar.gz → 0.14.0__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.
Files changed (60) hide show
  1. {redqueue-0.13.0 → redqueue-0.14.0}/CHANGELOG.md +57 -22
  2. {redqueue-0.13.0 → redqueue-0.14.0}/PKG-INFO +30 -7
  3. {redqueue-0.13.0 → redqueue-0.14.0}/README-zh-CN.md +63 -43
  4. {redqueue-0.13.0 → redqueue-0.14.0}/README.md +67 -44
  5. {redqueue-0.13.0 → redqueue-0.14.0}/docs/API.md +97 -60
  6. redqueue-0.14.0/docs/superpowers/plans/2026-06-27-message-deduplication.md +357 -0
  7. redqueue-0.14.0/docs/superpowers/specs/2026-06-27-message-deduplication-design.md +170 -0
  8. {redqueue-0.13.0 → redqueue-0.14.0}/pyproject.toml +2 -2
  9. {redqueue-0.13.0 → redqueue-0.14.0}/src/redqueue/__init__.py +8 -7
  10. {redqueue-0.13.0 → redqueue-0.14.0}/src/redqueue/_version.py +1 -1
  11. {redqueue-0.13.0 → redqueue-0.14.0}/src/redqueue/async_client.py +115 -45
  12. {redqueue-0.13.0 → redqueue-0.14.0}/src/redqueue/backends/async_delay.py +372 -372
  13. {redqueue-0.13.0 → redqueue-0.14.0}/src/redqueue/backends/async_list.py +12 -12
  14. {redqueue-0.13.0 → redqueue-0.14.0}/src/redqueue/backends/async_stream.py +601 -601
  15. {redqueue-0.13.0 → redqueue-0.14.0}/src/redqueue/backends/base.py +160 -160
  16. {redqueue-0.13.0 → redqueue-0.14.0}/src/redqueue/backends/delay.py +380 -380
  17. {redqueue-0.13.0 → redqueue-0.14.0}/src/redqueue/backends/list.py +12 -12
  18. {redqueue-0.13.0 → redqueue-0.14.0}/src/redqueue/backends/stream.py +575 -575
  19. {redqueue-0.13.0 → redqueue-0.14.0}/src/redqueue/cli.py +19 -19
  20. {redqueue-0.13.0 → redqueue-0.14.0}/src/redqueue/client.py +106 -43
  21. {redqueue-0.13.0 → redqueue-0.14.0}/src/redqueue/config.py +30 -0
  22. redqueue-0.14.0/src/redqueue/deduplication.py +334 -0
  23. {redqueue-0.13.0 → redqueue-0.14.0}/src/redqueue/message.py +181 -176
  24. {redqueue-0.13.0 → redqueue-0.14.0}/src/redqueue/monitoring.py +187 -186
  25. {redqueue-0.13.0 → redqueue-0.14.0}/tests/fakes.py +68 -26
  26. {redqueue-0.13.0 → redqueue-0.14.0}/tests/test_availability.py +255 -255
  27. {redqueue-0.13.0 → redqueue-0.14.0}/tests/test_cli.py +61 -61
  28. {redqueue-0.13.0 → redqueue-0.14.0}/tests/test_project_skeleton.py +418 -146
  29. {redqueue-0.13.0 → redqueue-0.14.0}/tests/test_real_redis_availability.py +162 -99
  30. {redqueue-0.13.0 → redqueue-0.14.0}/.github/workflows/ci.yml +0 -0
  31. {redqueue-0.13.0 → redqueue-0.14.0}/.gitignore +0 -0
  32. {redqueue-0.13.0 → redqueue-0.14.0}/CODE_OF_CONDUCT.md +0 -0
  33. {redqueue-0.13.0 → redqueue-0.14.0}/CONTRIBUTING.md +0 -0
  34. {redqueue-0.13.0 → redqueue-0.14.0}/LICENSE +0 -0
  35. {redqueue-0.13.0 → redqueue-0.14.0}/NOTICE +0 -0
  36. {redqueue-0.13.0 → redqueue-0.14.0}/docs/RELEASE.md +0 -0
  37. {redqueue-0.13.0 → redqueue-0.14.0}/examples/README.md +0 -0
  38. {redqueue-0.13.0 → redqueue-0.14.0}/examples/__init__.py +0 -0
  39. {redqueue-0.13.0 → redqueue-0.14.0}/examples/async_list_queue.py +0 -0
  40. {redqueue-0.13.0 → redqueue-0.14.0}/examples/common.py +0 -0
  41. {redqueue-0.13.0 → redqueue-0.14.0}/examples/compatibility_check.py +0 -0
  42. {redqueue-0.13.0 → redqueue-0.14.0}/examples/custom_serializer.py +0 -0
  43. {redqueue-0.13.0 → redqueue-0.14.0}/examples/delayed_tasks.py +0 -0
  44. {redqueue-0.13.0 → redqueue-0.14.0}/examples/monitoring_hooks.py +0 -0
  45. {redqueue-0.13.0 → redqueue-0.14.0}/examples/stream_queue.py +0 -0
  46. {redqueue-0.13.0 → redqueue-0.14.0}/examples/sync_list_queue.py +0 -0
  47. {redqueue-0.13.0 → redqueue-0.14.0}/requirements.txt +0 -0
  48. {redqueue-0.13.0 → redqueue-0.14.0}/scripts/check.py +0 -0
  49. {redqueue-0.13.0 → redqueue-0.14.0}/src/redqueue/__main__.py +0 -0
  50. {redqueue-0.13.0 → redqueue-0.14.0}/src/redqueue/backends/__init__.py +0 -0
  51. {redqueue-0.13.0 → redqueue-0.14.0}/src/redqueue/compat.py +0 -0
  52. {redqueue-0.13.0 → redqueue-0.14.0}/src/redqueue/connection.py +0 -0
  53. {redqueue-0.13.0 → redqueue-0.14.0}/src/redqueue/exceptions.py +0 -0
  54. {redqueue-0.13.0 → redqueue-0.14.0}/src/redqueue/serialization.py +0 -0
  55. {redqueue-0.13.0 → redqueue-0.14.0}/tests/README.md +0 -0
  56. {redqueue-0.13.0 → redqueue-0.14.0}/tests/__init__.py +0 -0
  57. {redqueue-0.13.0 → redqueue-0.14.0}/tests/test_backend_contracts.py +0 -0
  58. {redqueue-0.13.0 → redqueue-0.14.0}/tests/test_integration_redis.py +0 -0
  59. {redqueue-0.13.0 → redqueue-0.14.0}/tests/test_performance.py +0 -0
  60. {redqueue-0.13.0 → redqueue-0.14.0}/tests/test_real_redis_performance.py +0 -0
@@ -7,35 +7,70 @@ All notable public release changes are documented here.
7
7
  Development versions are tracked separately from formal release versions.
8
8
  开发版本与正式版本分开管理。
9
9
 
10
- ## [0.13.0] - 2026-06-27
10
+ ## [0.14.0] - 2026-06-27
11
11
 
12
12
  ### Added
13
13
 
14
- - Added first-class `trace_id` support to `Message`, sync and async
15
- `publish()`, sync and async `delay()`, List, Streams, and delayed task
16
- backends.
17
- - Added trace propagation through message envelopes, retry, dead-letter,
18
- delayed release, and requeue flows while remaining compatible with existing
19
- `headers["trace_id"]` messages.
20
- - Added `trace_id` to monitoring events and CLI message output for lifecycle
21
- correlation.
22
- - Added CLI `--trace-id` support for `publish` and `delay`.
23
- - Added `new_trace_id()` helper for callers that want RedQueue-generated trace
24
- identifiers.
14
+ - Added opt-in Redis-backed message deduplication with
15
+ `DeduplicationConfig`.
16
+ - Added `dedup_key` support to sync and async `publish()` and `delay()`.
17
+ - Added duplicate-hit behavior that returns the first message id without
18
+ enqueueing or scheduling another message.
19
+ - Added `message.deduplicated` monitoring events.
25
20
 
26
21
  ### 新增
27
22
 
28
- - `Message`、同步和异步 `publish()`、同步和异步 `delay()`、List、Streams
29
- 和延迟任务后端新增一等 `trace_id` 支持。
30
- - trace 会穿过消息 envelope、重试、死信、延迟释放和重放流程,并兼容已有的
31
- `headers["trace_id"]` 消息。
32
- - 监控事件和 CLI 消息输出新增 `trace_id`,方便按链路聚合生命周期事件。
33
- - CLI 的 `publish` 和 `delay` 命令新增 `--trace-id`。
34
- - 新增 `new_trace_id()` helper,方便调用方生成 RedQueue trace 标识。
23
+ - 新增基于 Redis 的可选消息去重能力,通过 `DeduplicationConfig` 启用。
24
+ - 同步和异步 `publish()`、`delay()` 新增 `dedup_key` 参数。
25
+ - 重复命中时返回第一次消息 ID,不再重复入队或重复调度。
26
+ - 新增 `message.deduplicated` 监控事件。
35
27
 
36
- ## [0.12.0] - 2026-06-23
37
-
38
- ### Added
28
+ ## [0.13.1] - 2026-06-27
29
+
30
+ ### Fixed
31
+
32
+ - Fixed a compatibility regression where legacy messages with blank
33
+ `headers["trace_id"]` values could fail to construct or consume after
34
+ upgrading to `0.13.0`.
35
+ - Fixed sync and async `publish(..., delay=..., message_id=...)` so delayed
36
+ publishing preserves caller-provided message ids.
37
+
38
+ ### 修复
39
+
40
+ - 修复升级到 `0.13.0` 后,历史消息中空白 `headers["trace_id"]` 可能导致
41
+ 消息构造或消费失败的兼容性回归。
42
+ - 修复同步和异步 `publish(..., delay=..., message_id=...)`,现在延迟发布会
43
+ 保留调用方传入的消息 ID。
44
+
45
+ ## [0.13.0] - 2026-06-27
46
+
47
+ ### Added
48
+
49
+ - Added first-class `trace_id` support to `Message`, sync and async
50
+ `publish()`, sync and async `delay()`, List, Streams, and delayed task
51
+ backends.
52
+ - Added trace propagation through message envelopes, retry, dead-letter,
53
+ delayed release, and requeue flows while remaining compatible with existing
54
+ `headers["trace_id"]` messages.
55
+ - Added `trace_id` to monitoring events and CLI message output for lifecycle
56
+ correlation.
57
+ - Added CLI `--trace-id` support for `publish` and `delay`.
58
+ - Added `new_trace_id()` helper for callers that want RedQueue-generated trace
59
+ identifiers.
60
+
61
+ ### 新增
62
+
63
+ - `Message`、同步和异步 `publish()`、同步和异步 `delay()`、List、Streams
64
+ 和延迟任务后端新增一等 `trace_id` 支持。
65
+ - trace 会穿过消息 envelope、重试、死信、延迟释放和重放流程,并兼容已有的
66
+ `headers["trace_id"]` 消息。
67
+ - 监控事件和 CLI 消息输出新增 `trace_id`,方便按链路聚合生命周期事件。
68
+ - CLI 的 `publish` 和 `delay` 命令新增 `--trace-id`。
69
+ - 新增 `new_trace_id()` helper,方便调用方生成 RedQueue trace 标识。
70
+
71
+ ## [0.12.0] - 2026-06-23
72
+
73
+ ### Added
39
74
 
40
75
  - Added the `redqueue` CLI and `python -m redqueue` module entry point for
41
76
  developer debugging.
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: redqueue
3
- Version: 0.13.0
4
- Summary: Redis-backed Python message queue library with List, Streams, delayed tasks, and monitoring.
3
+ Version: 0.14.0
4
+ Summary: Redis-backed Python message queue library with List, Streams, delayed tasks, deduplication, and monitoring.
5
5
  Project-URL: Homepage, https://github.com/SpringMirror-pear/redqueue
6
6
  Project-URL: Repository, https://github.com/SpringMirror-pear/redqueue.git
7
7
  Project-URL: Issues, https://github.com/SpringMirror-pear/redqueue/issues
@@ -52,6 +52,7 @@ https://github.com/SpringMirror-pear/redqueue.git
52
52
  - Redis connection pool managers for shared sync and async resources.
53
53
  - `redqueue` CLI for local debugging and operational checks.
54
54
  - First-class `trace_id` propagation for lifecycle tracing.
55
+ - Opt-in message deduplication with Redis `SET NX` and TTL windows.
55
56
  - Unified exception hierarchy with structured context.
56
57
  - Monitoring events for publish, consume, ack, nack, retry, dead letter, delay,
57
58
  and backend errors.
@@ -75,6 +76,7 @@ Redis:
75
76
  | Streams | `>=5.0` | Uses `XADD`, `XGROUP CREATE`, `XREADGROUP` |
76
77
  | Streams auto claim | `>=6.2` | Uses `XAUTOCLAIM`; Redis 5.x uses `XPENDING`/`XCLAIM` fallback |
77
78
  | Delayed tasks | `>=1.2` | Uses `ZADD` and timestamp scores |
79
+ | Message deduplication | `>=2.6.12` | Uses `SET` with `NX` and `EX`/`PX` |
78
80
 
79
81
  ## Installation
80
82
 
@@ -210,6 +212,23 @@ client.delay({"to": "later@example.com"}, delay_seconds=60, trace_id="trace-123"
210
212
  released = client.schedule_due(limit=100)
211
213
  ```
212
214
 
215
+ Message deduplication:
216
+
217
+ ```python
218
+ from redqueue import DeduplicationConfig, QueueClient
219
+
220
+ client = QueueClient.from_url(
221
+ "redis://127.0.0.1:6379/0",
222
+ queue="emails",
223
+ deduplication=DeduplicationConfig(enabled=True, ttl_seconds=3600),
224
+ )
225
+
226
+ first_id = client.publish({"order": 1}, dedup_key="order-1")
227
+ second_id = client.publish({"order": 1}, dedup_key="order-1")
228
+
229
+ assert second_id == first_id
230
+ ```
231
+
213
232
  Trace IDs:
214
233
 
215
234
  ```python
@@ -367,17 +386,21 @@ REDQUEUE_REDIS_URL=redis://127.0.0.1:6379/0 PYTHONPATH=src python -m pytest -m "
367
386
 
368
387
  Latest local run on Python `3.14.5`:
369
388
 
370
- - Full test suite without `REDQUEUE_REDIS_URL`: `77 passed, 8 skipped`.
371
- - Real Redis availability suite: `3 passed` with
389
+ - Full test suite without `REDQUEUE_REDIS_URL`: `114 passed, 10 skipped`.
390
+ - Real Redis integration suite: `10 passed` with
391
+ `redis://127.0.0.1:6379/0`.
392
+ - Real Redis availability suite: `5 passed` with
372
393
  `redis://127.0.0.1:6379/0`.
373
394
  - Real Redis server: Redis for Windows `5.0.14.1`.
374
395
  - Availability suite: covers List processing recovery, List dead-letter
375
396
  requeue, Streams Redis `<5.0` compatibility rejection, Streams Redis 5.x
376
397
  pending recovery fallback, Streams dead-letter requeue, delayed task publish
377
- failure rollback, missing delayed payload errors, async List recovery, async
378
- Streams dead-letter requeue, and async delayed task rollback.
398
+ failure rollback, missing delayed payload errors, publish-time deduplication,
399
+ delayed task deduplication, async List recovery, async Streams dead-letter
400
+ requeue, and async delayed task rollback.
379
401
  - Real Redis availability suite additionally validates List recovery, Streams
380
- dead-letter requeue, and delayed task rollback against a running Redis server.
402
+ dead-letter requeue, delayed task rollback, List deduplication, and delayed
403
+ task deduplication against a running Redis server.
381
404
 
382
405
  ## Performance Results
383
406
 
@@ -14,10 +14,11 @@ https://github.com/SpringMirror-pear/redqueue.git
14
14
  `BRPOPLPUSH`。
15
15
  - 基于 Redis Streams 的消费组后端,Streams 要求 Redis `>=5.0`。
16
16
  - 基于 Redis Sorted Set 的延迟任务。
17
- - 同步客户端 `QueueClient` 与异步客户端 `AsyncQueueClient`。
17
+ - 同步客户端 `QueueClient` 与异步客户端 `AsyncQueueClient`。
18
18
  - 支持同步和异步 Redis 连接池管理器,方便多个客户端共享连接池。
19
19
  - 提供 `redqueue` CLI,用于本地调试和运行时检查。
20
20
  - 一等 `trace_id` 链路追踪能力,贯穿消息生命周期。
21
+ - 可选消息去重能力,基于 Redis `SET NX` 和 TTL 窗口。
21
22
  - 带结构化上下文的统一异常体系。
22
23
  - 针对发布、消费、确认、拒绝、重试、死信、延迟和后端错误的监控事件。
23
24
  - 通过 `INFO server` 探测 Redis 能力。
@@ -37,9 +38,10 @@ Redis:
37
38
  | --- | --- | --- |
38
39
  | List 阻塞消费 | `>=2.0` | 以 `BLPOP` 系列能力为基础 |
39
40
  | List 可靠搬移 | `>=2.2` | 使用 `BRPOPLPUSH`;Redis `>=6.2` 优先使用 `BLMOVE` |
40
- | Streams | `>=5.0` | 使用 `XADD`、`XGROUP CREATE`、`XREADGROUP` |
41
- | Streams 自动认领 | `>=6.2` | 使用 `XAUTOCLAIM`;Redis 5.x 回退 `XPENDING`/`XCLAIM` |
42
- | 延迟任务 | `>=1.2` | 使用 `ZADD` 和时间戳 score |
41
+ | Streams | `>=5.0` | 使用 `XADD`、`XGROUP CREATE`、`XREADGROUP` |
42
+ | Streams 自动认领 | `>=6.2` | 使用 `XAUTOCLAIM`;Redis 5.x 回退 `XPENDING`/`XCLAIM` |
43
+ | 延迟任务 | `>=1.2` | 使用 `ZADD` 和时间戳 score |
44
+ | 消息去重 | `>=2.6.12` | 使用带 `NX` 和 `EX`/`PX` 的 `SET` |
43
45
 
44
46
  ## 安装
45
47
 
@@ -72,16 +74,16 @@ redqueue stats --url redis://127.0.0.1:6379/0 --queue emails
72
74
 
73
75
  发布和消费消息:
74
76
 
75
- ```bash
76
- redqueue publish --queue emails --payload '{"to":"user@example.com"}'
77
- redqueue consume --queue emails --timeout 1 --ack
78
- ```
79
-
80
- 发布时指定 trace:
81
-
82
- ```bash
83
- redqueue publish --queue emails --payload '{"to":"user@example.com"}' --trace-id trace-123
84
- ```
77
+ ```bash
78
+ redqueue publish --queue emails --payload '{"to":"user@example.com"}'
79
+ redqueue consume --queue emails --timeout 1 --ack
80
+ ```
81
+
82
+ 发布时指定 trace:
83
+
84
+ ```bash
85
+ redqueue publish --queue emails --payload '{"to":"user@example.com"}' --trace-id trace-123
86
+ ```
85
87
 
86
88
  调试延迟任务:
87
89
 
@@ -102,8 +104,8 @@ redqueue dead-letters --queue emails --limit 20
102
104
 
103
105
  同步 List 队列:
104
106
 
105
- ```python
106
- from redqueue import QueueClient, new_trace_id
107
+ ```python
108
+ from redqueue import QueueClient, new_trace_id
107
109
 
108
110
  client = QueueClient.from_url(
109
111
  "redis://127.0.0.1:6379/0",
@@ -111,15 +113,15 @@ client = QueueClient.from_url(
111
113
  backend="list",
112
114
  )
113
115
 
114
- trace_id = new_trace_id()
115
- message_id = client.publish({"to": "user@example.com"}, trace_id=trace_id)
116
- message = client.consume(timeout=1)
117
-
118
- if message is not None:
119
- try:
120
- print(message.trace_id)
121
- print(message.payload)
122
- client.ack(message)
116
+ trace_id = new_trace_id()
117
+ message_id = client.publish({"to": "user@example.com"}, trace_id=trace_id)
118
+ message = client.consume(timeout=1)
119
+
120
+ if message is not None:
121
+ try:
122
+ print(message.trace_id)
123
+ print(message.payload)
124
+ client.ack(message)
123
125
  except Exception:
124
126
  client.retry(message, reason="handler failed")
125
127
  ```
@@ -165,35 +167,52 @@ async def main() -> None:
165
167
  asyncio.run(main())
166
168
  ```
167
169
 
168
- 延迟任务:
170
+ 延迟任务:
169
171
 
170
172
  ```python
171
173
  from redqueue import QueueClient
172
174
 
173
- client = QueueClient.from_url("redis://127.0.0.1:6379/0", queue="emails")
174
- client.delay({"to": "later@example.com"}, delay_seconds=60, trace_id="trace-123")
175
+ client = QueueClient.from_url("redis://127.0.0.1:6379/0", queue="emails")
176
+ client.delay({"to": "later@example.com"}, delay_seconds=60, trace_id="trace-123")
175
177
  released = client.schedule_due(limit=100)
176
178
  ```
177
179
 
178
- 链路追踪:
180
+ 消息去重:
179
181
 
180
182
  ```python
181
- from redqueue import InMemoryMonitoringHook, QueueClient, new_trace_id
183
+ from redqueue import DeduplicationConfig, QueueClient
182
184
 
183
- hook = InMemoryMonitoringHook()
184
185
  client = QueueClient.from_url(
185
186
  "redis://127.0.0.1:6379/0",
186
187
  queue="emails",
187
- monitoring=hook,
188
+ deduplication=DeduplicationConfig(enabled=True, ttl_seconds=3600),
188
189
  )
189
190
 
190
- trace_id = new_trace_id()
191
- client.publish({"to": "user@example.com"}, trace_id=trace_id)
192
- message = client.consume(timeout=1)
191
+ first_id = client.publish({"order": 1}, dedup_key="order-1")
192
+ second_id = client.publish({"order": 1}, dedup_key="order-1")
193
193
 
194
- assert message.trace_id == trace_id
195
- assert hook.events[-1].trace_id == trace_id
194
+ assert second_id == first_id
196
195
  ```
196
+
197
+ 链路追踪:
198
+
199
+ ```python
200
+ from redqueue import InMemoryMonitoringHook, QueueClient, new_trace_id
201
+
202
+ hook = InMemoryMonitoringHook()
203
+ client = QueueClient.from_url(
204
+ "redis://127.0.0.1:6379/0",
205
+ queue="emails",
206
+ monitoring=hook,
207
+ )
208
+
209
+ trace_id = new_trace_id()
210
+ client.publish({"to": "user@example.com"}, trace_id=trace_id)
211
+ message = client.consume(timeout=1)
212
+
213
+ assert message.trace_id == trace_id
214
+ assert hook.events[-1].trace_id == trace_id
215
+ ```
197
216
 
198
217
  连接池管理:
199
218
 
@@ -329,15 +348,16 @@ REDQUEUE_REDIS_URL=redis://127.0.0.1:6379/0 PYTHONPATH=src python -m pytest -m "
329
348
 
330
349
  Python `3.14.5` 最新本地运行结果:
331
350
 
332
- - 未设置 `REDQUEUE_REDIS_URL` 时的完整测试套件:`77 passed, 8 skipped`。
333
- - 真实 Redis 可用性套件:使用 `redis://127.0.0.1:6379/0` 时 `3 passed`。
351
+ - 未设置 `REDQUEUE_REDIS_URL` 时的完整测试套件:`114 passed, 10 skipped`。
352
+ - 真实 Redis 集成套件:使用 `redis://127.0.0.1:6379/0` 时 `10 passed`。
353
+ - 真实 Redis 可用性套件:使用 `redis://127.0.0.1:6379/0` 时 `5 passed`。
334
354
  - 真实 Redis 服务端:Redis for Windows `5.0.14.1`。
335
355
  - 可用性套件覆盖:List processing 恢复、List 死信重放、Streams Redis
336
356
  `<5.0` 兼容性拒绝、Streams Redis 5.x pending 恢复回退、Streams 死信
337
- 重放、延迟任务发布失败回滚、延迟 payload 缺失错误、异步 List 恢复、异步
338
- Streams 死信重放和异步延迟任务回滚。
339
- - 真实 Redis 可用性套件额外验证了运行中 Redis 服务上的 List 恢复、Streams
340
- 死信重放和延迟任务回滚。
357
+ 重放、延迟任务发布失败回滚、延迟 payload 缺失错误、发布时去重、延迟任务
358
+ 去重、异步 List 恢复、异步 Streams 死信重放和异步延迟任务回滚。
359
+ - 真实 Redis 可用性套件额外验证了运行中 Redis 服务上的 List 恢复、Streams
360
+ 死信重放、延迟任务回滚、List 去重和延迟任务去重。
341
361
 
342
362
  ## 性能测试结果
343
363
 
@@ -15,10 +15,11 @@ https://github.com/SpringMirror-pear/redqueue.git
15
15
  fallback on older compatible Redis versions.
16
16
  - Redis Streams backend with consumer groups. Streams require Redis `>=5.0`.
17
17
  - Delayed tasks based on Redis Sorted Set.
18
- - Sync client `QueueClient` and async client `AsyncQueueClient`.
18
+ - Sync client `QueueClient` and async client `AsyncQueueClient`.
19
19
  - Redis connection pool managers for shared sync and async resources.
20
20
  - `redqueue` CLI for local debugging and operational checks.
21
21
  - First-class `trace_id` propagation for lifecycle tracing.
22
+ - Opt-in message deduplication with Redis `SET NX` and TTL windows.
22
23
  - Unified exception hierarchy with structured context.
23
24
  - Monitoring events for publish, consume, ack, nack, retry, dead letter, delay,
24
25
  and backend errors.
@@ -39,9 +40,10 @@ Redis:
39
40
  | --- | --- | --- |
40
41
  | List blocking consume | `>=2.0` | Uses `BLPOP` family compatibility baseline |
41
42
  | List reliable move | `>=2.2` | Uses `BRPOPLPUSH`; `BLMOVE` preferred on `>=6.2` |
42
- | Streams | `>=5.0` | Uses `XADD`, `XGROUP CREATE`, `XREADGROUP` |
43
- | Streams auto claim | `>=6.2` | Uses `XAUTOCLAIM`; Redis 5.x uses `XPENDING`/`XCLAIM` fallback |
44
- | Delayed tasks | `>=1.2` | Uses `ZADD` and timestamp scores |
43
+ | Streams | `>=5.0` | Uses `XADD`, `XGROUP CREATE`, `XREADGROUP` |
44
+ | Streams auto claim | `>=6.2` | Uses `XAUTOCLAIM`; Redis 5.x uses `XPENDING`/`XCLAIM` fallback |
45
+ | Delayed tasks | `>=1.2` | Uses `ZADD` and timestamp scores |
46
+ | Message deduplication | `>=2.6.12` | Uses `SET` with `NX` and `EX`/`PX` |
45
47
 
46
48
  ## Installation
47
49
 
@@ -74,16 +76,16 @@ redqueue stats --url redis://127.0.0.1:6379/0 --queue emails
74
76
 
75
77
  Publish and consume messages:
76
78
 
77
- ```bash
78
- redqueue publish --queue emails --payload '{"to":"user@example.com"}'
79
- redqueue consume --queue emails --timeout 1 --ack
80
- ```
81
-
82
- Publish with trace correlation:
83
-
84
- ```bash
85
- redqueue publish --queue emails --payload '{"to":"user@example.com"}' --trace-id trace-123
86
- ```
79
+ ```bash
80
+ redqueue publish --queue emails --payload '{"to":"user@example.com"}'
81
+ redqueue consume --queue emails --timeout 1 --ack
82
+ ```
83
+
84
+ Publish with trace correlation:
85
+
86
+ ```bash
87
+ redqueue publish --queue emails --payload '{"to":"user@example.com"}' --trace-id trace-123
88
+ ```
87
89
 
88
90
  Delayed task debugging:
89
91
 
@@ -104,8 +106,8 @@ All command output is JSON so it can be piped into scripts or log processors.
104
106
 
105
107
  Synchronous List queue:
106
108
 
107
- ```python
108
- from redqueue import QueueClient, new_trace_id
109
+ ```python
110
+ from redqueue import QueueClient, new_trace_id
109
111
 
110
112
  client = QueueClient.from_url(
111
113
  "redis://127.0.0.1:6379/0",
@@ -113,15 +115,15 @@ client = QueueClient.from_url(
113
115
  backend="list",
114
116
  )
115
117
 
116
- trace_id = new_trace_id()
117
- message_id = client.publish({"to": "user@example.com"}, trace_id=trace_id)
118
- message = client.consume(timeout=1)
119
-
120
- if message is not None:
121
- try:
122
- print(message.trace_id)
123
- print(message.payload)
124
- client.ack(message)
118
+ trace_id = new_trace_id()
119
+ message_id = client.publish({"to": "user@example.com"}, trace_id=trace_id)
120
+ message = client.consume(timeout=1)
121
+
122
+ if message is not None:
123
+ try:
124
+ print(message.trace_id)
125
+ print(message.payload)
126
+ client.ack(message)
125
127
  except Exception:
126
128
  client.retry(message, reason="handler failed")
127
129
  ```
@@ -167,35 +169,52 @@ async def main() -> None:
167
169
  asyncio.run(main())
168
170
  ```
169
171
 
170
- Delayed task:
172
+ Delayed task:
171
173
 
172
174
  ```python
173
175
  from redqueue import QueueClient
174
176
 
175
- client = QueueClient.from_url("redis://127.0.0.1:6379/0", queue="emails")
176
- client.delay({"to": "later@example.com"}, delay_seconds=60, trace_id="trace-123")
177
+ client = QueueClient.from_url("redis://127.0.0.1:6379/0", queue="emails")
178
+ client.delay({"to": "later@example.com"}, delay_seconds=60, trace_id="trace-123")
177
179
  released = client.schedule_due(limit=100)
178
180
  ```
179
181
 
180
- Trace IDs:
182
+ Message deduplication:
181
183
 
182
184
  ```python
183
- from redqueue import InMemoryMonitoringHook, QueueClient, new_trace_id
185
+ from redqueue import DeduplicationConfig, QueueClient
184
186
 
185
- hook = InMemoryMonitoringHook()
186
187
  client = QueueClient.from_url(
187
188
  "redis://127.0.0.1:6379/0",
188
189
  queue="emails",
189
- monitoring=hook,
190
+ deduplication=DeduplicationConfig(enabled=True, ttl_seconds=3600),
190
191
  )
191
192
 
192
- trace_id = new_trace_id()
193
- client.publish({"to": "user@example.com"}, trace_id=trace_id)
194
- message = client.consume(timeout=1)
193
+ first_id = client.publish({"order": 1}, dedup_key="order-1")
194
+ second_id = client.publish({"order": 1}, dedup_key="order-1")
195
195
 
196
- assert message.trace_id == trace_id
197
- assert hook.events[-1].trace_id == trace_id
196
+ assert second_id == first_id
198
197
  ```
198
+
199
+ Trace IDs:
200
+
201
+ ```python
202
+ from redqueue import InMemoryMonitoringHook, QueueClient, new_trace_id
203
+
204
+ hook = InMemoryMonitoringHook()
205
+ client = QueueClient.from_url(
206
+ "redis://127.0.0.1:6379/0",
207
+ queue="emails",
208
+ monitoring=hook,
209
+ )
210
+
211
+ trace_id = new_trace_id()
212
+ client.publish({"to": "user@example.com"}, trace_id=trace_id)
213
+ message = client.consume(timeout=1)
214
+
215
+ assert message.trace_id == trace_id
216
+ assert hook.events[-1].trace_id == trace_id
217
+ ```
199
218
 
200
219
  Connection pool management:
201
220
 
@@ -334,17 +353,21 @@ REDQUEUE_REDIS_URL=redis://127.0.0.1:6379/0 PYTHONPATH=src python -m pytest -m "
334
353
 
335
354
  Latest local run on Python `3.14.5`:
336
355
 
337
- - Full test suite without `REDQUEUE_REDIS_URL`: `77 passed, 8 skipped`.
338
- - Real Redis availability suite: `3 passed` with
339
- `redis://127.0.0.1:6379/0`.
356
+ - Full test suite without `REDQUEUE_REDIS_URL`: `114 passed, 10 skipped`.
357
+ - Real Redis integration suite: `10 passed` with
358
+ `redis://127.0.0.1:6379/0`.
359
+ - Real Redis availability suite: `5 passed` with
360
+ `redis://127.0.0.1:6379/0`.
340
361
  - Real Redis server: Redis for Windows `5.0.14.1`.
341
362
  - Availability suite: covers List processing recovery, List dead-letter
342
363
  requeue, Streams Redis `<5.0` compatibility rejection, Streams Redis 5.x
343
364
  pending recovery fallback, Streams dead-letter requeue, delayed task publish
344
- failure rollback, missing delayed payload errors, async List recovery, async
345
- Streams dead-letter requeue, and async delayed task rollback.
346
- - Real Redis availability suite additionally validates List recovery, Streams
347
- dead-letter requeue, and delayed task rollback against a running Redis server.
365
+ failure rollback, missing delayed payload errors, publish-time deduplication,
366
+ delayed task deduplication, async List recovery, async Streams dead-letter
367
+ requeue, and async delayed task rollback.
368
+ - Real Redis availability suite additionally validates List recovery, Streams
369
+ dead-letter requeue, delayed task rollback, List deduplication, and delayed
370
+ task deduplication against a running Redis server.
348
371
 
349
372
  ## Performance Results
350
373