DefStream-Redis 0.1.2.3__tar.gz → 0.1.2.4__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 (28) hide show
  1. defstream_redis-0.1.2.4/DefStream_Redis.egg-info/PKG-INFO +245 -0
  2. {defstream_redis-0.1.2.3 → defstream_redis-0.1.2.4}/DefStream_Redis.egg-info/SOURCES.txt +1 -0
  3. defstream_redis-0.1.2.4/PKG-INFO +245 -0
  4. defstream_redis-0.1.2.4/README.en.md +217 -0
  5. defstream_redis-0.1.2.4/README.md +214 -0
  6. defstream_redis-0.1.2.3/DefStream_Redis.egg-info/PKG-INFO +0 -191
  7. defstream_redis-0.1.2.3/PKG-INFO +0 -191
  8. defstream_redis-0.1.2.3/README.md +0 -160
  9. {defstream_redis-0.1.2.3 → defstream_redis-0.1.2.4}/.github/workflows/ci.yml +0 -0
  10. {defstream_redis-0.1.2.3 → defstream_redis-0.1.2.4}/.gitignore +0 -0
  11. {defstream_redis-0.1.2.3 → defstream_redis-0.1.2.4}/DefStream_Redis.egg-info/dependency_links.txt +0 -0
  12. {defstream_redis-0.1.2.3 → defstream_redis-0.1.2.4}/DefStream_Redis.egg-info/requires.txt +0 -0
  13. {defstream_redis-0.1.2.3 → defstream_redis-0.1.2.4}/DefStream_Redis.egg-info/top_level.txt +0 -0
  14. {defstream_redis-0.1.2.3 → defstream_redis-0.1.2.4}/LICENSE +0 -0
  15. {defstream_redis-0.1.2.3 → defstream_redis-0.1.2.4}/sample/Sample.Stream.py +0 -0
  16. {defstream_redis-0.1.2.3 → defstream_redis-0.1.2.4}/sample/Sample.py +0 -0
  17. {defstream_redis-0.1.2.3 → defstream_redis-0.1.2.4}/setup.cfg +0 -0
  18. {defstream_redis-0.1.2.3 → defstream_redis-0.1.2.4}/setup.py +0 -0
  19. {defstream_redis-0.1.2.3 → defstream_redis-0.1.2.4}/src/Configuration.py +0 -0
  20. {defstream_redis-0.1.2.3 → defstream_redis-0.1.2.4}/src/Connection.py +0 -0
  21. {defstream_redis-0.1.2.3 → defstream_redis-0.1.2.4}/src/Consumer.py +0 -0
  22. {defstream_redis-0.1.2.3 → defstream_redis-0.1.2.4}/src/Event.py +0 -0
  23. {defstream_redis-0.1.2.3 → defstream_redis-0.1.2.4}/src/Queue.py +0 -0
  24. {defstream_redis-0.1.2.3 → defstream_redis-0.1.2.4}/src/Serializer.py +0 -0
  25. {defstream_redis-0.1.2.3 → defstream_redis-0.1.2.4}/src/Subscription.py +0 -0
  26. {defstream_redis-0.1.2.3 → defstream_redis-0.1.2.4}/src/__init__.py +0 -0
  27. {defstream_redis-0.1.2.3 → defstream_redis-0.1.2.4}/test/test_redis.py +0 -0
  28. {defstream_redis-0.1.2.3 → defstream_redis-0.1.2.4}/test/test_redis_integration.py +0 -0
