DefStream-Redis 0.1.3__9-py3-none-any.whl

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.
@@ -0,0 +1,46 @@
1
+ from collections.abc import Mapping
2
+ from dataclasses import dataclass
3
+ from typing import Any
4
+
5
+ __all__ = (
6
+ 'Subscription',
7
+ )
8
+
9
+
10
+ @dataclass(frozen=True)
11
+ class Subscription:
12
+ queue: str
13
+ group: str = 'DefStream'
14
+
15
+ def __post_init__(self) -> None:
16
+ for value in (
17
+ self.queue,
18
+ self.group,
19
+ ):
20
+ if not isinstance(
21
+ value,
22
+ str,
23
+ ) or not value:
24
+ raise ValueError('Redis queue and group must be non-empty strings')
25
+
26
+ @classmethod
27
+ def parse(
28
+ cls,
29
+ parameters: Any,
30
+ ) -> 'Subscription':
31
+ if isinstance(
32
+ parameters,
33
+ cls,
34
+ ):
35
+ return parameters
36
+ if isinstance(
37
+ parameters,
38
+ str,
39
+ ):
40
+ return cls(parameters)
41
+ if isinstance(
42
+ parameters,
43
+ Mapping,
44
+ ):
45
+ return cls(**dict(parameters))
46
+ raise TypeError('Redis subscriptions require a queue name, Subscription or mapping')
@@ -0,0 +1,29 @@
1
+ from DefStream.Broker import (
2
+ EventHandler,
3
+ Reject,
4
+ Requeue,
5
+ )
6
+ from .Configuration import (
7
+ Configuration,
8
+ ConnectionType,
9
+ )
10
+ from .Connection import (
11
+ Connection,
12
+ )
13
+ from .Consumer import Consumer
14
+ from .Event import Event
15
+ from .Queue import Queue
16
+ from .Subscription import Subscription
17
+
18
+ __all__ = (
19
+ 'Connection',
20
+ 'Configuration',
21
+ 'ConnectionType',
22
+ 'Consumer',
23
+ 'Event',
24
+ 'EventHandler',
25
+ 'Queue',
26
+ 'Subscription',
27
+ 'Reject',
28
+ 'Requeue',
29
+ )
@@ -0,0 +1,255 @@
1
+ Metadata-Version: 2.4
2
+ Name: DefStream-Redis
3
+ Version: 0.1.3
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.5
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
+ - 공개 버전은 `MAJOR.MINOR.PATCH`이며, Python 소스(`src/**/*.py`) 또는 패키징 설정(`setup.py`, `setup.cfg`, `pyproject.toml`, `MANIFEST.in`) 변경 시 PATCH가 증가합니다.
44
+ - CI·문서·비 Python 리소스만 변경되면 공개 버전을 유지하고 wheel의 빌드 번호만 증가합니다. 예: `defstream_redis-0.1.5-42-py3-none-any.whl`의 패키지 버전은 `0.1.5`입니다.
45
+ - 기존 4자리 버전 또는 post 릴리즈에서 전환할 때는 버전 역행을 막기 위해 PATCH를 한 번 증가시킵니다.
46
+ - master 직접 push도 테스트 후 배포하며, 커밋 목록을 릴리즈 노트로 사용합니다. PR은 기존 `issues/<브랜치>/README.md` 검증을 유지합니다.
47
+ - 새 공개 버전은 wheel과 소스 배포본을 게시합니다. 동일 버전 재빌드는 새 wheel만 추가하고, 기존 PyPI 파일·Git 태그는 변경하지 않으며 GitHub Release에 빌드 기록을 추가합니다.
48
+ - 이미 설치된 동일 버전은 `pip install --upgrade`만으로 새 빌드로 교체되지 않을 수 있습니다. 필요한 경우 새 wheel을 지정해 `--force-reinstall`로 설치하세요.
49
+ - 실행 리소스 변경도 같은 버전으로 재빌드되므로, 소스 배포본에는 이전 리소스가 남습니다. 사용자에게 구분 가능한 업데이트나 새 소스 배포본이 필요하면 공개 버전 증가가 필요합니다.
50
+
51
+ ## 설치
52
+
53
+ Python **3.9 이상**, Redis **6.2 이상**이 필요합니다.
54
+ 이탈한 소비자의 pending 메시지 복구에 `XAUTOCLAIM`을 사용합니다.
55
+
56
+ ```shell
57
+ python -m pip install DefStream-Redis
58
+ ```
59
+
60
+ `DefStream>=0.1.0`과 `redis>=5.1.1`도 함께 설치됩니다.
61
+ 배포 패키지 이름은 `DefStream-Redis`이고 import 경로는
62
+ `DefStream.Broker.Implements.Redis`입니다.
63
+
64
+ ## 메시지 보내고 받기
65
+
66
+ 로컬 Redis가 `127.0.0.1:6379`에서 실행 중이라고 가정합니다.
67
+ 예제는 `defstream.example.redis` 스트림과 `DefStream` 그룹을 만들고 실행 후에도 유지합니다.
68
+
69
+ ```python
70
+ from DefStream.Broker.Implements.Redis import Configuration, Connection
71
+
72
+
73
+ connection = Connection(Configuration(
74
+ host='127.0.0.1',
75
+ port=6379,
76
+ ))
77
+ try:
78
+ connection.connect()
79
+ connection.create_queue(
80
+ 'defstream.example.redis',
81
+ group='DefStream',
82
+ )
83
+ queue = connection.queue(
84
+ 'defstream.example.redis',
85
+ group='DefStream',
86
+ )
87
+ message_id = queue.send({'message': 'Hello, DefStream!'})
88
+ print(message_id)
89
+ event = queue.pop(timeout=1000)
90
+ if event is not None:
91
+ print(event.id, event.body)
92
+ event.ack()
93
+ finally:
94
+ connection.close()
95
+ ```
96
+
97
+ `connect()`는 PING으로 연결을 확인하며 오류를 숨기지 않습니다.
98
+ 기본 코덱은 UTF-8 JSON이며 스트림의 `body` 필드에 메시지 전체를 저장합니다.
99
+ 기존 field-per-value 방식 스트림과는 호환되지 않습니다.
100
+ 사용자 정의 코덱은 `Configuration(encode=..., decode=...)`로 지정합니다.
101
+
102
+ ## 연결 설정
103
+
104
+ | 옵션 | 기본값·의미 |
105
+ | --- | --- |
106
+ | `host`, `port` | `127.0.0.1`, `6379` |
107
+ | `username`, `password` | 인증 정보. 비밀번호만 사용하는 인증도 지원 |
108
+ | `secure` | TLS 사용 여부, 기본값 `False` |
109
+ | `database` | DB 번호, 기본값 0 |
110
+ | `type` | `ConnectionType.Pool` 또는 `ConnectionType.Cluster` |
111
+ | `max` | 최대 연결 수, 기본값 10 |
112
+ | `timeout` | 연결·소켓 제한 시간, **초** 단위, 기본값 5 |
113
+
114
+ `ConnectionType`은 어댑터 패키지에서 가져옵니다.
115
+ 기본 Pool은 redis-py 연결 풀을 사용합니다.
116
+ Cluster는 Redis Cluster를 사용하며 `database=0`이어야 합니다.
117
+ 운영 인증 정보는 환경변수·비밀 저장소에서 읽으세요.
118
+
119
+ ## 소비자 그룹과 이력
120
+
121
+ - `connection.queue(name, group='DefStream')`과 `connection.stream(name, group='DefStream')`은
122
+ 같은 스트림 기반 `Queue`를 반환합니다.
123
+ - `create_queue()`와 `create_stream()`은 스트림과 그룹을 준비합니다.
124
+ 발행·구독 시에도 필요한 리소스를 지연 생성할 수 있습니다.
125
+ - 새 그룹은 `0-0`부터 시작합니다. 구독 전에 발행된 메시지도 그룹에서 소비할 수 있습니다.
126
+ - 같은 그룹의 소비자는 메시지를 나눠 받고, 다른 그룹은 각각 메시지를 받습니다.
127
+ 기존 배포의 처리 위치와 pending 상태를 이어가려면 그룹 이름을 유지하세요.
128
+ - `pop(timeout=1000)`은 그룹 메시지 하나를 기다립니다. 제한 시간은 **밀리초**이며,
129
+ 양수 제한 시간에 메시지가 없으면 `None`을 반환합니다. `None` 또는 `0`은 무기한 대기입니다.
130
+
131
+ 그룹 상태를 바꾸지 않고 스트림 이력을 읽을 수도 있습니다.
132
+ 다음은 위 직접 연결 예제에서 `connect()`와 `close()` 사이에 사용하는 코드입니다.
133
+
134
+ ```python
135
+ for event in connection.stream('defstream.example.redis').read(
136
+ offset=0,
137
+ timeout=1000,
138
+ ):
139
+ print(event.id, event.body)
140
+ ```
141
+
142
+ `read()`는 기존 이력과 새 메시지를 읽으며 제한 시간은 총 밀리초입니다.
143
+ `None` 또는 `0`이면 계속 대기합니다. 읽기를 끝내려면 이터레이터를 닫으세요.
144
+ 이력 이벤트는 그룹 처리용이 아니므로 ACK하거나 재큐잉하지 않습니다.
145
+ 문자열 스트림 ID를 `offset`에 전달하면 해당 ID **이후**부터 읽습니다.
146
+
147
+ ## Worker로 작업 처리하기
148
+
149
+ 다음 실행 파일은 작업 스트림과 그룹을 준비한 뒤 소비합니다.
150
+
151
+ ```python
152
+ from signal import SIGINT, signal
153
+
154
+ from DefStream.Broker.Policies import Reject
155
+ from DefStream.Broker.Implements.Redis import Configuration, Connection, Subscription
156
+ from DefStream.Worker import Run, ThreadPool, Worker, WorkerSetup, declare
157
+
158
+
159
+ class Add(
160
+ Run,
161
+ parameters=Subscription(
162
+ queue='defstream.example.redis.add',
163
+ group='DefStream',
164
+ ),
165
+ on_error=Reject(),
166
+ ):
167
+ def run(
168
+ self,
169
+ a: int,
170
+ b: int,
171
+ ):
172
+ print(a + b)
173
+
174
+
175
+ class Prepare(WorkerSetup):
176
+ def __call__(
177
+ self,
178
+ worker: Worker,
179
+ ) -> None:
180
+ for task in worker.tasks:
181
+ declare(
182
+ task=task,
183
+ broker=worker.broker,
184
+ )
185
+
186
+
187
+ if __name__ == '__main__':
188
+ worker = Worker(
189
+ Connection(Configuration(
190
+ host='127.0.0.1',
191
+ port=6379,
192
+ )),
193
+ ThreadPool(4),
194
+ setups=Prepare(),
195
+ )
196
+ worker.add(Add)
197
+ signal(
198
+ SIGINT,
199
+ lambda *_: worker.stop(),
200
+ )
201
+ worker.run()
202
+ ```
203
+
204
+ 다른 프로세스에서 같은 스트림에 `{'a': 10, 'b': 20}`을 보내면 처리됩니다.
205
+ Worker는 실행과 완료 훅이 끝난 뒤 ACK합니다. 반환값을 RPC 응답으로 보내지는 않습니다.
206
+ `on_error` 생략 시 기본값은 `Requeue()`이며 예제는 `Reject()`를 명시합니다.
207
+ Task API인 `send(body, task=Add, broker=connection)`도 사용할 수 있고,
208
+ 연결은 호출자가 열고 닫아야 합니다.
209
+
210
+ ## ACK, 거부와 재시도
211
+
212
+ | 동작 | 효과 |
213
+ | --- | --- |
214
+ | `ack()` | 그룹에서 XACK. 스트림 이력은 유지 |
215
+ | `reject()` / `Reject()` | 그룹에서 XACK. 기본 DLQ 없음, 이력 유지 |
216
+ | `nack()` / `Requeue()` | 새 메시지를 추가하고 원래 pending 메시지를 ACK |
217
+
218
+ 재큐잉은 단일 키 Lua 스크립트로 원자적으로 처리하지만, 복사본은 새 스트림 ID를 가집니다.
219
+ **새 메시지는 실패한 그룹뿐 아니라 다른 모든 그룹에도 보입니다.**
220
+ 그룹별로 격리된 재시도가 필요하면 별도의 재시도 스트림을 설계하세요.
221
+ 자동 재시도 횟수 제한이나 기본 오류 스트림은 제공하지 않습니다.
222
+
223
+ 직접 소비자를 생성할 때 다음 설정을 사용할 수 있습니다.
224
+
225
+ | 옵션 | 의미 |
226
+ | --- | --- |
227
+ | `connection.consumer(qos=1)` | 모든 구독에 걸친 로컬 미확인 메시지 수 제한 |
228
+ | `reclaim_idle=60000` | 유휴 pending 항목을 재확보하기까지의 밀리초, 기본값 60초 |
229
+ | `reclaim_idle=None` | 자동 pending 복구 비활성화 |
230
+
231
+ 기본 Worker는 기본 소비자 설정을 사용합니다.
232
+ 긴 작업은 처리 중에도 다른 소비자가 재확보할 수 있으므로 작업 시간과 복구 정책을 함께 설계하세요.
233
+ 종료 시 끝나지 않은 작업은 ACK하지 않고, 나중에 복구할 수 있도록 pending 상태로 남깁니다.
234
+
235
+ ## 운영 시 주의사항
236
+
237
+ - 최소 한 번(at-least-once) 전달입니다. 부수 효과가 중복 실행되어도 안전하도록 설계하세요.
238
+ - ACK·거부 후에도 이력이 남고 재시도는 항목을 추가합니다. 보존 정책을 외부에서 관리하세요.
239
+ 처리 중인 pending 항목을 무분별하게 trim하지 마세요.
240
+ - `delete_queue()`와 `delete_stream()`은 스트림 키 자체를 삭제합니다.
241
+ 공유 스트림·그룹을 정리 코드에서 무단 삭제하지 마세요.
242
+ - 연결·디코딩·핸들러 오류는 호출자에게 전파하며 자동 재연결하지 않습니다.
243
+ - Exchange와 RPC는 지원하지 않습니다. `request()`와 콘솔 RPC를 사용할 수 없습니다.
244
+ - Redis Cluster 구성은 지원하지만 현재 통합 테스트 대상은 standalone Redis입니다.
245
+ - Worker의 연결과 풀은 한 번의 실행 전용입니다. `worker.stop()`은 작업을 기다린 뒤 자원을 닫습니다.
246
+
247
+ ## 더 알아보기
248
+
249
+ - [DefStream 사용 설명서](https://github.com/yong5eon/DefStream#readme): 작업, 풀, 훅, 검증, Inspector, 대시보드·콘솔
250
+ - [송수신·이력 예제](./sample/Sample.py)
251
+ - [스트림 오프셋 예제](./sample/Sample.Stream.py)
252
+ - [Redis Worker 예제](https://github.com/yong5eon/DefStream/blob/master/sample/Worker/Redis/Sample.py)
253
+ - [릴리즈와 변경 내역](https://github.com/yong5eon/DefStream-Redis/releases)
254
+
255
+ GNU GPL v3 라이선스를 사용합니다. 자세한 조건은 [LICENSE](./LICENSE)를 확인하세요.
@@ -0,0 +1,13 @@
1
+ DefStream/Broker/Implements/Redis/Configuration.py,sha256=A86E44Jo2z_d45xBHH92atR-PPyySlT7QWgxc3UX1Yc,1551
2
+ DefStream/Broker/Implements/Redis/Connection.py,sha256=NNlEQpGUcjT392lkURixT56qau-tm-BPywNYPGLoA5k,3048
3
+ DefStream/Broker/Implements/Redis/Consumer.py,sha256=z9pkUGLkR9m5JcEtWAYTiHOYI1M8nX61KmSzvtMzErI,4650
4
+ DefStream/Broker/Implements/Redis/Event.py,sha256=AD_RZDV3fn2O_1oXxm2ABhQhtJ59ovpzPnGkOAyv-Ps,1775
5
+ DefStream/Broker/Implements/Redis/Queue.py,sha256=yKI3bu655RU5gM58sHcIOpPHnGGkF-Z_XI5_yCFU52E,2860
6
+ DefStream/Broker/Implements/Redis/Serializer.py,sha256=NWGxpnf5kegR9i_kr8cxQk5JiXO1lMdvTo2sPmghHe0,635
7
+ DefStream/Broker/Implements/Redis/Subscription.py,sha256=I6MZdT8-VSBeiGBHuOmld11xwiQv048YQLfRGS9FzSE,811
8
+ DefStream/Broker/Implements/Redis/__init__.py,sha256=mYLkr6H040-cphMjTFv6j-_75q9qusEYPMY97erfjAQ,449
9
+ defstream_redis-0.1.3.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
10
+ defstream_redis-0.1.3.dist-info/METADATA,sha256=7Y-7dOCXx1nsk2ZMUwT_eP4w1KgtC96OvpPqDz1yBL0,11230
11
+ defstream_redis-0.1.3.dist-info/WHEEL,sha256=ddXGXFAv2JqlFlcNOAe0wHVcM9UwH0UMQqgxVkZav-Y,100
12
+ defstream_redis-0.1.3.dist-info/top_level.txt,sha256=zsnisIu5wHL4b7VM8e9AggG9KTSdUU4morFqpb8YWFs,10
13
+ defstream_redis-0.1.3.dist-info/RECORD,,
@@ -0,0 +1,6 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+ Build: 9
6
+