DefStream-RabbitMQ 0.1.1.3__tar.gz → 0.1.2.6__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.
Potentially problematic release.
This version of DefStream-RabbitMQ might be problematic. Click here for more details.
- {defstream_rabbitmq-0.1.1.3 → defstream_rabbitmq-0.1.2.6}/.github/workflows/ci.yml +111 -5
- {defstream_rabbitmq-0.1.1.3 → defstream_rabbitmq-0.1.2.6/DefStream_RabbitMQ.egg-info}/PKG-INFO +43 -1
- {defstream_rabbitmq-0.1.1.3 → defstream_rabbitmq-0.1.2.6}/DefStream_RabbitMQ.egg-info/SOURCES.txt +6 -0
- {defstream_rabbitmq-0.1.1.3/DefStream_RabbitMQ.egg-info → defstream_rabbitmq-0.1.2.6}/PKG-INFO +43 -1
- {defstream_rabbitmq-0.1.1.3 → defstream_rabbitmq-0.1.2.6}/README.en.md +47 -0
- {defstream_rabbitmq-0.1.1.3 → defstream_rabbitmq-0.1.2.6}/README.md +42 -0
- defstream_rabbitmq-0.1.2.6/issues/feature/1/IMPLEMENTS.md +93 -0
- defstream_rabbitmq-0.1.2.6/issues/feature/1/README.md +103 -0
- defstream_rabbitmq-0.1.2.6/issues/feature/1/REQUIREMENTS.md +152 -0
- defstream_rabbitmq-0.1.2.6/issues/feature/1/TASK.md +44 -0
- defstream_rabbitmq-0.1.2.6/issues/feature/1/ci.refs.json +3 -0
- {defstream_rabbitmq-0.1.1.3 → defstream_rabbitmq-0.1.2.6}/src/Configuration.py +1 -1
- {defstream_rabbitmq-0.1.1.3 → defstream_rabbitmq-0.1.2.6}/src/Connection.py +42 -10
- {defstream_rabbitmq-0.1.1.3 → defstream_rabbitmq-0.1.2.6}/src/Consumer.py +3 -5
- {defstream_rabbitmq-0.1.1.3 → defstream_rabbitmq-0.1.2.6}/src/Event.py +1 -1
- {defstream_rabbitmq-0.1.1.3 → defstream_rabbitmq-0.1.2.6}/src/Exchange.py +8 -3
- {defstream_rabbitmq-0.1.1.3 → defstream_rabbitmq-0.1.2.6}/src/Policies.py +1 -1
- {defstream_rabbitmq-0.1.1.3 → defstream_rabbitmq-0.1.2.6}/src/Queue.py +15 -3
- {defstream_rabbitmq-0.1.1.3 → defstream_rabbitmq-0.1.2.6}/src/Stream.py +1 -1
- {defstream_rabbitmq-0.1.1.3 → defstream_rabbitmq-0.1.2.6}/src/Subscription.py +27 -0
- defstream_rabbitmq-0.1.2.6/test/test_priorities.py +442 -0
- {defstream_rabbitmq-0.1.1.3 → defstream_rabbitmq-0.1.2.6}/.gitignore +0 -0
- {defstream_rabbitmq-0.1.1.3 → defstream_rabbitmq-0.1.2.6}/DefStream_RabbitMQ.egg-info/dependency_links.txt +0 -0
- {defstream_rabbitmq-0.1.1.3 → defstream_rabbitmq-0.1.2.6}/DefStream_RabbitMQ.egg-info/requires.txt +0 -0
- {defstream_rabbitmq-0.1.1.3 → defstream_rabbitmq-0.1.2.6}/DefStream_RabbitMQ.egg-info/top_level.txt +0 -0
- {defstream_rabbitmq-0.1.1.3 → defstream_rabbitmq-0.1.2.6}/LICENSE +0 -0
- {defstream_rabbitmq-0.1.1.3 → defstream_rabbitmq-0.1.2.6}/sample/Sample.RPC.py +0 -0
- {defstream_rabbitmq-0.1.1.3 → defstream_rabbitmq-0.1.2.6}/sample/Sample.Stream.py +0 -0
- {defstream_rabbitmq-0.1.1.3 → defstream_rabbitmq-0.1.2.6}/sample/Sample.py +0 -0
- {defstream_rabbitmq-0.1.1.3 → defstream_rabbitmq-0.1.2.6}/setup.cfg +0 -0
- {defstream_rabbitmq-0.1.1.3 → defstream_rabbitmq-0.1.2.6}/setup.py +0 -0
- {defstream_rabbitmq-0.1.1.3 → defstream_rabbitmq-0.1.2.6}/src/Serializer.py +0 -0
- {defstream_rabbitmq-0.1.1.3 → defstream_rabbitmq-0.1.2.6}/src/__init__.py +0 -0
- {defstream_rabbitmq-0.1.1.3 → defstream_rabbitmq-0.1.2.6}/test/test_broker_methods.py +0 -0
- {defstream_rabbitmq-0.1.1.3 → defstream_rabbitmq-0.1.2.6}/test/test_rabbitmq_integration.py +0 -0
- {defstream_rabbitmq-0.1.1.3 → defstream_rabbitmq-0.1.2.6}/test/test_rabbitmq_worker.py +0 -0
- {defstream_rabbitmq-0.1.1.3 → defstream_rabbitmq-0.1.2.6}/test/test_rpc.py +0 -0
- {defstream_rabbitmq-0.1.1.3 → defstream_rabbitmq-0.1.2.6}/test/test_rpc_integration.py +0 -0
|
@@ -19,11 +19,17 @@ concurrency:
|
|
|
19
19
|
jobs:
|
|
20
20
|
changes:
|
|
21
21
|
name: Plan release version
|
|
22
|
+
permissions:
|
|
23
|
+
contents: read
|
|
24
|
+
pull-requests: read
|
|
22
25
|
runs-on: ubuntu-latest
|
|
23
26
|
timeout-minutes: 5
|
|
24
27
|
outputs:
|
|
25
28
|
release: ${{ steps.changes.outputs.release }}
|
|
26
29
|
version: ${{ steps.changes.outputs.version }}
|
|
30
|
+
core_ref: ${{ steps.refs.outputs.core_ref }}
|
|
31
|
+
rabbitmq_ref: ${{ steps.refs.outputs.rabbitmq_ref }}
|
|
32
|
+
redis_ref: ${{ steps.refs.outputs.redis_ref }}
|
|
27
33
|
steps:
|
|
28
34
|
- uses: actions/checkout@v7
|
|
29
35
|
with:
|
|
@@ -33,6 +39,96 @@ jobs:
|
|
|
33
39
|
uses: actions/setup-python@v7
|
|
34
40
|
with:
|
|
35
41
|
python-version: '3.13'
|
|
42
|
+
- name: Validate PR branch changelog
|
|
43
|
+
id: changelog
|
|
44
|
+
env:
|
|
45
|
+
GH_TOKEN: ${{ github.token }}
|
|
46
|
+
run: |
|
|
47
|
+
python - <<'PY'
|
|
48
|
+
from json import loads
|
|
49
|
+
from os import environ
|
|
50
|
+
from pathlib import Path
|
|
51
|
+
from subprocess import check_output
|
|
52
|
+
|
|
53
|
+
head = environ['GITHUB_SHA']
|
|
54
|
+
if environ['GITHUB_EVENT_NAME'] == 'pull_request':
|
|
55
|
+
event = loads(Path(environ['GITHUB_EVENT_PATH']).read_text(encoding='utf-8'))
|
|
56
|
+
branch = event['pull_request']['head']['ref']
|
|
57
|
+
else:
|
|
58
|
+
repository = environ['GITHUB_REPOSITORY']
|
|
59
|
+
pages = loads(check_output([
|
|
60
|
+
'gh', 'api', '--paginate', '--slurp',
|
|
61
|
+
f'repos/{repository}/commits/{head}/pulls?per_page=100',
|
|
62
|
+
]))
|
|
63
|
+
pulls = [
|
|
64
|
+
pull for page in pages for pull in page
|
|
65
|
+
if pull['merged_at'] is not None
|
|
66
|
+
and pull['merge_commit_sha'] == head
|
|
67
|
+
and pull['base']['ref'] == 'master'
|
|
68
|
+
and pull['base']['repo']['full_name'] == repository
|
|
69
|
+
]
|
|
70
|
+
if len(pulls) != 1:
|
|
71
|
+
raise ValueError('Expected exactly one merged PR for the release commit')
|
|
72
|
+
branch = pulls[0]['head']['ref']
|
|
73
|
+
path = f'issues/{branch}/README.md'
|
|
74
|
+
entry = check_output(['git', 'ls-tree', head, '--', path]).decode().split()
|
|
75
|
+
if not entry or entry[0] not in ('100644', '100755'):
|
|
76
|
+
raise ValueError(f'Changelog must exist as a regular file: {path}')
|
|
77
|
+
body = check_output(['git', 'show', f'{head}:{path}'])
|
|
78
|
+
if not body.decode('utf-8').strip():
|
|
79
|
+
raise ValueError(f'Customer changelog is empty: {path}')
|
|
80
|
+
notes = Path(environ['RUNNER_TEMP'], 'release-notes', 'README.md')
|
|
81
|
+
notes.parent.mkdir(parents=True, exist_ok=True)
|
|
82
|
+
notes.write_bytes(body)
|
|
83
|
+
print(f'Customer changelog: {path}')
|
|
84
|
+
with Path(environ['GITHUB_OUTPUT']).open('a', encoding='utf-8') as output:
|
|
85
|
+
output.write(f'branch={branch}\n')
|
|
86
|
+
PY
|
|
87
|
+
- name: Resolve dependency refs
|
|
88
|
+
id: refs
|
|
89
|
+
env:
|
|
90
|
+
BRANCH: ${{ steps.changelog.outputs.branch }}
|
|
91
|
+
run: |
|
|
92
|
+
python - <<'PY'
|
|
93
|
+
from json import loads
|
|
94
|
+
from os import environ
|
|
95
|
+
from pathlib import Path
|
|
96
|
+
from subprocess import check_output, run
|
|
97
|
+
|
|
98
|
+
head = environ['GITHUB_SHA']
|
|
99
|
+
path = f'issues/{environ["BRANCH"]}/ci.refs.json'
|
|
100
|
+
entry = check_output(['git', 'ls-tree', head, '--', path]).decode().split()
|
|
101
|
+
refs = {}
|
|
102
|
+
if entry:
|
|
103
|
+
if entry[0] not in ('100644', '100755'):
|
|
104
|
+
raise ValueError(f'Dependency refs must be a regular file: {path}')
|
|
105
|
+
refs = loads(check_output(['git', 'show', f'{head}:{path}']).decode('utf-8'))
|
|
106
|
+
else:
|
|
107
|
+
print(f'{path} not found: dependency refs default to master')
|
|
108
|
+
projects = {
|
|
109
|
+
'DefStream': 'core_ref',
|
|
110
|
+
'DefStream-RabbitMQ': 'rabbitmq_ref',
|
|
111
|
+
'DefStream-Redis': 'redis_ref',
|
|
112
|
+
}
|
|
113
|
+
if not isinstance(refs, dict) or refs.keys() - projects.keys():
|
|
114
|
+
raise ValueError(f'Expected an object with only supported project names: {path}')
|
|
115
|
+
with Path(environ['GITHUB_OUTPUT']).open('a', encoding='utf-8') as output:
|
|
116
|
+
for project, key in projects.items():
|
|
117
|
+
ref = refs.get(project, 'master')
|
|
118
|
+
if not isinstance(ref, str) or not ref or ref.startswith('-'):
|
|
119
|
+
raise ValueError(f'Invalid dependency ref for {project}: {ref!r}')
|
|
120
|
+
run(['git', 'check-ref-format', '--allow-onelevel', ref], check=True)
|
|
121
|
+
output.write(f'{key}={ref}\n')
|
|
122
|
+
print(f'{project} dependency ref: {ref}')
|
|
123
|
+
PY
|
|
124
|
+
- name: Preserve customer release notes
|
|
125
|
+
uses: actions/upload-artifact@v7
|
|
126
|
+
with:
|
|
127
|
+
name: release-notes
|
|
128
|
+
path: ${{ runner.temp }}/release-notes/README.md
|
|
129
|
+
if-no-files-found: error
|
|
130
|
+
retention-days: 14
|
|
131
|
+
overwrite: true
|
|
36
132
|
- name: Check complete push range and plan version
|
|
37
133
|
id: changes
|
|
38
134
|
env:
|
|
@@ -132,7 +228,7 @@ jobs:
|
|
|
132
228
|
uses: actions/checkout@v7
|
|
133
229
|
with:
|
|
134
230
|
repository: yong5eon/DefStream
|
|
135
|
-
ref: ${{ github.event.repository.name == 'DefStream' && github.sha ||
|
|
231
|
+
ref: ${{ github.event.repository.name == 'DefStream' && github.sha || needs.changes.outputs.core_ref }}
|
|
136
232
|
path: packages/DefStream
|
|
137
233
|
fetch-depth: 0
|
|
138
234
|
persist-credentials: false
|
|
@@ -141,7 +237,7 @@ jobs:
|
|
|
141
237
|
uses: actions/checkout@v7
|
|
142
238
|
with:
|
|
143
239
|
repository: yong5eon/DefStream-RabbitMQ
|
|
144
|
-
ref: ${{ github.event.repository.name == 'DefStream-RabbitMQ' && github.sha ||
|
|
240
|
+
ref: ${{ github.event.repository.name == 'DefStream-RabbitMQ' && github.sha || needs.changes.outputs.rabbitmq_ref }}
|
|
145
241
|
path: packages/DefStream-RabbitMQ
|
|
146
242
|
fetch-depth: 0
|
|
147
243
|
persist-credentials: false
|
|
@@ -150,7 +246,7 @@ jobs:
|
|
|
150
246
|
uses: actions/checkout@v7
|
|
151
247
|
with:
|
|
152
248
|
repository: yong5eon/DefStream-Redis
|
|
153
|
-
ref: ${{ github.event.repository.name == 'DefStream-Redis' && github.sha ||
|
|
249
|
+
ref: ${{ github.event.repository.name == 'DefStream-Redis' && github.sha || needs.changes.outputs.redis_ref }}
|
|
154
250
|
path: packages/DefStream-Redis
|
|
155
251
|
fetch-depth: 0
|
|
156
252
|
persist-credentials: false
|
|
@@ -267,6 +363,12 @@ jobs:
|
|
|
267
363
|
name: distributions
|
|
268
364
|
path: dist
|
|
269
365
|
|
|
366
|
+
- name: Download customer release notes
|
|
367
|
+
uses: actions/download-artifact@v8
|
|
368
|
+
with:
|
|
369
|
+
name: release-notes
|
|
370
|
+
path: release-notes
|
|
371
|
+
|
|
270
372
|
- name: Set up Python
|
|
271
373
|
uses: actions/setup-python@v7
|
|
272
374
|
with:
|
|
@@ -371,6 +473,9 @@ jobs:
|
|
|
371
473
|
|
|
372
474
|
repository = environ['GITHUB_REPOSITORY']
|
|
373
475
|
version = environ['VERSION']
|
|
476
|
+
notes = Path('release-notes/README.md')
|
|
477
|
+
if not notes.read_text(encoding='utf-8').strip():
|
|
478
|
+
raise ValueError('Customer release notes are empty')
|
|
374
479
|
request = Request(
|
|
375
480
|
f'{environ["GITHUB_API_URL"]}/repos/{repository}/releases/tags/{quote(version, safe="")}',
|
|
376
481
|
headers={
|
|
@@ -387,7 +492,7 @@ jobs:
|
|
|
387
492
|
raise
|
|
388
493
|
run([
|
|
389
494
|
'gh', 'release', 'create', version, '--repo', repository,
|
|
390
|
-
'--verify-tag', '--title', version, '--
|
|
495
|
+
'--verify-tag', '--title', version, '--notes-file', str(notes), '--draft',
|
|
391
496
|
], check=True)
|
|
392
497
|
else:
|
|
393
498
|
if release['tag_name'] != version:
|
|
@@ -398,6 +503,7 @@ jobs:
|
|
|
398
503
|
'--clobber', *artifacts,
|
|
399
504
|
], check=True)
|
|
400
505
|
run([
|
|
401
|
-
'gh', 'release', 'edit', version, '--repo', repository,
|
|
506
|
+
'gh', 'release', 'edit', version, '--repo', repository,
|
|
507
|
+
'--notes-file', str(notes), '--draft=false',
|
|
402
508
|
], check=True)
|
|
403
509
|
PY
|
{defstream_rabbitmq-0.1.1.3 → defstream_rabbitmq-0.1.2.6/DefStream_RabbitMQ.egg-info}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: DefStream-RabbitMQ
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2.6
|
|
4
4
|
Summary: RabbitMQ broker adapter for DefStream
|
|
5
5
|
Home-page: https://github.com/yong5eon/DefStream-RabbitMQ
|
|
6
6
|
Author: Heo Yongseon
|
|
@@ -176,12 +176,54 @@ Worker는 실행과 완료 훅이 끝난 뒤 ACK하고, 실패하면 지정한
|
|
|
176
176
|
Worker의 공유 풀 크기와 별개입니다.
|
|
177
177
|
- `error`: `declare()`가 생성하고 연결할 오류 큐 이름. 거부된 메시지의 DLQ입니다.
|
|
178
178
|
- `offset`: RabbitMQ 스트림 구독에 사용할 정수 오프셋.
|
|
179
|
+
- `max_priority`: classic 작업 큐의 최대 우선순위. 정수 `1..255` 또는 `None`.
|
|
180
|
+
- `priority`: 기본 메시지 우선순위. 정수 `0..max_priority` 또는 `None`.
|
|
181
|
+
이 값만 지정하면 `max_priority`는 `255`로 설정됩니다.
|
|
179
182
|
|
|
180
183
|
`Subscription` 객체 생성이나 `consumer.subs()`는 큐를 선언하지 않습니다.
|
|
181
184
|
작업용 `declare()`는 일반 durable 큐와 선택적 DLQ를 준비하며,
|
|
182
185
|
스트림 큐나 Exchange 바인딩을 자동 생성하지 않습니다.
|
|
183
186
|
기존 큐의 선언 옵션이 다르면 실패하며 자동 삭제·재생성하지 않습니다.
|
|
184
187
|
|
|
188
|
+
## 워커 작업 우선순위
|
|
189
|
+
|
|
190
|
+
작업에 지정한 `Subscription`에 기본값 `priority`와 큐 최대값 `max_priority`를
|
|
191
|
+
함께 설정합니다. `Run`에는 별도의 `priority` 설정을 두지 않습니다.
|
|
192
|
+
|
|
193
|
+
```python
|
|
194
|
+
class PriorityAdd(
|
|
195
|
+
Add,
|
|
196
|
+
parameters=Subscription(
|
|
197
|
+
queue='defstream.example.rabbitmq.priority.add',
|
|
198
|
+
qos=1,
|
|
199
|
+
max_priority=5,
|
|
200
|
+
priority=2,
|
|
201
|
+
),
|
|
202
|
+
):
|
|
203
|
+
pass
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
- 워커 API `declare(task=PriorityAdd, broker=connection)`는 큐에
|
|
207
|
+
`x-max-priority=5`를 반영합니다. 기존 DLQ 설정도 보존합니다.
|
|
208
|
+
- `send(..., task=PriorityAdd, broker=connection)` 및
|
|
209
|
+
`request(..., task=PriorityAdd, broker=connection)`는 작업 기본값 `2`를 사용합니다.
|
|
210
|
+
호출에 `priority=0` 또는 다른 유효한 값을 지정하여 재정의할 수 있습니다.
|
|
211
|
+
- 기본값만 정의하면 큐 최대값은 `255`, 최대값만 정의하면 기본값은 `None`입니다.
|
|
212
|
+
호출별 값도 없으면 AMQP 우선순위 속성을 생략합니다.
|
|
213
|
+
- 워커 경로는 작업의 유효 최대값으로 검증합니다. 작업 설정 없이 호출에서만
|
|
214
|
+
지정하면 `0..255` 범위만 검증하며, 우선순위 큐 사전 선언은 호출자 책임입니다.
|
|
215
|
+
- 기존 저수준 `Queue.send`, `Exchange.send`와 `Queue.call`도 `priority=`를 받습니다.
|
|
216
|
+
이 경로는 작업 정의를 받지 않으므로 작업별 최대값은 검증하지 않습니다.
|
|
217
|
+
정수 `0..255`만 허용하며 `bool`은 거부합니다.
|
|
218
|
+
- 최대값을 늘릴수록 자원 비용이 증가하므로 필요한 수준만 지정하는 것을 권장합니다.
|
|
219
|
+
- 동일 큐를 여러 작업 정의에서 선언한다면 최대값을 동일하게 맞춰야 합니다.
|
|
220
|
+
기존 일반 큐나 최대값이 다른 큐를 재선언하면 RabbitMQ 오류가 전파됩니다.
|
|
221
|
+
자동 삭제하지 않으므로, 새 큐로 전환하거나 미처리 메시지를 보존한 후 운영자가
|
|
222
|
+
안전하게 재생성하고 생산자·소비자를 함께 전환해야 합니다.
|
|
223
|
+
- 높은 값이 먼저 전달되지만 이미 prefetch된 메시지나 실행 중인 작업은 선점하지
|
|
224
|
+
않습니다. 재전달, 다중 소비자에서는 완료 순서를 보장하지 않으며 낮은 우선순위
|
|
225
|
+
메시지가 오래 대기할 수 있습니다. Stream 및 Quorum 큐는 이번 지원 대상이 아닙니다.
|
|
226
|
+
|
|
185
227
|
## 동기 RPC
|
|
186
228
|
|
|
187
229
|
실행 중인 Worker가 있는 큐에 `call()`을 사용하면 `Run.run()`의 반환값을 받습니다.
|
{defstream_rabbitmq-0.1.1.3 → defstream_rabbitmq-0.1.2.6}/DefStream_RabbitMQ.egg-info/SOURCES.txt
RENAMED
|
@@ -9,6 +9,11 @@ DefStream_RabbitMQ.egg-info/SOURCES.txt
|
|
|
9
9
|
DefStream_RabbitMQ.egg-info/dependency_links.txt
|
|
10
10
|
DefStream_RabbitMQ.egg-info/requires.txt
|
|
11
11
|
DefStream_RabbitMQ.egg-info/top_level.txt
|
|
12
|
+
issues/feature/1/IMPLEMENTS.md
|
|
13
|
+
issues/feature/1/README.md
|
|
14
|
+
issues/feature/1/REQUIREMENTS.md
|
|
15
|
+
issues/feature/1/TASK.md
|
|
16
|
+
issues/feature/1/ci.refs.json
|
|
12
17
|
sample/Sample.RPC.py
|
|
13
18
|
sample/Sample.Stream.py
|
|
14
19
|
sample/Sample.py
|
|
@@ -24,6 +29,7 @@ src/Stream.py
|
|
|
24
29
|
src/Subscription.py
|
|
25
30
|
src/__init__.py
|
|
26
31
|
test/test_broker_methods.py
|
|
32
|
+
test/test_priorities.py
|
|
27
33
|
test/test_rabbitmq_integration.py
|
|
28
34
|
test/test_rabbitmq_worker.py
|
|
29
35
|
test/test_rpc.py
|
{defstream_rabbitmq-0.1.1.3/DefStream_RabbitMQ.egg-info → defstream_rabbitmq-0.1.2.6}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: DefStream-RabbitMQ
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2.6
|
|
4
4
|
Summary: RabbitMQ broker adapter for DefStream
|
|
5
5
|
Home-page: https://github.com/yong5eon/DefStream-RabbitMQ
|
|
6
6
|
Author: Heo Yongseon
|
|
@@ -176,12 +176,54 @@ Worker는 실행과 완료 훅이 끝난 뒤 ACK하고, 실패하면 지정한
|
|
|
176
176
|
Worker의 공유 풀 크기와 별개입니다.
|
|
177
177
|
- `error`: `declare()`가 생성하고 연결할 오류 큐 이름. 거부된 메시지의 DLQ입니다.
|
|
178
178
|
- `offset`: RabbitMQ 스트림 구독에 사용할 정수 오프셋.
|
|
179
|
+
- `max_priority`: classic 작업 큐의 최대 우선순위. 정수 `1..255` 또는 `None`.
|
|
180
|
+
- `priority`: 기본 메시지 우선순위. 정수 `0..max_priority` 또는 `None`.
|
|
181
|
+
이 값만 지정하면 `max_priority`는 `255`로 설정됩니다.
|
|
179
182
|
|
|
180
183
|
`Subscription` 객체 생성이나 `consumer.subs()`는 큐를 선언하지 않습니다.
|
|
181
184
|
작업용 `declare()`는 일반 durable 큐와 선택적 DLQ를 준비하며,
|
|
182
185
|
스트림 큐나 Exchange 바인딩을 자동 생성하지 않습니다.
|
|
183
186
|
기존 큐의 선언 옵션이 다르면 실패하며 자동 삭제·재생성하지 않습니다.
|
|
184
187
|
|
|
188
|
+
## 워커 작업 우선순위
|
|
189
|
+
|
|
190
|
+
작업에 지정한 `Subscription`에 기본값 `priority`와 큐 최대값 `max_priority`를
|
|
191
|
+
함께 설정합니다. `Run`에는 별도의 `priority` 설정을 두지 않습니다.
|
|
192
|
+
|
|
193
|
+
```python
|
|
194
|
+
class PriorityAdd(
|
|
195
|
+
Add,
|
|
196
|
+
parameters=Subscription(
|
|
197
|
+
queue='defstream.example.rabbitmq.priority.add',
|
|
198
|
+
qos=1,
|
|
199
|
+
max_priority=5,
|
|
200
|
+
priority=2,
|
|
201
|
+
),
|
|
202
|
+
):
|
|
203
|
+
pass
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
- 워커 API `declare(task=PriorityAdd, broker=connection)`는 큐에
|
|
207
|
+
`x-max-priority=5`를 반영합니다. 기존 DLQ 설정도 보존합니다.
|
|
208
|
+
- `send(..., task=PriorityAdd, broker=connection)` 및
|
|
209
|
+
`request(..., task=PriorityAdd, broker=connection)`는 작업 기본값 `2`를 사용합니다.
|
|
210
|
+
호출에 `priority=0` 또는 다른 유효한 값을 지정하여 재정의할 수 있습니다.
|
|
211
|
+
- 기본값만 정의하면 큐 최대값은 `255`, 최대값만 정의하면 기본값은 `None`입니다.
|
|
212
|
+
호출별 값도 없으면 AMQP 우선순위 속성을 생략합니다.
|
|
213
|
+
- 워커 경로는 작업의 유효 최대값으로 검증합니다. 작업 설정 없이 호출에서만
|
|
214
|
+
지정하면 `0..255` 범위만 검증하며, 우선순위 큐 사전 선언은 호출자 책임입니다.
|
|
215
|
+
- 기존 저수준 `Queue.send`, `Exchange.send`와 `Queue.call`도 `priority=`를 받습니다.
|
|
216
|
+
이 경로는 작업 정의를 받지 않으므로 작업별 최대값은 검증하지 않습니다.
|
|
217
|
+
정수 `0..255`만 허용하며 `bool`은 거부합니다.
|
|
218
|
+
- 최대값을 늘릴수록 자원 비용이 증가하므로 필요한 수준만 지정하는 것을 권장합니다.
|
|
219
|
+
- 동일 큐를 여러 작업 정의에서 선언한다면 최대값을 동일하게 맞춰야 합니다.
|
|
220
|
+
기존 일반 큐나 최대값이 다른 큐를 재선언하면 RabbitMQ 오류가 전파됩니다.
|
|
221
|
+
자동 삭제하지 않으므로, 새 큐로 전환하거나 미처리 메시지를 보존한 후 운영자가
|
|
222
|
+
안전하게 재생성하고 생산자·소비자를 함께 전환해야 합니다.
|
|
223
|
+
- 높은 값이 먼저 전달되지만 이미 prefetch된 메시지나 실행 중인 작업은 선점하지
|
|
224
|
+
않습니다. 재전달, 다중 소비자에서는 완료 순서를 보장하지 않으며 낮은 우선순위
|
|
225
|
+
메시지가 오래 대기할 수 있습니다. Stream 및 Quorum 큐는 이번 지원 대상이 아닙니다.
|
|
226
|
+
|
|
185
227
|
## 동기 RPC
|
|
186
228
|
|
|
187
229
|
실행 중인 Worker가 있는 큐에 `call()`을 사용하면 `Run.run()`의 반환값을 받습니다.
|
|
@@ -146,12 +146,59 @@ the selected failure policy on error. Omitting `on_error` defaults to `Requeue()
|
|
|
146
146
|
is 1; 0 means unlimited. This is separate from shared Worker pool capacity.
|
|
147
147
|
- `error`: error queue to create and bind through `declare()`; a DLQ for rejected messages.
|
|
148
148
|
- `offset`: integer offset for RabbitMQ stream subscriptions.
|
|
149
|
+
- `max_priority`: maximum priority for a classic task queue; an integer in `1..255` or `None`.
|
|
150
|
+
- `priority`: default message priority; an integer in `0..max_priority` or `None`.
|
|
151
|
+
Setting only this value sets `max_priority` to `255`.
|
|
149
152
|
|
|
150
153
|
Constructing a `Subscription` or calling `consumer.subs()` does not declare queues.
|
|
151
154
|
Task `declare()` provisions ordinary durable queues and an optional DLQ,
|
|
152
155
|
not stream queues or Exchange bindings. Incompatible existing queue options
|
|
153
156
|
fail explicitly; queues are not automatically deleted and recreated.
|
|
154
157
|
|
|
158
|
+
## Worker task priorities
|
|
159
|
+
|
|
160
|
+
Set the default `priority` and queue `max_priority` together in the task's
|
|
161
|
+
`Subscription`. There is no separate `priority` setting on `Run`.
|
|
162
|
+
|
|
163
|
+
```python
|
|
164
|
+
class PriorityAdd(
|
|
165
|
+
Add,
|
|
166
|
+
parameters=Subscription(
|
|
167
|
+
queue='defstream.example.rabbitmq.priority.add',
|
|
168
|
+
qos=1,
|
|
169
|
+
max_priority=5,
|
|
170
|
+
priority=2,
|
|
171
|
+
),
|
|
172
|
+
):
|
|
173
|
+
pass
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
- The Worker API `declare(task=PriorityAdd, broker=connection)` declares the queue
|
|
177
|
+
with `x-max-priority=5` while preserving existing DLQ settings.
|
|
178
|
+
- `send(..., task=PriorityAdd, broker=connection)` and
|
|
179
|
+
`request(..., task=PriorityAdd, broker=connection)` use the task default of `2`.
|
|
180
|
+
Pass `priority=0` or another valid value to override it for an individual call.
|
|
181
|
+
- Setting only the default sets the queue maximum to `255`. Setting only the
|
|
182
|
+
maximum leaves the default as `None`. Without a per-call value either, the AMQP
|
|
183
|
+
priority property is omitted.
|
|
184
|
+
- Worker calls validate against the task's effective maximum. If priority is
|
|
185
|
+
specified only at call time, only the `0..255` range is validated; the caller
|
|
186
|
+
must provision a priority queue beforehand.
|
|
187
|
+
- The existing low-level `Queue.send`, `Exchange.send`, and `Queue.call` also
|
|
188
|
+
accept `priority=`. These APIs do not receive a task definition and therefore
|
|
189
|
+
do not validate against a task-specific maximum. They accept integers in
|
|
190
|
+
`0..255` and reject `bool`.
|
|
191
|
+
- Larger maximum values increase resource costs; configure only the levels you need.
|
|
192
|
+
- Task definitions declaring the same queue must use the same maximum.
|
|
193
|
+
Redeclaring an ordinary queue or a queue with a different maximum propagates
|
|
194
|
+
a RabbitMQ error. Queues are not automatically deleted. Switch to a new queue,
|
|
195
|
+
or preserve pending messages before safely recreating the queue and migrating
|
|
196
|
+
producers and consumers together.
|
|
197
|
+
- Higher values are delivered first, but prefetched messages and running tasks
|
|
198
|
+
are not preempted. Redelivery and multiple consumers prevent guarantees about
|
|
199
|
+
completion order, and lower-priority messages can wait indefinitely.
|
|
200
|
+
Streams and quorum queues are outside the scope of this feature.
|
|
201
|
+
|
|
155
202
|
## Synchronous RPC
|
|
156
203
|
|
|
157
204
|
Call a queue served by a running Worker to receive the result of `Run.run()`.
|
|
@@ -145,12 +145,54 @@ Worker는 실행과 완료 훅이 끝난 뒤 ACK하고, 실패하면 지정한
|
|
|
145
145
|
Worker의 공유 풀 크기와 별개입니다.
|
|
146
146
|
- `error`: `declare()`가 생성하고 연결할 오류 큐 이름. 거부된 메시지의 DLQ입니다.
|
|
147
147
|
- `offset`: RabbitMQ 스트림 구독에 사용할 정수 오프셋.
|
|
148
|
+
- `max_priority`: classic 작업 큐의 최대 우선순위. 정수 `1..255` 또는 `None`.
|
|
149
|
+
- `priority`: 기본 메시지 우선순위. 정수 `0..max_priority` 또는 `None`.
|
|
150
|
+
이 값만 지정하면 `max_priority`는 `255`로 설정됩니다.
|
|
148
151
|
|
|
149
152
|
`Subscription` 객체 생성이나 `consumer.subs()`는 큐를 선언하지 않습니다.
|
|
150
153
|
작업용 `declare()`는 일반 durable 큐와 선택적 DLQ를 준비하며,
|
|
151
154
|
스트림 큐나 Exchange 바인딩을 자동 생성하지 않습니다.
|
|
152
155
|
기존 큐의 선언 옵션이 다르면 실패하며 자동 삭제·재생성하지 않습니다.
|
|
153
156
|
|
|
157
|
+
## 워커 작업 우선순위
|
|
158
|
+
|
|
159
|
+
작업에 지정한 `Subscription`에 기본값 `priority`와 큐 최대값 `max_priority`를
|
|
160
|
+
함께 설정합니다. `Run`에는 별도의 `priority` 설정을 두지 않습니다.
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
class PriorityAdd(
|
|
164
|
+
Add,
|
|
165
|
+
parameters=Subscription(
|
|
166
|
+
queue='defstream.example.rabbitmq.priority.add',
|
|
167
|
+
qos=1,
|
|
168
|
+
max_priority=5,
|
|
169
|
+
priority=2,
|
|
170
|
+
),
|
|
171
|
+
):
|
|
172
|
+
pass
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
- 워커 API `declare(task=PriorityAdd, broker=connection)`는 큐에
|
|
176
|
+
`x-max-priority=5`를 반영합니다. 기존 DLQ 설정도 보존합니다.
|
|
177
|
+
- `send(..., task=PriorityAdd, broker=connection)` 및
|
|
178
|
+
`request(..., task=PriorityAdd, broker=connection)`는 작업 기본값 `2`를 사용합니다.
|
|
179
|
+
호출에 `priority=0` 또는 다른 유효한 값을 지정하여 재정의할 수 있습니다.
|
|
180
|
+
- 기본값만 정의하면 큐 최대값은 `255`, 최대값만 정의하면 기본값은 `None`입니다.
|
|
181
|
+
호출별 값도 없으면 AMQP 우선순위 속성을 생략합니다.
|
|
182
|
+
- 워커 경로는 작업의 유효 최대값으로 검증합니다. 작업 설정 없이 호출에서만
|
|
183
|
+
지정하면 `0..255` 범위만 검증하며, 우선순위 큐 사전 선언은 호출자 책임입니다.
|
|
184
|
+
- 기존 저수준 `Queue.send`, `Exchange.send`와 `Queue.call`도 `priority=`를 받습니다.
|
|
185
|
+
이 경로는 작업 정의를 받지 않으므로 작업별 최대값은 검증하지 않습니다.
|
|
186
|
+
정수 `0..255`만 허용하며 `bool`은 거부합니다.
|
|
187
|
+
- 최대값을 늘릴수록 자원 비용이 증가하므로 필요한 수준만 지정하는 것을 권장합니다.
|
|
188
|
+
- 동일 큐를 여러 작업 정의에서 선언한다면 최대값을 동일하게 맞춰야 합니다.
|
|
189
|
+
기존 일반 큐나 최대값이 다른 큐를 재선언하면 RabbitMQ 오류가 전파됩니다.
|
|
190
|
+
자동 삭제하지 않으므로, 새 큐로 전환하거나 미처리 메시지를 보존한 후 운영자가
|
|
191
|
+
안전하게 재생성하고 생산자·소비자를 함께 전환해야 합니다.
|
|
192
|
+
- 높은 값이 먼저 전달되지만 이미 prefetch된 메시지나 실행 중인 작업은 선점하지
|
|
193
|
+
않습니다. 재전달, 다중 소비자에서는 완료 순서를 보장하지 않으며 낮은 우선순위
|
|
194
|
+
메시지가 오래 대기할 수 있습니다. Stream 및 Quorum 큐는 이번 지원 대상이 아닙니다.
|
|
195
|
+
|
|
154
196
|
## 동기 RPC
|
|
155
197
|
|
|
156
198
|
실행 중인 Worker가 있는 큐에 `call()`을 사용하면 `Run.run()`의 반환값을 받습니다.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# RabbitMQ 작업 우선순위 구현 기록
|
|
2
|
+
|
|
3
|
+
## 1. 구현 범위
|
|
4
|
+
|
|
5
|
+
[요구사항](./REQUIREMENTS.md)에 따라 classic 작업 큐의 선언, 일반 전송 및 RPC
|
|
6
|
+
요청에 우선순위를 연결했다. 기존 큐 종류를 변경하거나 Redis 기능을 추가하지 않았다.
|
|
7
|
+
|
|
8
|
+
## 2. 주요 변경
|
|
9
|
+
|
|
10
|
+
- [Subscription](../../../src/Subscription.py)의 기존 필드 뒤에 `max_priority`와
|
|
11
|
+
`priority`를 추가하여 기존 위치 인자의 의미를 보존했다. 매핑 입력에도 동일하게 적용한다.
|
|
12
|
+
- 최대값은 `1..255` 정수로 검증한다. 메시지 값은 최대값이 있으면 그 범위,
|
|
13
|
+
없으면 `0..255`로 검증한다. `bool`은 거부한다.
|
|
14
|
+
- 구독 생성·파싱 시 `priority`가 있고 최대값이 없으면 `max_priority=255`로
|
|
15
|
+
정규화하고 두 값의 범위를 함께 검증한다. 최대값만 있으면 기본값은 `None`이다.
|
|
16
|
+
- [Connection](../../../src/Connection.py)의 `priority_parameters`는
|
|
17
|
+
`Subscription.parse`에 위임한다. 기본값을 별도 인자로 받거나 `Run`에서 가져오지 않는다.
|
|
18
|
+
- `declare`는 최대값을 `x-max-priority`에 반영하고 기존 DLQ 인자를 보존한다.
|
|
19
|
+
오류 큐에는 작업 우선순위를 자동 적용하지 않는다. 선언 충돌은 그대로 전파한다.
|
|
20
|
+
- `send`는 메시지 우선순위를 AMQP 속성에 반영한다.
|
|
21
|
+
`request`는 `Queue.call`까지 우선순위를 전달한다. 두 메서드는 호출값이
|
|
22
|
+
`None`이면 `Subscription.priority`를 사용하며 명시적 `0`을 보존한다.
|
|
23
|
+
- [Queue.call](../../../src/Queue.py)은 기존 타임아웃 위치 인자를 보존하면서
|
|
24
|
+
키워드 전용 `priority`를 추가했다. 임시 응답 큐와 응답 메시지에는 적용하지 않는다.
|
|
25
|
+
- 기존 `Queue.send`, [Exchange.send](../../../src/Exchange.py)도 공유 검증 함수를
|
|
26
|
+
사용하여 브로커 범위를 검증한다. 작업 정의를 받지 않는 저수준 API는 작업별
|
|
27
|
+
최대값을 강제하지 않는다.
|
|
28
|
+
- 기존 publisher confirm, mandatory, RPC correlation ID, 만료, 원격 오류,
|
|
29
|
+
연결 정리 및 브로커 NACK 재전달 방식은 유지했다.
|
|
30
|
+
- 공개 바인딩 클래스 `Any`와 충돌하지 않도록 타입 주석용 `typing.Any`는
|
|
31
|
+
`AnyValue`로 임포트했다. 공개 바인딩 클래스 이름과 런타임 동작은 유지했다.
|
|
32
|
+
|
|
33
|
+
## 3. 검증 결과
|
|
34
|
+
|
|
35
|
+
- 전체 어댑터 테스트 35개 중 32개 통과, 선택적 통합 테스트 3개 건너뜀.
|
|
36
|
+
- 새 단위 테스트 5개로 구독 파싱과 범위, DLQ 인자 병합, 선언 오류 전파,
|
|
37
|
+
큐·exchange 전송 및 실제 RPC 발행 속성을 모의 연결로 검증했다.
|
|
38
|
+
- 실제 classic 큐의 우선순위·동순위 FIFO 전달, 재전달 속성 보존 및 선언 충돌을
|
|
39
|
+
검증하는 선택적 통합 테스트 1개를 추가했다.
|
|
40
|
+
- 로컬 AMQP 포트에는 접속할 수 있었지만 기본 계정은 인증 거부를 받았다.
|
|
41
|
+
사용자가 제공한 별도 계정으로 재시도한 결과 인증 후 `/` vhost 접근이
|
|
42
|
+
`530 NOT_ALLOWED`로 거부되어 실제 전달 검증은 완료하지 못했다.
|
|
43
|
+
- 서버의 계정, 권한, vhost 및 기존 큐는 변경하지 않았다.
|
|
44
|
+
인증 정보는 소스나 문서에 저장하지 않고 해당 테스트 프로세스의 환경변수로만 전달했다.
|
|
45
|
+
- 후속 검증에서는 작업 폴더의 Docker 설정으로 RabbitMQ 3.13.7을 새로 기동했다.
|
|
46
|
+
기본 `guest` 계정과 `/` vhost, classic 큐에서 전체 35개 테스트가
|
|
47
|
+
건너뜀 없이 통과했다. 앞선 인증·권한 문제로 인한 통합 검증 제한은 해소됐다.
|
|
48
|
+
- 높은 우선순위 우선 전달, 동순위 FIFO, 재전달 속성 보존, 선언 충돌 전파와
|
|
49
|
+
기존 워커·RPC 성공·실패·타임아웃 경로를 실제 브로커에서 검증했다.
|
|
50
|
+
- 어댑터 원본 경로의 Pylance 진단은 오류가 없었지만, 코어 내부 정션 경로와
|
|
51
|
+
테스트의 import 분석에는 이전 시그니처 및 타입 진단이 남아 있었다.
|
|
52
|
+
실제 파일·런타임 시그니처와 분석 결과가 달라 전체 정적 타입 검증을
|
|
53
|
+
통과한 것으로 간주하지 않는다. 큐의 `auto_decode`, 기존 nullable 주석 등
|
|
54
|
+
이번 기능과 무관한 진단도 있어 일괄 수정하지 않았다.
|
|
55
|
+
- 기본값을 `Subscription.priority`로 통합한 뒤 Docker RabbitMQ 3.13.7에서
|
|
56
|
+
전체 38개 테스트가 건너뜀 없이 통과했다. 구독 기본값의 정규화·입력 불변성,
|
|
57
|
+
잘못된 기본값과 최대값 조합, 직접 브로커 전송·RPC의 기본값 사용을 추가로 검증했다.
|
|
58
|
+
- 최신 Pylance 진단에서는 어댑터 원본 경로의 상대 import 해석 문제와 기존
|
|
59
|
+
타입 진단도 확인되어 전체 정적 분석 통과 상태는 아니다.
|
|
60
|
+
|
|
61
|
+
## 4. 재검증 방법
|
|
62
|
+
|
|
63
|
+
작업 폴더 루트에서 단위 테스트를 실행한다.
|
|
64
|
+
|
|
65
|
+
```powershell
|
|
66
|
+
$env:PYTHONIOENCODING = 'utf-8'
|
|
67
|
+
$env:PYTHONUTF8 = '1'
|
|
68
|
+
.\.venv\Scripts\python.exe -m unittest discover -s .\DefStream-RabbitMQ\test
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
실제 브로커 테스트는 로컬 `127.0.0.1:5672`의 `/` vhost에 큐 생성·발행·소비·삭제
|
|
72
|
+
권한이 있는 계정이 필요하다. `DEFSTREAM_RABBITMQ_USERNAME`과
|
|
73
|
+
`DEFSTREAM_RABBITMQ_PASSWORD` 환경변수에 테스트 인증 정보를 설정한 후 실행한다.
|
|
74
|
+
미설정 시 기존 테스트 관례대로 `guest`를 사용한다.
|
|
75
|
+
|
|
76
|
+
```powershell
|
|
77
|
+
$env:DEFSTREAM_RABBITMQ_TESTS = '1'
|
|
78
|
+
.\.venv\Scripts\python.exe -m unittest discover -s .\DefStream-RabbitMQ\test -p test_priorities.py
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
통합 테스트는 고유한 이름의 큐만 생성하고 종료 시 해당 큐를 삭제한다.
|
|
82
|
+
|
|
83
|
+
## 5. 공통 브로커 참조 경로 통일
|
|
84
|
+
|
|
85
|
+
- 모든 공통 인터페이스·예외·검증 함수·정책 참조를
|
|
86
|
+
`from DefStream.Broker import ...`로 변경했다.
|
|
87
|
+
- 구현체 내부 상대 import와 기존 공개 정책 재노출은 유지했다.
|
|
88
|
+
- RabbitMQ 3.13.7 실서버 통합 테스트를 포함한 전체 38개 테스트가 통과했다.
|
|
89
|
+
|
|
90
|
+
## 6. 관련 문서
|
|
91
|
+
|
|
92
|
+
- [사용 설명서](../../../README.md)
|
|
93
|
+
- [공통 워커 구현 기록](../../../../DefStream/issues/feature/1/IMPLEMENTS.md)
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# RabbitMQ 워커 작업 우선순위 지원
|
|
2
|
+
|
|
3
|
+
## 변경 전 (AS-IS)
|
|
4
|
+
|
|
5
|
+
저수준 `create_queue(maxPriority=...)`, `Queue.send(priority=...)`에서는
|
|
6
|
+
우선순위를 지정할 수 있었지만, 워커의 작업 정의와 `declare`, `send`, `request`
|
|
7
|
+
경로에서는 이를 설정할 수 없었습니다. `Queue.call`도 요청 우선순위를 받지 않았습니다.
|
|
8
|
+
|
|
9
|
+
## 변경 후 (TO-BE)
|
|
10
|
+
|
|
11
|
+
작업에 지정하는 `Subscription`에서 큐 최대 우선순위와 기본 메시지 우선순위를
|
|
12
|
+
함께 정의할 수 있습니다. `declare`가 이 설정으로 classic 우선순위 큐를 생성하고,
|
|
13
|
+
`send`와 `request`는 작업 기본값 또는 호출별 우선순위로 요청을 발행합니다.
|
|
14
|
+
저수준 `Queue.call`에도 `priority=`를 사용할 수 있습니다.
|
|
15
|
+
|
|
16
|
+
## 사용 방법
|
|
17
|
+
|
|
18
|
+
작업 정의의 구독 설정에 `max_priority`와 `priority`를 추가하세요.
|
|
19
|
+
|
|
20
|
+
```python
|
|
21
|
+
from DefStream.Broker.Implements.RabbitMQ import Subscription
|
|
22
|
+
from DefStream.Worker import (
|
|
23
|
+
Run,
|
|
24
|
+
declare,
|
|
25
|
+
request,
|
|
26
|
+
send,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class Add(
|
|
31
|
+
Run,
|
|
32
|
+
parameters=Subscription(
|
|
33
|
+
queue='tasks.add.priority',
|
|
34
|
+
qos=1,
|
|
35
|
+
max_priority=5,
|
|
36
|
+
priority=2,
|
|
37
|
+
),
|
|
38
|
+
):
|
|
39
|
+
def run(
|
|
40
|
+
self,
|
|
41
|
+
a,
|
|
42
|
+
b,
|
|
43
|
+
):
|
|
44
|
+
return a + b
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
아래 코드는 호출자가 연결한 RabbitMQ `Connection`인 `broker`를 사용합니다.
|
|
48
|
+
생산자와 소비자가 같은 작업 정의를 사용하고, 전송 전에 큐를 준비하세요.
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
declare(
|
|
52
|
+
task=Add,
|
|
53
|
+
broker=broker,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
# 작업 기본 우선순위 2로 전송합니다.
|
|
57
|
+
send(
|
|
58
|
+
{'a': 1, 'b': 2},
|
|
59
|
+
task=Add,
|
|
60
|
+
broker=broker,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
# 실행 중인 Worker에 우선순위 5로 RPC 요청을 보냅니다.
|
|
64
|
+
result = request(
|
|
65
|
+
{'a': 1, 'b': 2},
|
|
66
|
+
task=Add,
|
|
67
|
+
broker=broker,
|
|
68
|
+
priority=5,
|
|
69
|
+
)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
| 설정 | 적용 결과 |
|
|
73
|
+
|---|---|
|
|
74
|
+
| `priority`만 지정 | 큐 최대값은 `255` |
|
|
75
|
+
| `max_priority`만 지정 | 기본 메시지 우선순위는 `None` |
|
|
76
|
+
| 호출값 생략 또는 `None` | 구독의 기본 `priority` 사용 |
|
|
77
|
+
| 호출값 `0` | 기본값을 덮어쓰는 가장 낮은 우선순위 |
|
|
78
|
+
| 작업·호출 모두 미지정 | 기존 동작 유지 |
|
|
79
|
+
|
|
80
|
+
## 기존 코드에 미치는 영향과 전환
|
|
81
|
+
|
|
82
|
+
- 우선순위를 사용하지 않는 기존 작업은 수정할 필요가 없습니다.
|
|
83
|
+
- 기존 일반 큐에 우선순위를 추가하거나 큐 최대값을 변경하면 재선언이 실패합니다.
|
|
84
|
+
새 큐 이름으로 선언하고 생산자·소비자를 함께 전환하세요. 같은 이름을 유지해야
|
|
85
|
+
한다면 미처리 메시지를 보존한 뒤 운영 절차에 따라 큐를 재생성해야 합니다.
|
|
86
|
+
라이브러리는 기존 큐를 자동 삭제하지 않습니다.
|
|
87
|
+
- 우선순위는 정수여야 하며 `bool`은 허용하지 않습니다. 최대값은 `1..255`,
|
|
88
|
+
메시지 값은 `0..큐 최대값`이어야 합니다. 타입 오류는 `TypeError`,
|
|
89
|
+
범위 오류는 `ValueError`가 발생합니다.
|
|
90
|
+
- 작업 설정 없이 호출에서만 지정하면 `0..255` 범위만 검증합니다.
|
|
91
|
+
이 경우 우선순위 큐의 사전 선언과 실제 큐 최대값 준수는 호출자 책임입니다.
|
|
92
|
+
- 기존 저수준 `Queue.send`, `Exchange.send`도 이제 우선순위의 타입과
|
|
93
|
+
`0..255` 범위를 명시적으로 검증합니다. 문자열·실수 등의 입력은 정수로 수정하세요.
|
|
94
|
+
- 자식 작업에서 구독 설정을 교체할 때 부모의 기본·최대 우선순위를 제거할 수
|
|
95
|
+
없습니다. 유지할 값이나 재정의할 유효한 값을 새 `Subscription`에도 명시하세요.
|
|
96
|
+
|
|
97
|
+
## 전달 순서 주의사항
|
|
98
|
+
|
|
99
|
+
숫자가 클수록 같은 큐에 대기 중인 메시지를 먼저 전달합니다. 이미 prefetch되거나
|
|
100
|
+
실행 중인 작업은 선점하지 않으며, 여러 소비자의 실행·완료 순서는 보장하지 않습니다.
|
|
101
|
+
높은 우선순위가 계속 들어오면 낮은 우선순위가 오래 대기할 수 있습니다.
|
|
102
|
+
큐 최대값이 클수록 자원 비용이 증가하므로 필요한 수준만 지정하세요.
|
|
103
|
+
이번 기능은 classic 큐를 대상으로 하며 Stream·Quorum 큐는 대상이 아닙니다.
|