jetstream-api 0.20.3__tar.gz → 0.21.3__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.
- {jetstream_api-0.20.3 → jetstream_api-0.21.3}/PKG-INFO +80 -1
- {jetstream_api-0.20.3 → jetstream_api-0.21.3}/README.md +79 -0
- {jetstream_api-0.20.3 → jetstream_api-0.21.3}/ilink/__init__.py +1 -1
- {jetstream_api-0.20.3 → jetstream_api-0.21.3}/ilink/_consumer.py +63 -8
- {jetstream_api-0.20.3 → jetstream_api-0.21.3}/ilink/pattern.py +69 -2
- {jetstream_api-0.20.3 → jetstream_api-0.21.3}/ilink/producer.py +703 -105
- {jetstream_api-0.20.3 → jetstream_api-0.21.3}/ilink/qmgr.py +90 -24
- {jetstream_api-0.20.3 → jetstream_api-0.21.3}/ilink/topic.py +69 -2
- {jetstream_api-0.20.3 → jetstream_api-0.21.3}/jetstream_api.egg-info/PKG-INFO +80 -1
- {jetstream_api-0.20.3 → jetstream_api-0.21.3}/pyproject.toml +1 -1
- {jetstream_api-0.20.3 → jetstream_api-0.21.3}/LICENSE.txt +0 -0
- {jetstream_api-0.20.3 → jetstream_api-0.21.3}/ilink/_codec.py +0 -0
- {jetstream_api-0.20.3 → jetstream_api-0.21.3}/ilink/_groupwire.py +0 -0
- {jetstream_api-0.20.3 → jetstream_api-0.21.3}/ilink/_qleader.py +0 -0
- {jetstream_api-0.20.3 → jetstream_api-0.21.3}/ilink/admin.py +0 -0
- {jetstream_api-0.20.3 → jetstream_api-0.21.3}/ilink/admintopic.py +0 -0
- {jetstream_api-0.20.3 → jetstream_api-0.21.3}/ilink/common.py +0 -0
- {jetstream_api-0.20.3 → jetstream_api-0.21.3}/ilink/connectivity.py +0 -0
- {jetstream_api-0.20.3 → jetstream_api-0.21.3}/ilink/exception.py +0 -0
- {jetstream_api-0.20.3 → jetstream_api-0.21.3}/ilink/msg.py +0 -0
- {jetstream_api-0.20.3 → jetstream_api-0.21.3}/ilink/partitioner.py +0 -0
- {jetstream_api-0.20.3 → jetstream_api-0.21.3}/ilink/properties.py +0 -0
- {jetstream_api-0.20.3 → jetstream_api-0.21.3}/ilink/py.typed +0 -0
- {jetstream_api-0.20.3 → jetstream_api-0.21.3}/ilink/util.py +0 -0
- {jetstream_api-0.20.3 → jetstream_api-0.21.3}/jetstream_api.egg-info/SOURCES.txt +0 -0
- {jetstream_api-0.20.3 → jetstream_api-0.21.3}/jetstream_api.egg-info/dependency_links.txt +0 -0
- {jetstream_api-0.20.3 → jetstream_api-0.21.3}/jetstream_api.egg-info/requires.txt +0 -0
- {jetstream_api-0.20.3 → jetstream_api-0.21.3}/jetstream_api.egg-info/top_level.txt +0 -0
- {jetstream_api-0.20.3 → jetstream_api-0.21.3}/setup.cfg +0 -0
- {jetstream_api-0.20.3 → jetstream_api-0.21.3}/tests/test_example.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: jetstream-api
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.21.3
|
|
4
4
|
Summary: Python client API for iLink M.O.M. (Message Oriented Middleware)
|
|
5
5
|
Author: snowjeans
|
|
6
6
|
License: Boost Software License - Version 1.0 - August 17th, 2003
|
|
@@ -60,6 +60,85 @@ lz4 배치 압축을 쓰려면 `pip install "jetstream-api[lz4]"`, zstd 배치
|
|
|
60
60
|
## 설치 방법
|
|
61
61
|
pip install jetstream-api
|
|
62
62
|
|
|
63
|
+
## 0.21.3 변경 - 메타데이터 재질의 '같은 응답' 지수 대기 (자바 v2.17.3 과 같음, 짝 엔진 7.0.1.3512 이상 - 리더 적재만은 7.0.1.3566 이상)
|
|
64
|
+
|
|
65
|
+
> 0.21.0 ~ 0.21.2 는 test.pypi 시험 판으로만 냈다. 아래 0.21.2 · 0.21.1 · 0.21.0 절도 이 판으로 처음 PyPI 에 나간다.
|
|
66
|
+
|
|
67
|
+
- **메타데이터 응답이 성공인데 진전이 없으면 재질의 간격이 는다**(사용자 결정 2026-10-05, Kafka 3.7 Metadata 와 같게): 리더 epoch 가
|
|
68
|
+
그대로인 응답이 이어지면 횟수를 세어 `retryBackoffMs` 부터 두 배씩 `retryBackoffMaxMs` 까지 늘린다(±20%). 처음 보는 / 더 큰 리더
|
|
69
|
+
epoch, epoch 없는 응답, 실패, 5분 넘은 메타데이터는 횟수를 지운다. 성공 뒤에도 다음 재질의는 `retryBackoffMs` 뒤다.
|
|
70
|
+
- producer: 리더 표(파티션 리더 epoch)와 링크별 토픽 메타데이터(토픽 epoch). 크기 초과 뒤 세그먼트 크기 재조회는 이제 봉투를 막지
|
|
71
|
+
않는다(거절된 배치는 쪼개 보낸다).
|
|
72
|
+
- 구독: 메타데이터 재조회(리더 바뀜 · 노드 끊김 뒤). 간격 안의 재조회는 건너뛰고 지금 메타데이터로 계속한다. 패턴 구독에 새
|
|
73
|
+
토픽이 맞으면 곧바로 묻는다.
|
|
74
|
+
|
|
75
|
+
## 0.21.2 변경 - 크기 초과 배치 쪼개기 · 재접속 지수 대기 (자바 v2.17.2 와 같음, 짝 엔진 7.0.1.3512 이상 - 리더 적재만은 7.0.1.3566 이상)
|
|
76
|
+
|
|
77
|
+
- **크기 초과로 거절된 배치는 쪼개 다시 보낸다**(사용자 지시 2026-10-05, Kafka 와 같게): 2건 이상 배치가
|
|
78
|
+
`MIMQE_TOPIC_MESSAGE_TOO_LARGE` 로 거절되면 레코드 경계에서 나눠 다시 보낸다. 쪼개기는 재시도 횟수를 쓰지 않고, 조각은 원래
|
|
79
|
+
배치의 전달 시한을 이어받는다. 1건짜리 배치는 종전처럼 실패한다. 조각은 레코드 바이트를 원래의 절반 이하로 잡아 늘 둘 이상으로
|
|
80
|
+
나뉜다 - 운영 중 토픽 세그먼트가 줄어 큰 배치가 거절될 때 맞을 때까지 쪼갠다(전에는 그 배치를 실패로 올렸다).
|
|
81
|
+
- 멱등이면 쪼갠 조각에 보낼 때 번호를 새로 매긴다(원래 배치의 번호는 돌려받는다). 결과를 모르는 시도가 있었던 멱등 배치는
|
|
82
|
+
쪼개지 않는다 - 앞 시도가 적재됐을 수 있어 종전처럼 `commit in doubt` 로 올린다.
|
|
83
|
+
- **재접속 대기가 지수로 는다**: 새 설정 `reconnectBackoffMs`(기본 50) / `reconnectBackoffMaxMs`(기본 1000), snake
|
|
84
|
+
`reconnect_backoff_ms` / `reconnect_backoff_max_ms`. 주소마다 잇달아 실패한 횟수 n 으로 첫 값 x 2^n 을 쉬고 상한에서 멈춘다(±20%).
|
|
85
|
+
연결에 성공하면 처음 값으로 돌아간다. 상한을 정하지 않고 첫 값만 정하면 상한도 그 값이다(고정 대기).
|
|
86
|
+
- producer: 부트스트랩 재접속, 파티션 리더 연결, 생성자의 첫 연결이 후보를 다시 도는 간격(전에는 100ms -> 1s 고정 배수).
|
|
87
|
+
- 구독(`ILSubscribeOptions` / `ILPatternOptions` 의 같은 이름 옵션): 끊긴 노드(파티션 리더 · 코디네이터) 연결. 쉬는 동안 그
|
|
88
|
+
노드의 파티션은 건너뛰고 다른 노드의 파티션을 읽는다. 전에는 다음 read 마다 곧바로 다시 열었다.
|
|
89
|
+
- **메타데이터 재질의 간격도 지수로**: 토픽 메타데이터(FLUSH 중)와 리더 표 재질의가 잇달아 실패하면 `retryBackoffMs` 부터 두 배씩
|
|
90
|
+
`retryBackoffMaxMs` 까지 늘린다. 성공하면 처음 값으로.
|
|
91
|
+
- 지수 대기의 지터: 지수를 상한에서 먼저 멈추고 흔든다 - 상한에 닿은 뒤의 대기는 상한의 0.8 ~ 1배다(0.21.1 은 상한으로 고정됐다).
|
|
92
|
+
|
|
93
|
+
## 0.21.1 변경 - acks=0 완결 시점 · retries 를 Kafka 와 같게 (자바 v2.17.1 과 같음, 짝 엔진 7.0.1.3512 이상 - 리더 적재만은 7.0.1.3566 이상)
|
|
94
|
+
|
|
95
|
+
- **acks=0 은 소켓에 다 쓴 뒤 완결한다**(사용자 지시 2026-10-05): 봉투의 마지막 바이트를 커널에 넘긴 뒤 future 가 성공(오프셋 -1)
|
|
96
|
+
으로 끝난다. 전에는 송신 큐에 넣는 순간 성공으로 끝나, 쓰기 전에 연결이 끊긴 배치도 성공으로 보였다. 다 쓰기 전에 끊긴 배치는
|
|
97
|
+
엔진이 온전히 받지 못한 것이라 **retries 안에서 다시 붙은 연결로 다시 보내고**, 예산을 다 쓰면
|
|
98
|
+
`MIMQE_BIO_ERROR (acks=0 batch not written ...)` 로 실패한다. `flush()` 는 그 배치가 다 쓰일 때까지 기다린다.
|
|
99
|
+
- **retries 를 Kafka 와 같게**:
|
|
100
|
+
- 멱등을 지정하지 않았으면 `retries(0)` 일 때도 멱등이 꺼진다(acks `"all"` · `maxInFlight <= 5` 에 더해). `enableIdempotence(True)`
|
|
101
|
+
를 명시하고 `retries(0)` 이면 생성이 `ValueError("enableIdempotence requires retries > 0")` 로 실패한다.
|
|
102
|
+
- **재시도 대기가 지수로 는다**(Kafka 3.7 KIP-580): 같은 배치의 재시도 대기가 `retryBackoffMs`(기본 100)에서 시작해 실패마다 두
|
|
103
|
+
배로 늘고 새 설정 `retryBackoffMaxMs`(기본 1000, snake `retry_backoff_max_ms`)에서 멈춘다. 매번 ±20% 를 흔든다.
|
|
104
|
+
`retryBackoffMs` 가 상한보다 크면 상한을 고정 대기로 쓴다. 전처럼 늘 같은 대기를 원하면 두 값을 같게 준다. 연결을 다시 잇는
|
|
105
|
+
간격 · 메타데이터를 다시 묻는 간격은 그대로 `retryBackoffMs` 다.
|
|
106
|
+
|
|
107
|
+
## 0.21.0 변경 - acks 를 Kafka 와 같은 뜻으로 (자바 v2.17.0 과 같음, 짝 엔진 7.0.1.3512 이상 - 리더 적재만은 7.0.1.3566 이상)
|
|
108
|
+
|
|
109
|
+
- ☠ **acks 를 Kafka 와 같은 뜻으로**(사용자 지시 2026-10-05, 엔진 7.0.1.3566 ACKSLEADER-1): `acks("all")` / `-1` = ISR 팔로워
|
|
110
|
+
확인(**기본**), `acks(1)` = **리더 적재만**, `acks(0)` = 무응답. 문자열 `"all"` · `"-1"` · `"1"` · `"0"` 도 받는다.
|
|
111
|
+
- **행동 변경**: 0.20.x 까지의 `acks(1)` 은 클러스터에서 팔로워 확인까지(지금의 `"all"`)였다. 이 판부터 `acks(1)` 을 **명시한**
|
|
112
|
+
코드는 리더 적재만으로 약해진다 - 예전과 같게 하려면 `"all"` 을 주거나 지정하지 않는다(기본 `"all"` 이라 지정하지 않은 코드는
|
|
113
|
+
그대로다). `get_acks()` 는 all 을 `-1` 로 돌려준다.
|
|
114
|
+
- 멱등 규칙도 Kafka 3.x 와 같다: 지정하지 않았으면 acks 가 `"all"` 일 때만 켜지고(`0` / `1` 이면 꺼짐), `enableIdempotence(True)`
|
|
115
|
+
를 명시하고 `"all"` 이 아니면 생성이 `ValueError("enableIdempotence requires acks=all")` 로 실패한다.
|
|
116
|
+
- 엔진: 리더만은 봉투 acks 바이트 2 로 싣는다. 7.0.1.3566 전 엔진은 2 를 all 로 읽는다(더 엄격). 리더만 · 0 은 토픽 MINSYNC 를
|
|
117
|
+
보지 않는다. 소비 가시성은 그대로 HW 뒤다.
|
|
118
|
+
|
|
119
|
+
## 0.20.6 변경 - 얼어붙은 노드를 건너뛰는 핸드셰이크 시한 · 승격 대기 (자바 v2.16.6 과 같음, 짝 엔진 7.0.1.3512 이상)
|
|
120
|
+
|
|
121
|
+
> 0.20.4 · 0.20.5 는 test.pypi 시험 판으로만 냈다(회귀 BOOTFROZEN-1). 0.20.4 는 얼린 리더를 건너뛴 뒤 팔로워가 승격 전이면 생성이
|
|
122
|
+
> 5초 만에 실패했고, 0.20.5 는 생성자가 승격 전에 끝나면 첫 send 가 얼린 옛 리더로 리더 링크를 열며 30초를 썼다.
|
|
123
|
+
|
|
124
|
+
- **얼어붙은 노드에서 부트스트랩이 30초를 통째로 기다리지 않는다**: 멈춘(SIGSTOP) · 얼어붙은 브로커는 커널이 TCP 연결을 받아
|
|
125
|
+
주므로 접속은 되고 첫 응답이 오지 않는다. 후보를 도는 접속(프로듀서 부트스트랩 · 재접속, `ILQmgr` 주소 목록 접속 ·
|
|
126
|
+
`reconnect` · 구독 연결 · 팔로워가 준 리더 힌트로 옮겨 붙기)에서 **다른 후보가 남아 있으면** TCP 접속과 첫 `NEW_CONNECTION`
|
|
127
|
+
응답을 핸드셰이크 시한(기본 5초)만 기다리고 다음 후보로 간다. 그 시한을 넘긴 노드는 그 순회 동안 응답 없음으로 쳐서 리더
|
|
128
|
+
힌트로도 다시 가지 않는다. 응답 없음이 아닌 다른 후보가 없으면(주소 하나로 붙는 접속 등) 종전처럼 요청 · 세션 시한을 다
|
|
129
|
+
기다린다 - 바쁜 브로커를 거짓 실패시키지 않는다. PID 발급 · 메타데이터 같은 그 뒤 요청은 종전 시한이다.
|
|
130
|
+
- 설정: `ILProducerConfig.handshakeTimeoutMs(ms)`(snake `handshake_timeout_ms`), `ILQmgr.set_handshake_timeout(ms)` /
|
|
131
|
+
`setHandshakeTimeout`. 0 이하면 끈다.
|
|
132
|
+
- 팔로워 하나로 접속했는데 그 팔로워가 얼어붙은 옛 리더를 힌트로 주는 경우(장애 순간), 힌트 노드에서 짧은 시한 뒤 원래
|
|
133
|
+
주소로 돌아간다(그사이 리더가 됐으면 붙는다). `ILQmgr` 의 단일 주소 접속도 힌트로 옮겨 붙을 때 원래 주소를 뒤 후보로 둔다.
|
|
134
|
+
- **승격 대기**: 리더가 얼어 있고 팔로워가 아직 승격 전이면(팔로워가 얼린 옛 리더를 힌트로 준다) 후보가 바닥난다. 프로듀서
|
|
135
|
+
생성은 `requestTimeoutMs` 안에서 100ms → 1초로 쉬며 다시 돌아 승격이 끝나면 새 리더에 붙는다(끝내 안 되면 종전과 같은
|
|
136
|
+
시간에 마지막 거절로 실패). `ILQmgr` 후보 순회는 2회전 고정이던 것을 세션 타임아웃 안에서 1초 간격으로 되풀이한다(3회전부터는
|
|
137
|
+
리더 힌트 · 연결 계열 실패일 때만). 응답 없음 표시는 회차마다 새로 시작하고, 확정 거절(권한 · 토픽 없음 등)은 다시 돌지 않는다.
|
|
138
|
+
- **파티션 리더 링크**: 리더 표가 얼린 옛 리더를 가리키면(승격 전) 링크 열기도 핸드셰이크 시한 뒤 그 주소만 쉬고 리더 표를
|
|
139
|
+
다시 받는다 - 승격 뒤 새 리더로 간다. 같은 주소에서 잇달아 3번 넘기면 다음 한 번은 `requestTimeoutMs` 로 열고(늘 느린 리더도
|
|
140
|
+
끝내 붙게), 그렇게 오래 여는 동안에도 리더 표를 1초 간격으로 다시 받아 표가 바뀌면 배치는 기다리지 않고 새 리더로 간다.
|
|
141
|
+
|
|
63
142
|
## 0.20.3 변경 - 토픽 파티션 격리본 재주입 (자바 v2.16.3 과 같음, 짝 엔진 7.0.1.3512 이상 - 토픽 격리본은 7.0.1.3553 이상)
|
|
64
143
|
|
|
65
144
|
- 격리본 토픽 파티션(엔진 7.0.1.3553 QUARTOOL-2): `reinjectQuarantine` 의 큐 인자를 선택으로 바꿨다 - 토픽 파티션 격리본은 큐를 비우고
|
|
@@ -10,6 +10,85 @@ lz4 배치 압축을 쓰려면 `pip install "jetstream-api[lz4]"`, zstd 배치
|
|
|
10
10
|
## 설치 방법
|
|
11
11
|
pip install jetstream-api
|
|
12
12
|
|
|
13
|
+
## 0.21.3 변경 - 메타데이터 재질의 '같은 응답' 지수 대기 (자바 v2.17.3 과 같음, 짝 엔진 7.0.1.3512 이상 - 리더 적재만은 7.0.1.3566 이상)
|
|
14
|
+
|
|
15
|
+
> 0.21.0 ~ 0.21.2 는 test.pypi 시험 판으로만 냈다. 아래 0.21.2 · 0.21.1 · 0.21.0 절도 이 판으로 처음 PyPI 에 나간다.
|
|
16
|
+
|
|
17
|
+
- **메타데이터 응답이 성공인데 진전이 없으면 재질의 간격이 는다**(사용자 결정 2026-10-05, Kafka 3.7 Metadata 와 같게): 리더 epoch 가
|
|
18
|
+
그대로인 응답이 이어지면 횟수를 세어 `retryBackoffMs` 부터 두 배씩 `retryBackoffMaxMs` 까지 늘린다(±20%). 처음 보는 / 더 큰 리더
|
|
19
|
+
epoch, epoch 없는 응답, 실패, 5분 넘은 메타데이터는 횟수를 지운다. 성공 뒤에도 다음 재질의는 `retryBackoffMs` 뒤다.
|
|
20
|
+
- producer: 리더 표(파티션 리더 epoch)와 링크별 토픽 메타데이터(토픽 epoch). 크기 초과 뒤 세그먼트 크기 재조회는 이제 봉투를 막지
|
|
21
|
+
않는다(거절된 배치는 쪼개 보낸다).
|
|
22
|
+
- 구독: 메타데이터 재조회(리더 바뀜 · 노드 끊김 뒤). 간격 안의 재조회는 건너뛰고 지금 메타데이터로 계속한다. 패턴 구독에 새
|
|
23
|
+
토픽이 맞으면 곧바로 묻는다.
|
|
24
|
+
|
|
25
|
+
## 0.21.2 변경 - 크기 초과 배치 쪼개기 · 재접속 지수 대기 (자바 v2.17.2 와 같음, 짝 엔진 7.0.1.3512 이상 - 리더 적재만은 7.0.1.3566 이상)
|
|
26
|
+
|
|
27
|
+
- **크기 초과로 거절된 배치는 쪼개 다시 보낸다**(사용자 지시 2026-10-05, Kafka 와 같게): 2건 이상 배치가
|
|
28
|
+
`MIMQE_TOPIC_MESSAGE_TOO_LARGE` 로 거절되면 레코드 경계에서 나눠 다시 보낸다. 쪼개기는 재시도 횟수를 쓰지 않고, 조각은 원래
|
|
29
|
+
배치의 전달 시한을 이어받는다. 1건짜리 배치는 종전처럼 실패한다. 조각은 레코드 바이트를 원래의 절반 이하로 잡아 늘 둘 이상으로
|
|
30
|
+
나뉜다 - 운영 중 토픽 세그먼트가 줄어 큰 배치가 거절될 때 맞을 때까지 쪼갠다(전에는 그 배치를 실패로 올렸다).
|
|
31
|
+
- 멱등이면 쪼갠 조각에 보낼 때 번호를 새로 매긴다(원래 배치의 번호는 돌려받는다). 결과를 모르는 시도가 있었던 멱등 배치는
|
|
32
|
+
쪼개지 않는다 - 앞 시도가 적재됐을 수 있어 종전처럼 `commit in doubt` 로 올린다.
|
|
33
|
+
- **재접속 대기가 지수로 는다**: 새 설정 `reconnectBackoffMs`(기본 50) / `reconnectBackoffMaxMs`(기본 1000), snake
|
|
34
|
+
`reconnect_backoff_ms` / `reconnect_backoff_max_ms`. 주소마다 잇달아 실패한 횟수 n 으로 첫 값 x 2^n 을 쉬고 상한에서 멈춘다(±20%).
|
|
35
|
+
연결에 성공하면 처음 값으로 돌아간다. 상한을 정하지 않고 첫 값만 정하면 상한도 그 값이다(고정 대기).
|
|
36
|
+
- producer: 부트스트랩 재접속, 파티션 리더 연결, 생성자의 첫 연결이 후보를 다시 도는 간격(전에는 100ms -> 1s 고정 배수).
|
|
37
|
+
- 구독(`ILSubscribeOptions` / `ILPatternOptions` 의 같은 이름 옵션): 끊긴 노드(파티션 리더 · 코디네이터) 연결. 쉬는 동안 그
|
|
38
|
+
노드의 파티션은 건너뛰고 다른 노드의 파티션을 읽는다. 전에는 다음 read 마다 곧바로 다시 열었다.
|
|
39
|
+
- **메타데이터 재질의 간격도 지수로**: 토픽 메타데이터(FLUSH 중)와 리더 표 재질의가 잇달아 실패하면 `retryBackoffMs` 부터 두 배씩
|
|
40
|
+
`retryBackoffMaxMs` 까지 늘린다. 성공하면 처음 값으로.
|
|
41
|
+
- 지수 대기의 지터: 지수를 상한에서 먼저 멈추고 흔든다 - 상한에 닿은 뒤의 대기는 상한의 0.8 ~ 1배다(0.21.1 은 상한으로 고정됐다).
|
|
42
|
+
|
|
43
|
+
## 0.21.1 변경 - acks=0 완결 시점 · retries 를 Kafka 와 같게 (자바 v2.17.1 과 같음, 짝 엔진 7.0.1.3512 이상 - 리더 적재만은 7.0.1.3566 이상)
|
|
44
|
+
|
|
45
|
+
- **acks=0 은 소켓에 다 쓴 뒤 완결한다**(사용자 지시 2026-10-05): 봉투의 마지막 바이트를 커널에 넘긴 뒤 future 가 성공(오프셋 -1)
|
|
46
|
+
으로 끝난다. 전에는 송신 큐에 넣는 순간 성공으로 끝나, 쓰기 전에 연결이 끊긴 배치도 성공으로 보였다. 다 쓰기 전에 끊긴 배치는
|
|
47
|
+
엔진이 온전히 받지 못한 것이라 **retries 안에서 다시 붙은 연결로 다시 보내고**, 예산을 다 쓰면
|
|
48
|
+
`MIMQE_BIO_ERROR (acks=0 batch not written ...)` 로 실패한다. `flush()` 는 그 배치가 다 쓰일 때까지 기다린다.
|
|
49
|
+
- **retries 를 Kafka 와 같게**:
|
|
50
|
+
- 멱등을 지정하지 않았으면 `retries(0)` 일 때도 멱등이 꺼진다(acks `"all"` · `maxInFlight <= 5` 에 더해). `enableIdempotence(True)`
|
|
51
|
+
를 명시하고 `retries(0)` 이면 생성이 `ValueError("enableIdempotence requires retries > 0")` 로 실패한다.
|
|
52
|
+
- **재시도 대기가 지수로 는다**(Kafka 3.7 KIP-580): 같은 배치의 재시도 대기가 `retryBackoffMs`(기본 100)에서 시작해 실패마다 두
|
|
53
|
+
배로 늘고 새 설정 `retryBackoffMaxMs`(기본 1000, snake `retry_backoff_max_ms`)에서 멈춘다. 매번 ±20% 를 흔든다.
|
|
54
|
+
`retryBackoffMs` 가 상한보다 크면 상한을 고정 대기로 쓴다. 전처럼 늘 같은 대기를 원하면 두 값을 같게 준다. 연결을 다시 잇는
|
|
55
|
+
간격 · 메타데이터를 다시 묻는 간격은 그대로 `retryBackoffMs` 다.
|
|
56
|
+
|
|
57
|
+
## 0.21.0 변경 - acks 를 Kafka 와 같은 뜻으로 (자바 v2.17.0 과 같음, 짝 엔진 7.0.1.3512 이상 - 리더 적재만은 7.0.1.3566 이상)
|
|
58
|
+
|
|
59
|
+
- ☠ **acks 를 Kafka 와 같은 뜻으로**(사용자 지시 2026-10-05, 엔진 7.0.1.3566 ACKSLEADER-1): `acks("all")` / `-1` = ISR 팔로워
|
|
60
|
+
확인(**기본**), `acks(1)` = **리더 적재만**, `acks(0)` = 무응답. 문자열 `"all"` · `"-1"` · `"1"` · `"0"` 도 받는다.
|
|
61
|
+
- **행동 변경**: 0.20.x 까지의 `acks(1)` 은 클러스터에서 팔로워 확인까지(지금의 `"all"`)였다. 이 판부터 `acks(1)` 을 **명시한**
|
|
62
|
+
코드는 리더 적재만으로 약해진다 - 예전과 같게 하려면 `"all"` 을 주거나 지정하지 않는다(기본 `"all"` 이라 지정하지 않은 코드는
|
|
63
|
+
그대로다). `get_acks()` 는 all 을 `-1` 로 돌려준다.
|
|
64
|
+
- 멱등 규칙도 Kafka 3.x 와 같다: 지정하지 않았으면 acks 가 `"all"` 일 때만 켜지고(`0` / `1` 이면 꺼짐), `enableIdempotence(True)`
|
|
65
|
+
를 명시하고 `"all"` 이 아니면 생성이 `ValueError("enableIdempotence requires acks=all")` 로 실패한다.
|
|
66
|
+
- 엔진: 리더만은 봉투 acks 바이트 2 로 싣는다. 7.0.1.3566 전 엔진은 2 를 all 로 읽는다(더 엄격). 리더만 · 0 은 토픽 MINSYNC 를
|
|
67
|
+
보지 않는다. 소비 가시성은 그대로 HW 뒤다.
|
|
68
|
+
|
|
69
|
+
## 0.20.6 변경 - 얼어붙은 노드를 건너뛰는 핸드셰이크 시한 · 승격 대기 (자바 v2.16.6 과 같음, 짝 엔진 7.0.1.3512 이상)
|
|
70
|
+
|
|
71
|
+
> 0.20.4 · 0.20.5 는 test.pypi 시험 판으로만 냈다(회귀 BOOTFROZEN-1). 0.20.4 는 얼린 리더를 건너뛴 뒤 팔로워가 승격 전이면 생성이
|
|
72
|
+
> 5초 만에 실패했고, 0.20.5 는 생성자가 승격 전에 끝나면 첫 send 가 얼린 옛 리더로 리더 링크를 열며 30초를 썼다.
|
|
73
|
+
|
|
74
|
+
- **얼어붙은 노드에서 부트스트랩이 30초를 통째로 기다리지 않는다**: 멈춘(SIGSTOP) · 얼어붙은 브로커는 커널이 TCP 연결을 받아
|
|
75
|
+
주므로 접속은 되고 첫 응답이 오지 않는다. 후보를 도는 접속(프로듀서 부트스트랩 · 재접속, `ILQmgr` 주소 목록 접속 ·
|
|
76
|
+
`reconnect` · 구독 연결 · 팔로워가 준 리더 힌트로 옮겨 붙기)에서 **다른 후보가 남아 있으면** TCP 접속과 첫 `NEW_CONNECTION`
|
|
77
|
+
응답을 핸드셰이크 시한(기본 5초)만 기다리고 다음 후보로 간다. 그 시한을 넘긴 노드는 그 순회 동안 응답 없음으로 쳐서 리더
|
|
78
|
+
힌트로도 다시 가지 않는다. 응답 없음이 아닌 다른 후보가 없으면(주소 하나로 붙는 접속 등) 종전처럼 요청 · 세션 시한을 다
|
|
79
|
+
기다린다 - 바쁜 브로커를 거짓 실패시키지 않는다. PID 발급 · 메타데이터 같은 그 뒤 요청은 종전 시한이다.
|
|
80
|
+
- 설정: `ILProducerConfig.handshakeTimeoutMs(ms)`(snake `handshake_timeout_ms`), `ILQmgr.set_handshake_timeout(ms)` /
|
|
81
|
+
`setHandshakeTimeout`. 0 이하면 끈다.
|
|
82
|
+
- 팔로워 하나로 접속했는데 그 팔로워가 얼어붙은 옛 리더를 힌트로 주는 경우(장애 순간), 힌트 노드에서 짧은 시한 뒤 원래
|
|
83
|
+
주소로 돌아간다(그사이 리더가 됐으면 붙는다). `ILQmgr` 의 단일 주소 접속도 힌트로 옮겨 붙을 때 원래 주소를 뒤 후보로 둔다.
|
|
84
|
+
- **승격 대기**: 리더가 얼어 있고 팔로워가 아직 승격 전이면(팔로워가 얼린 옛 리더를 힌트로 준다) 후보가 바닥난다. 프로듀서
|
|
85
|
+
생성은 `requestTimeoutMs` 안에서 100ms → 1초로 쉬며 다시 돌아 승격이 끝나면 새 리더에 붙는다(끝내 안 되면 종전과 같은
|
|
86
|
+
시간에 마지막 거절로 실패). `ILQmgr` 후보 순회는 2회전 고정이던 것을 세션 타임아웃 안에서 1초 간격으로 되풀이한다(3회전부터는
|
|
87
|
+
리더 힌트 · 연결 계열 실패일 때만). 응답 없음 표시는 회차마다 새로 시작하고, 확정 거절(권한 · 토픽 없음 등)은 다시 돌지 않는다.
|
|
88
|
+
- **파티션 리더 링크**: 리더 표가 얼린 옛 리더를 가리키면(승격 전) 링크 열기도 핸드셰이크 시한 뒤 그 주소만 쉬고 리더 표를
|
|
89
|
+
다시 받는다 - 승격 뒤 새 리더로 간다. 같은 주소에서 잇달아 3번 넘기면 다음 한 번은 `requestTimeoutMs` 로 열고(늘 느린 리더도
|
|
90
|
+
끝내 붙게), 그렇게 오래 여는 동안에도 리더 표를 1초 간격으로 다시 받아 표가 바뀌면 배치는 기다리지 않고 새 리더로 간다.
|
|
91
|
+
|
|
13
92
|
## 0.20.3 변경 - 토픽 파티션 격리본 재주입 (자바 v2.16.3 과 같음, 짝 엔진 7.0.1.3512 이상 - 토픽 격리본은 7.0.1.3553 이상)
|
|
14
93
|
|
|
15
94
|
- 격리본 토픽 파티션(엔진 7.0.1.3553 QUARTOOL-2): `reinjectQuarantine` 의 큐 인자를 선택으로 바꿨다 - 토픽 파티션 격리본은 큐를 비우고
|
|
@@ -69,7 +69,8 @@ class GroupConsumer:
|
|
|
69
69
|
|
|
70
70
|
def __init__(self, qmgr, group: str, topics, *, commit_mode: int = COMMIT_AUTO, durable: bool = True,
|
|
71
71
|
start_mode: str = START_LATEST, poll_interval_ms: int = 0, auto_commit_ms: int = 0,
|
|
72
|
-
manual=None, on_assign=None, make_msg=None, on_topic_error=None
|
|
72
|
+
manual=None, on_assign=None, make_msg=None, on_topic_error=None, reconnect_backoff_ms: int = 50,
|
|
73
|
+
reconnect_backoff_max_ms: int = 1000) -> None:
|
|
73
74
|
"""멤버를 만든다. 연결은 :meth:`start` 가 연다.
|
|
74
75
|
|
|
75
76
|
Args:
|
|
@@ -86,6 +87,8 @@ class GroupConsumer:
|
|
|
86
87
|
make_msg (Callable): 레코드 객체 생성자(ILTopicMsg).
|
|
87
88
|
on_topic_error (Callable | None): 주면 FETCH 의 토픽 오류(상태 9)를 올리지 않고 ``(토픽, 사유)`` 로 부른다
|
|
88
89
|
(패턴 구독이 그 토픽을 떼어 낸다).
|
|
90
|
+
reconnect_backoff_ms (int): 끊긴 노드 연결을 다시 열기 전 첫 대기(밀리초, 0.21.2). 노드마다 두 배씩 는다.
|
|
91
|
+
reconnect_backoff_max_ms (int): 그 상한(밀리초).
|
|
89
92
|
"""
|
|
90
93
|
self.qmgr = qmgr
|
|
91
94
|
self.group = group
|
|
@@ -125,6 +128,13 @@ class GroupConsumer:
|
|
|
125
128
|
self._hb_thread = None
|
|
126
129
|
self._hb_stop = threading.Event()
|
|
127
130
|
self._hb_err = None # 하트비트 스레드가 본 치명 오류(다음 read 가 올린다)
|
|
131
|
+
# 0.21.2: 노드 재연결 대기 - "host:port" -> [잇달아 실패한 횟수, 다시 열 수 있는 시각(monotonic)]. 열리면 지운다
|
|
132
|
+
self._rc_base = max(0, reconnect_backoff_ms) / 1000.0
|
|
133
|
+
self._rc_max = max(0, reconnect_backoff_max_ms) / 1000.0
|
|
134
|
+
self._rc = {}
|
|
135
|
+
# 0.21.3: 메타데이터 재질의 간격(실패 + 진전 없는 같은 응답 - 파티션 리더 epoch 로 가른다)
|
|
136
|
+
from .producer import _MetaPace
|
|
137
|
+
self._meta_pace = _MetaPace(_BACKOFF_S, _BACKOFF_MAX_S)
|
|
128
138
|
|
|
129
139
|
# ── 연결 ─────────────────────────────────────────────────────────────
|
|
130
140
|
def start(self) -> None:
|
|
@@ -165,16 +175,40 @@ class GroupConsumer:
|
|
|
165
175
|
return self._boot
|
|
166
176
|
s = self._sessions.get(addr)
|
|
167
177
|
if s is None:
|
|
168
|
-
|
|
178
|
+
# 0.21.2: 그 노드가 재연결 대기 중이면 남은 시간을 쉬고 연다(FETCH 는 _rc_left 로 먼저 건너뛴다)
|
|
179
|
+
left = self._rc_left(addr)
|
|
180
|
+
if left > 0:
|
|
181
|
+
time.sleep(left)
|
|
182
|
+
try:
|
|
183
|
+
s = self.qmgr._open_sub_session(addr)
|
|
184
|
+
except Exception:
|
|
185
|
+
self._rc_down(addr)
|
|
186
|
+
raise
|
|
187
|
+
self._rc.pop(addr, None)
|
|
169
188
|
self._sessions[addr] = s
|
|
170
189
|
return s
|
|
171
190
|
|
|
191
|
+
def _rc_left(self, addr: str | None) -> float:
|
|
192
|
+
st = self._rc.get(addr) if addr is not None else None
|
|
193
|
+
return max(0.0, st[1] - time.monotonic()) if st is not None else 0.0
|
|
194
|
+
|
|
195
|
+
def _rc_down(self, addr: str | None) -> None:
|
|
196
|
+
# 0.21.2: 노드 연결이 끊겼거나 열리지 않았다 - 다음 열기를 첫 대기 x 2^(잇달아 실패한 횟수)(상한, ±20%) 뒤로 미룬다
|
|
197
|
+
if addr is None:
|
|
198
|
+
return
|
|
199
|
+
from .producer import _exp_backoff
|
|
200
|
+
st = self._rc.get(addr) or [0, 0.0]
|
|
201
|
+
st[1] = time.monotonic() + _exp_backoff(self._rc_base, self._rc_max, st[0])
|
|
202
|
+
st[0] += 1
|
|
203
|
+
self._rc[addr] = st
|
|
204
|
+
|
|
172
205
|
def _drop_session(self, addr: str | None) -> None:
|
|
173
206
|
if addr is None:
|
|
174
207
|
return
|
|
175
208
|
s = self._sessions.pop(addr, None)
|
|
176
209
|
if s is not None and s is not self._boot:
|
|
177
210
|
_disconnect_quietly(s)
|
|
211
|
+
self._rc_down(addr)
|
|
178
212
|
|
|
179
213
|
def _close_sessions(self) -> None:
|
|
180
214
|
for s in list(self._sessions.values()):
|
|
@@ -196,23 +230,35 @@ class GroupConsumer:
|
|
|
196
230
|
return self._call(sess, ILMsgUtil.create_request_msg_with_ext(code, ext, a1, a2, a3))
|
|
197
231
|
|
|
198
232
|
# ── 메타데이터 ───────────────────────────────────────────────────────
|
|
199
|
-
def _refresh_meta(self) -> None:
|
|
233
|
+
def _refresh_meta(self, new_topics: bool = False) -> None:
|
|
200
234
|
# 3397 + "P" + 토픽 목록. 엔진이 모르면(REPORT 거절) 메타데이터 없이 모든 파티션을 boot 가 맡는다고 본다
|
|
235
|
+
# 0.21.3: 재질의 간격 - 마지막 응답 뒤 retryBackoffMs 규칙의 대기가 지나기 전이면 건너뛴다(지금 메타데이터로 계속한다).
|
|
236
|
+
# 실패와 진전 없는 같은 응답(리더 epoch 그대로)이 이어지면 간격이 는다. 새 토픽(패턴)이면 곧바로 묻는다
|
|
201
237
|
if not self.topics:
|
|
202
238
|
self._meta = {}
|
|
203
239
|
return
|
|
240
|
+
pace = self._meta_pace
|
|
241
|
+
if new_topics:
|
|
242
|
+
pace.new_topics()
|
|
243
|
+
now = time.monotonic()
|
|
244
|
+
if self._meta is not None and not pace.allowed(now):
|
|
245
|
+
return
|
|
204
246
|
try:
|
|
205
247
|
res = self._request(self._boot, ILC.MIMQ_TOPIC_METADATA, G.enc_topics(self.topics), self.topics[0], "P", "")
|
|
206
|
-
except
|
|
248
|
+
except Exception:
|
|
249
|
+
pace.failed(time.monotonic())
|
|
207
250
|
raise
|
|
208
251
|
if res.getType() == ILC.MIMQ_LONG_REPORT_MESSAGE:
|
|
209
252
|
topics, nodes = G.parse_metadata(_text(res))
|
|
210
253
|
with self._lock:
|
|
211
254
|
self._meta = topics
|
|
212
255
|
self._nodes = nodes
|
|
256
|
+
pace.ok(time.monotonic(), [((t, p), pm.leader_epoch if pm.leader >= 0 else -1)
|
|
257
|
+
for t, tm in topics.items() for p, pm in tm.partitions.items()])
|
|
213
258
|
else:
|
|
214
259
|
with self._lock:
|
|
215
260
|
self._meta = None
|
|
261
|
+
pace.ok(time.monotonic(), [("-", -1)])
|
|
216
262
|
|
|
217
263
|
def _leader(self, topic: str, p: int):
|
|
218
264
|
# (주소 또는 None=boot, leaderEpoch)
|
|
@@ -266,7 +312,7 @@ class GroupConsumer:
|
|
|
266
312
|
# 둘 다 쉬었다가(_BACKOFF_S 부터 두 배씩, _BACKOFF_MAX_S 까지) _COORD_WAIT_S 안에서만 다시 한다. 한도를 넘기면 None - 부른 쪽이
|
|
267
313
|
# MIMQE_NOT_AVAILABLE (group coordinator for <g>) 로 올린다(4175 / 4176 은 앱에 보이지 않는다)
|
|
268
314
|
deadline = time.monotonic() + _COORD_WAIT_S
|
|
269
|
-
|
|
315
|
+
tries = 0
|
|
270
316
|
while True:
|
|
271
317
|
try:
|
|
272
318
|
sess = self._coord_session()
|
|
@@ -289,8 +335,10 @@ class GroupConsumer:
|
|
|
289
335
|
remain = deadline - time.monotonic()
|
|
290
336
|
if remain <= 0:
|
|
291
337
|
return None
|
|
292
|
-
|
|
293
|
-
|
|
338
|
+
# 0.21.2: retryBackoffMs 규칙과 같은 지수 대기(두 배씩, 상한, ±20%)
|
|
339
|
+
from .producer import _exp_backoff
|
|
340
|
+
time.sleep(min(_exp_backoff(_BACKOFF_S, _BACKOFF_MAX_S, tries), remain))
|
|
341
|
+
tries += 1
|
|
294
342
|
|
|
295
343
|
def _join(self) -> None:
|
|
296
344
|
# 가입(재가입). AUTO 면 먼저 건넨 좌표를 동기 커밋한다(재배정으로 넘어갈 파티션의 진행을 잃지 않게)
|
|
@@ -527,6 +575,13 @@ class GroupConsumer:
|
|
|
527
575
|
return False
|
|
528
576
|
any_data = False
|
|
529
577
|
items = list(groups.items())
|
|
578
|
+
# 0.21.2: 재연결 대기 중인 노드는 이번 바퀴에 건너뛴다 - 다른 노드의 파티션을 읽고, 모두 대기 중이면 남은 시간만 쉰다
|
|
579
|
+
waiting = [self._rc_left(a) for a, _ in items if a is not None and a not in self._sessions]
|
|
580
|
+
items = [(a, t) for a, t in items if a is None or a in self._sessions or self._rc_left(a) <= 0]
|
|
581
|
+
if not items:
|
|
582
|
+
if wait_ms > 0:
|
|
583
|
+
time.sleep(min(wait_ms / 1000.0, min(x for x in waiting if x > 0)))
|
|
584
|
+
return False
|
|
530
585
|
for i, (addr, topics) in enumerate(items):
|
|
531
586
|
w = wait_ms if i == len(items) - 1 else 0
|
|
532
587
|
req = ILMsg(ILC.MIMQ_TOPIC_FETCH_V2_MESSAGE,
|
|
@@ -811,7 +866,7 @@ class GroupConsumer:
|
|
|
811
866
|
self.topics = new
|
|
812
867
|
self._need_join = self.manual is None
|
|
813
868
|
if self._boot is not None:
|
|
814
|
-
self._refresh_meta()
|
|
869
|
+
self._refresh_meta(new_topics=True)
|
|
815
870
|
return True
|
|
816
871
|
|
|
817
872
|
# ── 종료 ─────────────────────────────────────────────────────────────
|
|
@@ -38,7 +38,7 @@ class ILPatternOptions:
|
|
|
38
38
|
_OPTIONS = frozenset((
|
|
39
39
|
"startMode", "newTopicStartMode", "durable", "commitMode", "refreshMs",
|
|
40
40
|
"maxTopics", "bufferPerTopic", "prefetchCount",
|
|
41
|
-
"onTopicsChanged", "onTopicFailed",
|
|
41
|
+
"onTopicsChanged", "onTopicFailed", "reconnectBackoffMs", "reconnectBackoffMaxMs",
|
|
42
42
|
))
|
|
43
43
|
|
|
44
44
|
def __init__(self) -> None:
|
|
@@ -55,6 +55,11 @@ class ILPatternOptions:
|
|
|
55
55
|
self._prefetchCount = 0
|
|
56
56
|
self._onTopicsChanged = None # callable(added, removed, current)
|
|
57
57
|
self._onTopicFailed = None # callable(topic, exception)
|
|
58
|
+
#: 0.21.2: 노드 재연결 대기(첫 값 / 상한, 밀리초)
|
|
59
|
+
self._reconnectBackoffMs = 50
|
|
60
|
+
self._reconnectBackoffMaxMs = 1000
|
|
61
|
+
self._reconnectBackoffSet = False
|
|
62
|
+
self._reconnectBackoffMaxSet = False
|
|
58
63
|
|
|
59
64
|
def __setattr__(self, name: str, value) -> None:
|
|
60
65
|
# 구버전 호환: opt.maxTopics = 8 같은 직접 대입을 내부 슬롯으로 돌린다.
|
|
@@ -210,6 +215,67 @@ class ILPatternOptions:
|
|
|
210
215
|
self._onTopicsChanged = fn; return self
|
|
211
216
|
|
|
212
217
|
# ── 빌더 (snake 별칭) ───────────────────────────────────────────────
|
|
218
|
+
def reconnectBackoffMs(self, ms: int) -> "ILPatternOptions":
|
|
219
|
+
"""끊긴 노드 연결(파티션 리더 · 코디네이터)을 다시 열기 전 첫 대기(밀리초). (기본 50, 0.21.2)
|
|
220
|
+
|
|
221
|
+
노드마다 잇달아 실패한 횟수 n 을 세어 이 값 x 2^n 을 쉬고 :meth:`reconnectBackoffMaxMs` 에서 멈춘다(±20% 흔듦).
|
|
222
|
+
연결에 성공하면 처음 값으로 돌아간다. 쉬는 동안 그 노드의 파티션은 건너뛰고 다른 노드의 파티션을 읽는다.
|
|
223
|
+
전에는 끊긴 노드를 다음 read 마다 곧바로 다시 열었다.
|
|
224
|
+
|
|
225
|
+
Args:
|
|
226
|
+
ms (int): 첫 대기(밀리초). **음수는 0 으로 보정된다.**
|
|
227
|
+
|
|
228
|
+
Returns:
|
|
229
|
+
ILPatternOptions: 체이닝을 위해 자기 자신.
|
|
230
|
+
"""
|
|
231
|
+
self._reconnectBackoffMs = 0 if ms < 0 else ms
|
|
232
|
+
self._reconnectBackoffSet = True
|
|
233
|
+
return self
|
|
234
|
+
|
|
235
|
+
def reconnectBackoffMaxMs(self, ms: int) -> "ILPatternOptions":
|
|
236
|
+
"""노드 재연결 대기의 상한(밀리초). (기본 1000, 0.21.2)
|
|
237
|
+
|
|
238
|
+
정하지 않고 :meth:`reconnectBackoffMs` 만 정하면 이 값도 그 값이 된다(늘리지 않는 고정 대기).
|
|
239
|
+
|
|
240
|
+
Args:
|
|
241
|
+
ms (int): 상한(밀리초). **음수는 0 으로 보정된다.**
|
|
242
|
+
|
|
243
|
+
Returns:
|
|
244
|
+
ILPatternOptions: 체이닝을 위해 자기 자신.
|
|
245
|
+
"""
|
|
246
|
+
self._reconnectBackoffMaxMs = 0 if ms < 0 else ms
|
|
247
|
+
self._reconnectBackoffMaxSet = True
|
|
248
|
+
return self
|
|
249
|
+
|
|
250
|
+
def reconnect_backoff_ms(self, ms: int) -> "ILPatternOptions":
|
|
251
|
+
""":meth:`reconnectBackoffMs` 의 snake 별칭."""
|
|
252
|
+
return self.reconnectBackoffMs(ms)
|
|
253
|
+
|
|
254
|
+
def reconnect_backoff_max_ms(self, ms: int) -> "ILPatternOptions":
|
|
255
|
+
""":meth:`reconnectBackoffMaxMs` 의 snake 별칭."""
|
|
256
|
+
return self.reconnectBackoffMaxMs(ms)
|
|
257
|
+
|
|
258
|
+
def get_reconnect_backoff_ms(self) -> int:
|
|
259
|
+
"""노드 재연결 첫 대기(밀리초). 기본 50.
|
|
260
|
+
|
|
261
|
+
Returns:
|
|
262
|
+
int: 첫 대기(밀리초).
|
|
263
|
+
"""
|
|
264
|
+
return self._reconnectBackoffMs
|
|
265
|
+
|
|
266
|
+
def get_reconnect_backoff_max_ms(self) -> int:
|
|
267
|
+
"""노드 재연결 대기의 상한(밀리초). 기본 1000. 정하지 않고 :meth:`reconnectBackoffMs` 만 정했으면 그 값이다.
|
|
268
|
+
|
|
269
|
+
Returns:
|
|
270
|
+
int: 상한(밀리초).
|
|
271
|
+
"""
|
|
272
|
+
if not self._reconnectBackoffMaxSet and self._reconnectBackoffSet:
|
|
273
|
+
return self._reconnectBackoffMs
|
|
274
|
+
return self._reconnectBackoffMaxMs
|
|
275
|
+
|
|
276
|
+
getReconnectBackoffMs = get_reconnect_backoff_ms
|
|
277
|
+
getReconnectBackoffMaxMs = get_reconnect_backoff_max_ms
|
|
278
|
+
|
|
213
279
|
def start_mode(self, mode: str) -> "ILPatternOptions":
|
|
214
280
|
""":meth:`startMode` 의 snake 별칭."""
|
|
215
281
|
return self.startMode(mode)
|
|
@@ -547,7 +613,8 @@ class ILPatternSubscription:
|
|
|
547
613
|
self._c = GroupConsumer(
|
|
548
614
|
qmgr, subscription_name, [], commit_mode=o.get_commit_mode(), durable=o.is_durable(),
|
|
549
615
|
start_mode=o.get_start_mode() or ILTopic.START_LATEST, make_msg=ILTopicMsg,
|
|
550
|
-
on_topic_error=self._topic_error)
|
|
616
|
+
on_topic_error=self._topic_error, reconnect_backoff_ms=o.get_reconnect_backoff_ms(),
|
|
617
|
+
reconnect_backoff_max_ms=o.get_reconnect_backoff_max_ms())
|
|
551
618
|
try:
|
|
552
619
|
self._c.open()
|
|
553
620
|
self._refresh(initial=True)
|