@@ -0,0 +1,245 @@
1
+ Metadata-Version: 2.4
2
+ Name: DefStream-Redis
3
+ Version: 0.1.2.4
4
+ Summary: Redis Streams broker adapter for DefStream
5
+ Home-page: https://github.com/yong5eon/DefStream-Redis
6
+ Author: Heo Yongseon
7
+ Project-URL: Source, https://github.com/yong5eon/DefStream-Redis
8
+ Project-URL: Issues, https://github.com/yong5eon/DefStream-Redis/issues
9
+ Project-URL: Releases, https://github.com/yong5eon/DefStream-Redis/releases
10
+ Classifier: Programming Language :: Python :: 3.9
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Programming Language :: Python :: 3.14
16
+ Requires-Python: >=3.9
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Requires-Dist: DefStream>=0.1.0
20
+ Requires-Dist: redis>=5.1.1
21
+ Dynamic: author
22
+ Dynamic: classifier
23
+ Dynamic: description
24
+ Dynamic: description-content-type
25
+ Dynamic: home-page
26
+ Dynamic: license-file
27
+ Dynamic: project-url
28
+ Dynamic: requires-dist
29
+ Dynamic: requires-python
30
+ Dynamic: summary
31
+
32
+ # DefStream-Redis
33
+
34
+ **한국어** | [English](./README.en.md)
35
+
36
+ DefStream에서 Redis Streams를 사용할 수 있게 해주는 브로커 어댑터입니다.
37
+ 스트림 기반 큐, 소비자 그룹, 이력 읽기와 메시지 정산을 제공하며,
38
+ DefStream Worker에 연결해 작업을 병렬 처리할 수 있습니다.
39
+ Redis Pub/Sub이나 List 기반 큐가 아니며, redis-py를 사용합니다.
40
+
41
+ ## 설치
42
+
43
+ Python **3.9 이상**, Redis **6.2 이상**이 필요합니다.
44
+ 이탈한 소비자의 pending 메시지 복구에 `XAUTOCLAIM`을 사용합니다.
45
+
46
+ ```shell
47
+ python -m pip install DefStream-Redis
48
+ ```
49
+
50
+ `DefStream>=0.1.0`과 `redis>=5.1.1`도 함께 설치됩니다.
51
+ 배포 패키지 이름은 `DefStream-Redis`이고 import 경로는
52
+ `DefStream.Broker.Implements.Redis`입니다.
53
+
54
+ ## 메시지 보내고 받기
55
+
56
+ 로컬 Redis가 `127.0.0.1:6379`에서 실행 중이라고 가정합니다.
57
+ 예제는 `defstream.example.redis` 스트림과 `DefStream` 그룹을 만들고 실행 후에도 유지합니다.
58
+
59
+ ```python
60
+ from DefStream.Broker.Implements.Redis import Configuration, Connection
61
+
62
+
63
+ connection = Connection(Configuration(
64
+ host='127.0.0.1',
65
+ port=6379,
66
+ ))
67
+ try:
68
+ connection.connect()
69
+ connection.create_queue(
70
+ 'defstream.example.redis',
71
+ group='DefStream',
72
+ )
73
+ queue = connection.queue(
74
+ 'defstream.example.redis',
75
+ group='DefStream',
76
+ )
77
+ message_id = queue.send({'message': 'Hello, DefStream!'})
78
+ print(message_id)
79
+ event = queue.pop(timeout=1000)
80
+ if event is not None:
81
+ print(event.id, event.body)
82
+ event.ack()
83
+ finally:
84
+ connection.close()
85
+ ```
86
+
87
+ `connect()`는 PING으로 연결을 확인하며 오류를 숨기지 않습니다.
88
+ 기본 코덱은 UTF-8 JSON이며 스트림의 `body` 필드에 메시지 전체를 저장합니다.
89
+ 기존 field-per-value 방식 스트림과는 호환되지 않습니다.
90
+ 사용자 정의 코덱은 `Configuration(encode=..., decode=...)`로 지정합니다.
91
+
92
+ ## 연결 설정
93
+
94
+ | 옵션 | 기본값·의미 |
95
+ | --- | --- |
96
+ | `host`, `port` | `127.0.0.1`, `6379` |
97
+ | `username`, `password` | 인증 정보. 비밀번호만 사용하는 인증도 지원 |
98
+ | `secure` | TLS 사용 여부, 기본값 `False` |
99
+ | `database` | DB 번호, 기본값 0 |
100
+ | `type` | `ConnectionType.Pool` 또는 `ConnectionType.Cluster` |
101
+ | `max` | 최대 연결 수, 기본값 10 |
102
+ | `timeout` | 연결·소켓 제한 시간, **초** 단위, 기본값 5 |
103
+
104
+ `ConnectionType`은 어댑터 패키지에서 가져옵니다.
105
+ 기본 Pool은 redis-py 연결 풀을 사용합니다.
106
+ Cluster는 Redis Cluster를 사용하며 `database=0`이어야 합니다.
107
+ 운영 인증 정보는 환경변수·비밀 저장소에서 읽으세요.
108
+
109
+ ## 소비자 그룹과 이력
110
+
111
+ - `connection.queue(name, group='DefStream')`과 `connection.stream(name, group='DefStream')`은
112
+ 같은 스트림 기반 `Queue`를 반환합니다.
113
+ - `create_queue()`와 `create_stream()`은 스트림과 그룹을 준비합니다.
114
+ 발행·구독 시에도 필요한 리소스를 지연 생성할 수 있습니다.
115
+ - 새 그룹은 `0-0`부터 시작합니다. 구독 전에 발행된 메시지도 그룹에서 소비할 수 있습니다.
116
+ - 같은 그룹의 소비자는 메시지를 나눠 받고, 다른 그룹은 각각 메시지를 받습니다.
117
+ 기존 배포의 처리 위치와 pending 상태를 이어가려면 그룹 이름을 유지하세요.
118
+ - `pop(timeout=1000)`은 그룹 메시지 하나를 기다립니다. 제한 시간은 **밀리초**이며,
119
+ 양수 제한 시간에 메시지가 없으면 `None`을 반환합니다. `None` 또는 `0`은 무기한 대기입니다.
120
+
121
+ 그룹 상태를 바꾸지 않고 스트림 이력을 읽을 수도 있습니다.
122
+ 다음은 위 직접 연결 예제에서 `connect()`와 `close()` 사이에 사용하는 코드입니다.
123
+
124
+ ```python
125
+ for event in connection.stream('defstream.example.redis').read(
126
+ offset=0,
127
+ timeout=1000,
128
+ ):
129
+ print(event.id, event.body)
130
+ ```
131
+
132
+ `read()`는 기존 이력과 새 메시지를 읽으며 제한 시간은 총 밀리초입니다.
133
+ `None` 또는 `0`이면 계속 대기합니다. 읽기를 끝내려면 이터레이터를 닫으세요.
134
+ 이력 이벤트는 그룹 처리용이 아니므로 ACK하거나 재큐잉하지 않습니다.
135
+ 문자열 스트림 ID를 `offset`에 전달하면 해당 ID **이후**부터 읽습니다.
136
+
137
+ ## Worker로 작업 처리하기
138
+
139
+ 다음 실행 파일은 작업 스트림과 그룹을 준비한 뒤 소비합니다.
140
+
141
+ ```python
142
+ from signal import SIGINT, signal
143
+
144
+ from DefStream.Broker.Policies import Reject
145
+ from DefStream.Broker.Implements.Redis import Configuration, Connection, Subscription
146
+ from DefStream.Worker import Run, ThreadPool, Worker, WorkerSetup, declare
147
+
148
+
149
+ class Add(
150
+ Run,
151
+ parameters=Subscription(
152
+ queue='defstream.example.redis.add',
153
+ group='DefStream',
154
+ ),
155
+ on_error=Reject(),
156
+ ):
157
+ def run(
158
+ self,
159
+ a: int,
160
+ b: int,
161
+ ):
162
+ print(a + b)
163
+
164
+
165
+ class Prepare(WorkerSetup):
166
+ def __call__(
167
+ self,
168
+ worker: Worker,
169
+ ) -> None:
170
+ for task in worker.tasks:
171
+ declare(
172
+ task=task,
173
+ broker=worker.broker,
174
+ )
175
+
176
+
177
+ if __name__ == '__main__':
178
+ worker = Worker(
179
+ Connection(Configuration(
180
+ host='127.0.0.1',
181
+ port=6379,
182
+ )),
183
+ ThreadPool(4),
184
+ setups=Prepare(),
185
+ )
186
+ worker.add(Add)
187
+ signal(
188
+ SIGINT,
189
+ lambda *_: worker.stop(),
190
+ )
191
+ worker.run()
192
+ ```
193
+
194
+ 다른 프로세스에서 같은 스트림에 `{'a': 10, 'b': 20}`을 보내면 처리됩니다.
195
+ Worker는 실행과 완료 훅이 끝난 뒤 ACK합니다. 반환값을 RPC 응답으로 보내지는 않습니다.
196
+ `on_error` 생략 시 기본값은 `Requeue()`이며 예제는 `Reject()`를 명시합니다.
197
+ Task API인 `send(body, task=Add, broker=connection)`도 사용할 수 있고,
198
+ 연결은 호출자가 열고 닫아야 합니다.
199
+
200
+ ## ACK, 거부와 재시도
201
+
202
+ | 동작 | 효과 |
203
+ | --- | --- |
204
+ | `ack()` | 그룹에서 XACK. 스트림 이력은 유지 |
205
+ | `reject()` / `Reject()` | 그룹에서 XACK. 기본 DLQ 없음, 이력 유지 |
206
+ | `nack()` / `Requeue()` | 새 메시지를 추가하고 원래 pending 메시지를 ACK |
207
+
208
+ 재큐잉은 단일 키 Lua 스크립트로 원자적으로 처리하지만, 복사본은 새 스트림 ID를 가집니다.
209
+ **새 메시지는 실패한 그룹뿐 아니라 다른 모든 그룹에도 보입니다.**
210
+ 그룹별로 격리된 재시도가 필요하면 별도의 재시도 스트림을 설계하세요.
211
+ 자동 재시도 횟수 제한이나 기본 오류 스트림은 제공하지 않습니다.
212
+
213
+ 직접 소비자를 생성할 때 다음 설정을 사용할 수 있습니다.
214
+
215
+ | 옵션 | 의미 |
216
+ | --- | --- |
217
+ | `connection.consumer(qos=1)` | 모든 구독에 걸친 로컬 미확인 메시지 수 제한 |
218
+ | `reclaim_idle=60000` | 유휴 pending 항목을 재확보하기까지의 밀리초, 기본값 60초 |
219
+ | `reclaim_idle=None` | 자동 pending 복구 비활성화 |
220
+
221
+ 기본 Worker는 기본 소비자 설정을 사용합니다.
222
+ 긴 작업은 처리 중에도 다른 소비자가 재확보할 수 있으므로 작업 시간과 복구 정책을 함께 설계하세요.
223
+ 종료 시 끝나지 않은 작업은 ACK하지 않고, 나중에 복구할 수 있도록 pending 상태로 남깁니다.
224
+
225
+ ## 운영 시 주의사항
226
+
227
+ - 최소 한 번(at-least-once) 전달입니다. 부수 효과가 중복 실행되어도 안전하도록 설계하세요.
228
+ - ACK·거부 후에도 이력이 남고 재시도는 항목을 추가합니다. 보존 정책을 외부에서 관리하세요.
229
+ 처리 중인 pending 항목을 무분별하게 trim하지 마세요.
230
+ - `delete_queue()`와 `delete_stream()`은 스트림 키 자체를 삭제합니다.
231
+ 공유 스트림·그룹을 정리 코드에서 무단 삭제하지 마세요.
232
+ - 연결·디코딩·핸들러 오류는 호출자에게 전파하며 자동 재연결하지 않습니다.
233
+ - Exchange와 RPC는 지원하지 않습니다. `request()`와 콘솔 RPC를 사용할 수 없습니다.
234
+ - Redis Cluster 구성은 지원하지만 현재 통합 테스트 대상은 standalone Redis입니다.
235
+ - Worker의 연결과 풀은 한 번의 실행 전용입니다. `worker.stop()`은 작업을 기다린 뒤 자원을 닫습니다.
236
+
237
+ ## 더 알아보기
238
+
239
+ - [DefStream 사용 설명서](https://github.com/yong5eon/DefStream#readme): 작업, 풀, 훅, 검증, Inspector, 대시보드·콘솔
240
+ - [송수신·이력 예제](./sample/Sample.py)
241
+ - [스트림 오프셋 예제](./sample/Sample.Stream.py)
242
+ - [Redis Worker 예제](https://github.com/yong5eon/DefStream/blob/master/sample/Worker/Redis/Sample.py)
243
+ - [릴리즈와 변경 내역](https://github.com/yong5eon/DefStream-Redis/releases)
244
+
245
+ GNU GPL v3 라이선스를 사용합니다. 자세한 조건은 [LICENSE](./LICENSE)를 확인하세요.
@@ -1,5 +1,6 @@
1
1
  .gitignore
2
2
  LICENSE
3
+ README.en.md
3
4
  README.md
4
5
  setup.py
5
6
  .github/workflows/ci.yml
@@ -0,0 +1,245 @@
1
+ Metadata-Version: 2.4
2
+ Name: DefStream-Redis
3
+ Version: 0.1.2.4
4
+ Summary: Redis Streams broker adapter for DefStream
5
+ Home-page: https://github.com/yong5eon/DefStream-Redis
6
+ Author: Heo Yongseon
7
+ Project-URL: Source, https://github.com/yong5eon/DefStream-Redis
8
+ Project-URL: Issues, https://github.com/yong5eon/DefStream-Redis/issues
9
+ Project-URL: Releases, https://github.com/yong5eon/DefStream-Redis/releases
10
+ Classifier: Programming Language :: Python :: 3.9
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Programming Language :: Python :: 3.14
16
+ Requires-Python: >=3.9
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Requires-Dist: DefStream>=0.1.0
20
+ Requires-Dist: redis>=5.1.1
21
+ Dynamic: author
22
+ Dynamic: classifier
23
+ Dynamic: description
24
+ Dynamic: description-content-type
25
+ Dynamic: home-page
26
+ Dynamic: license-file
27
+ Dynamic: project-url
28
+ Dynamic: requires-dist
29
+ Dynamic: requires-python
30
+ Dynamic: summary
31
+
32
+ # DefStream-Redis
33
+
34
+ **한국어** | [English](./README.en.md)
35
+
36
+ DefStream에서 Redis Streams를 사용할 수 있게 해주는 브로커 어댑터입니다.
37
+ 스트림 기반 큐, 소비자 그룹, 이력 읽기와 메시지 정산을 제공하며,
38
+ DefStream Worker에 연결해 작업을 병렬 처리할 수 있습니다.
39
+ Redis Pub/Sub이나 List 기반 큐가 아니며, redis-py를 사용합니다.
40
+
41
+ ## 설치
42
+
43
+ Python **3.9 이상**, Redis **6.2 이상**이 필요합니다.
44
+ 이탈한 소비자의 pending 메시지 복구에 `XAUTOCLAIM`을 사용합니다.
45
+
46
+ ```shell
47
+ python -m pip install DefStream-Redis
48
+ ```
49
+
50
+ `DefStream>=0.1.0`과 `redis>=5.1.1`도 함께 설치됩니다.
51
+ 배포 패키지 이름은 `DefStream-Redis`이고 import 경로는
52
+ `DefStream.Broker.Implements.Redis`입니다.
53
+
54
+ ## 메시지 보내고 받기
55
+
56
+ 로컬 Redis가 `127.0.0.1:6379`에서 실행 중이라고 가정합니다.
57
+ 예제는 `defstream.example.redis` 스트림과 `DefStream` 그룹을 만들고 실행 후에도 유지합니다.
58
+
59
+ ```python
60
+ from DefStream.Broker.Implements.Redis import Configuration, Connection
61
+
62
+
63
+ connection = Connection(Configuration(
64
+ host='127.0.0.1',
65
+ port=6379,
66
+ ))
67
+ try:
68
+ connection.connect()
69
+ connection.create_queue(
70
+ 'defstream.example.redis',
71
+ group='DefStream',
72
+ )
73
+ queue = connection.queue(
74
+ 'defstream.example.redis',
75
+ group='DefStream',
76
+ )
77
+ message_id = queue.send({'message': 'Hello, DefStream!'})
78
+ print(message_id)
79
+ event = queue.pop(timeout=1000)
80
+ if event is not None:
81
+ print(event.id, event.body)
82
+ event.ack()
83
+ finally:
84
+ connection.close()
85
+ ```
86
+
87
+ `connect()`는 PING으로 연결을 확인하며 오류를 숨기지 않습니다.
88
+ 기본 코덱은 UTF-8 JSON이며 스트림의 `body` 필드에 메시지 전체를 저장합니다.
89
+ 기존 field-per-value 방식 스트림과는 호환되지 않습니다.
90
+ 사용자 정의 코덱은 `Configuration(encode=..., decode=...)`로 지정합니다.
91
+
92
+ ## 연결 설정
93
+
94
+ | 옵션 | 기본값·의미 |
95
+ | --- | --- |
96
+ | `host`, `port` | `127.0.0.1`, `6379` |
97
+ | `username`, `password` | 인증 정보. 비밀번호만 사용하는 인증도 지원 |
98
+ | `secure` | TLS 사용 여부, 기본값 `False` |
99
+ | `database` | DB 번호, 기본값 0 |
100
+ | `type` | `ConnectionType.Pool` 또는 `ConnectionType.Cluster` |
101
+ | `max` | 최대 연결 수, 기본값 10 |
102
+ | `timeout` | 연결·소켓 제한 시간, **초** 단위, 기본값 5 |
103
+
104
+ `ConnectionType`은 어댑터 패키지에서 가져옵니다.
105
+ 기본 Pool은 redis-py 연결 풀을 사용합니다.
106
+ Cluster는 Redis Cluster를 사용하며 `database=0`이어야 합니다.
107
+ 운영 인증 정보는 환경변수·비밀 저장소에서 읽으세요.
108
+
109
+ ## 소비자 그룹과 이력
110
+
111
+ - `connection.queue(name, group='DefStream')`과 `connection.stream(name, group='DefStream')`은
112
+ 같은 스트림 기반 `Queue`를 반환합니다.
113
+ - `create_queue()`와 `create_stream()`은 스트림과 그룹을 준비합니다.
114
+ 발행·구독 시에도 필요한 리소스를 지연 생성할 수 있습니다.
115
+ - 새 그룹은 `0-0`부터 시작합니다. 구독 전에 발행된 메시지도 그룹에서 소비할 수 있습니다.
116
+ - 같은 그룹의 소비자는 메시지를 나눠 받고, 다른 그룹은 각각 메시지를 받습니다.
117
+ 기존 배포의 처리 위치와 pending 상태를 이어가려면 그룹 이름을 유지하세요.
118
+ - `pop(timeout=1000)`은 그룹 메시지 하나를 기다립니다. 제한 시간은 **밀리초**이며,
119
+ 양수 제한 시간에 메시지가 없으면 `None`을 반환합니다. `None` 또는 `0`은 무기한 대기입니다.
120
+
121
+ 그룹 상태를 바꾸지 않고 스트림 이력을 읽을 수도 있습니다.
122
+ 다음은 위 직접 연결 예제에서 `connect()`와 `close()` 사이에 사용하는 코드입니다.
123
+
124
+ ```python
125
+ for event in connection.stream('defstream.example.redis').read(
126
+ offset=0,
127
+ timeout=1000,
128
+ ):
129
+ print(event.id, event.body)
130
+ ```
131
+
132
+ `read()`는 기존 이력과 새 메시지를 읽으며 제한 시간은 총 밀리초입니다.
133
+ `None` 또는 `0`이면 계속 대기합니다. 읽기를 끝내려면 이터레이터를 닫으세요.
134
+ 이력 이벤트는 그룹 처리용이 아니므로 ACK하거나 재큐잉하지 않습니다.
135
+ 문자열 스트림 ID를 `offset`에 전달하면 해당 ID **이후**부터 읽습니다.
136
+
137
+ ## Worker로 작업 처리하기
138
+
139
+ 다음 실행 파일은 작업 스트림과 그룹을 준비한 뒤 소비합니다.
140
+
141
+ ```python
142
+ from signal import SIGINT, signal
143
+
144
+ from DefStream.Broker.Policies import Reject
145
+ from DefStream.Broker.Implements.Redis import Configuration, Connection, Subscription
146
+ from DefStream.Worker import Run, ThreadPool, Worker, WorkerSetup, declare
147
+
148
+
149
+ class Add(
150
+ Run,
151
+ parameters=Subscription(
152
+ queue='defstream.example.redis.add',
153
+ group='DefStream',
154
+ ),
155
+ on_error=Reject(),
156
+ ):
157
+ def run(
158
+ self,
159
+ a: int,
160
+ b: int,
161
+ ):
162
+ print(a + b)
163
+
164
+
165
+ class Prepare(WorkerSetup):
166
+ def __call__(
167
+ self,
168
+ worker: Worker,
169
+ ) -> None:
170
+ for task in worker.tasks:
171
+ declare(
172
+ task=task,
173
+ broker=worker.broker,
174
+ )
175
+
176
+
177
+ if __name__ == '__main__':
178
+ worker = Worker(
179
+ Connection(Configuration(
180
+ host='127.0.0.1',
181
+ port=6379,
182
+ )),
183
+ ThreadPool(4),
184
+ setups=Prepare(),
185
+ )
186
+ worker.add(Add)
187
+ signal(
188
+ SIGINT,
189
+ lambda *_: worker.stop(),
190
+ )
191
+ worker.run()
192
+ ```
193
+
194
+ 다른 프로세스에서 같은 스트림에 `{'a': 10, 'b': 20}`을 보내면 처리됩니다.
195
+ Worker는 실행과 완료 훅이 끝난 뒤 ACK합니다. 반환값을 RPC 응답으로 보내지는 않습니다.
196
+ `on_error` 생략 시 기본값은 `Requeue()`이며 예제는 `Reject()`를 명시합니다.
197
+ Task API인 `send(body, task=Add, broker=connection)`도 사용할 수 있고,
198
+ 연결은 호출자가 열고 닫아야 합니다.
199
+
200
+ ## ACK, 거부와 재시도
201
+
202
+ | 동작 | 효과 |
203
+ | --- | --- |
204
+ | `ack()` | 그룹에서 XACK. 스트림 이력은 유지 |
205
+ | `reject()` / `Reject()` | 그룹에서 XACK. 기본 DLQ 없음, 이력 유지 |
206
+ | `nack()` / `Requeue()` | 새 메시지를 추가하고 원래 pending 메시지를 ACK |
207
+
208
+ 재큐잉은 단일 키 Lua 스크립트로 원자적으로 처리하지만, 복사본은 새 스트림 ID를 가집니다.
209
+ **새 메시지는 실패한 그룹뿐 아니라 다른 모든 그룹에도 보입니다.**
210
+ 그룹별로 격리된 재시도가 필요하면 별도의 재시도 스트림을 설계하세요.
211
+ 자동 재시도 횟수 제한이나 기본 오류 스트림은 제공하지 않습니다.
212
+
213
+ 직접 소비자를 생성할 때 다음 설정을 사용할 수 있습니다.
214
+
215
+ | 옵션 | 의미 |
216
+ | --- | --- |
217
+ | `connection.consumer(qos=1)` | 모든 구독에 걸친 로컬 미확인 메시지 수 제한 |
218
+ | `reclaim_idle=60000` | 유휴 pending 항목을 재확보하기까지의 밀리초, 기본값 60초 |
219
+ | `reclaim_idle=None` | 자동 pending 복구 비활성화 |
220
+
221
+ 기본 Worker는 기본 소비자 설정을 사용합니다.
222
+ 긴 작업은 처리 중에도 다른 소비자가 재확보할 수 있으므로 작업 시간과 복구 정책을 함께 설계하세요.
223
+ 종료 시 끝나지 않은 작업은 ACK하지 않고, 나중에 복구할 수 있도록 pending 상태로 남깁니다.
224
+
225
+ ## 운영 시 주의사항
226
+
227
+ - 최소 한 번(at-least-once) 전달입니다. 부수 효과가 중복 실행되어도 안전하도록 설계하세요.
228
+ - ACK·거부 후에도 이력이 남고 재시도는 항목을 추가합니다. 보존 정책을 외부에서 관리하세요.
229
+ 처리 중인 pending 항목을 무분별하게 trim하지 마세요.
230
+ - `delete_queue()`와 `delete_stream()`은 스트림 키 자체를 삭제합니다.
231
+ 공유 스트림·그룹을 정리 코드에서 무단 삭제하지 마세요.
232
+ - 연결·디코딩·핸들러 오류는 호출자에게 전파하며 자동 재연결하지 않습니다.
233
+ - Exchange와 RPC는 지원하지 않습니다. `request()`와 콘솔 RPC를 사용할 수 없습니다.
234
+ - Redis Cluster 구성은 지원하지만 현재 통합 테스트 대상은 standalone Redis입니다.
235
+ - Worker의 연결과 풀은 한 번의 실행 전용입니다. `worker.stop()`은 작업을 기다린 뒤 자원을 닫습니다.
236
+
237
+ ## 더 알아보기
238
+
239
+ - [DefStream 사용 설명서](https://github.com/yong5eon/DefStream#readme): 작업, 풀, 훅, 검증, Inspector, 대시보드·콘솔
240
+ - [송수신·이력 예제](./sample/Sample.py)
241
+ - [스트림 오프셋 예제](./sample/Sample.Stream.py)
242
+ - [Redis Worker 예제](https://github.com/yong5eon/DefStream/blob/master/sample/Worker/Redis/Sample.py)
243
+ - [릴리즈와 변경 내역](https://github.com/yong5eon/DefStream-Redis/releases)
244
+
245
+ GNU GPL v3 라이선스를 사용합니다. 자세한 조건은 [LICENSE](./LICENSE)를 확인하세요.