jetstream-api 0.22.1__tar.gz → 0.22.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.
Files changed (32) hide show
  1. {jetstream_api-0.22.1 → jetstream_api-0.22.3}/PKG-INFO +19 -2
  2. {jetstream_api-0.22.1 → jetstream_api-0.22.3}/README.md +18 -1
  3. {jetstream_api-0.22.1 → jetstream_api-0.22.3}/ilink/__init__.py +1 -1
  4. {jetstream_api-0.22.1 → jetstream_api-0.22.3}/ilink/common.py +4 -10
  5. {jetstream_api-0.22.1 → jetstream_api-0.22.3}/ilink/properties.py +46 -420
  6. {jetstream_api-0.22.1 → jetstream_api-0.22.3}/jetstream_api.egg-info/PKG-INFO +19 -2
  7. {jetstream_api-0.22.1 → jetstream_api-0.22.3}/pyproject.toml +1 -1
  8. {jetstream_api-0.22.1 → jetstream_api-0.22.3}/LICENSE.txt +0 -0
  9. {jetstream_api-0.22.1 → jetstream_api-0.22.3}/ilink/_codec.py +0 -0
  10. {jetstream_api-0.22.1 → jetstream_api-0.22.3}/ilink/_consumer.py +0 -0
  11. {jetstream_api-0.22.1 → jetstream_api-0.22.3}/ilink/_groupwire.py +0 -0
  12. {jetstream_api-0.22.1 → jetstream_api-0.22.3}/ilink/_propv2.py +0 -0
  13. {jetstream_api-0.22.1 → jetstream_api-0.22.3}/ilink/_qleader.py +0 -0
  14. {jetstream_api-0.22.1 → jetstream_api-0.22.3}/ilink/_throttle.py +0 -0
  15. {jetstream_api-0.22.1 → jetstream_api-0.22.3}/ilink/admin.py +0 -0
  16. {jetstream_api-0.22.1 → jetstream_api-0.22.3}/ilink/admintopic.py +0 -0
  17. {jetstream_api-0.22.1 → jetstream_api-0.22.3}/ilink/connectivity.py +0 -0
  18. {jetstream_api-0.22.1 → jetstream_api-0.22.3}/ilink/exception.py +0 -0
  19. {jetstream_api-0.22.1 → jetstream_api-0.22.3}/ilink/msg.py +0 -0
  20. {jetstream_api-0.22.1 → jetstream_api-0.22.3}/ilink/partitioner.py +0 -0
  21. {jetstream_api-0.22.1 → jetstream_api-0.22.3}/ilink/pattern.py +0 -0
  22. {jetstream_api-0.22.1 → jetstream_api-0.22.3}/ilink/producer.py +0 -0
  23. {jetstream_api-0.22.1 → jetstream_api-0.22.3}/ilink/py.typed +0 -0
  24. {jetstream_api-0.22.1 → jetstream_api-0.22.3}/ilink/qmgr.py +0 -0
  25. {jetstream_api-0.22.1 → jetstream_api-0.22.3}/ilink/topic.py +0 -0
  26. {jetstream_api-0.22.1 → jetstream_api-0.22.3}/ilink/util.py +0 -0
  27. {jetstream_api-0.22.1 → jetstream_api-0.22.3}/jetstream_api.egg-info/SOURCES.txt +0 -0
  28. {jetstream_api-0.22.1 → jetstream_api-0.22.3}/jetstream_api.egg-info/dependency_links.txt +0 -0
  29. {jetstream_api-0.22.1 → jetstream_api-0.22.3}/jetstream_api.egg-info/requires.txt +0 -0
  30. {jetstream_api-0.22.1 → jetstream_api-0.22.3}/jetstream_api.egg-info/top_level.txt +0 -0
  31. {jetstream_api-0.22.1 → jetstream_api-0.22.3}/setup.cfg +0 -0
  32. {jetstream_api-0.22.1 → jetstream_api-0.22.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.22.1
3
+ Version: 0.22.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,12 +60,29 @@ lz4 배치 압축을 쓰려면 `pip install "jetstream-api[lz4]"`, zstd 배치
60
60
  ## 설치 방법
61
61
  pip install jetstream-api
62
62
 
63
+ ## 0.22.3 변경 - 옛 클러스터 블롭 코덱 · actingCoordinator 제거, servicePorts (자바 v2.18.3 와 같음, 짝 엔진 7.0.1.3603 이상 권장)
64
+
65
+ 0.22.2 는 시험 판(test.pypi)만 내고 버렸다 - actingCoordinator 제거를 묶어 0.22.3 으로 나간다.
66
+
67
+ - **없앰**(호환 깨짐): 옛 고정폭 클러스터 블롭 코덱 - ``ILClusterProperty(bytes)`` · ``ba=`` · ``get_serialized`` · ``get_serialized_length`` ·
68
+ ``LAYOUT_MARK``, ``ILClusterNode(bytes)`` · ``ba=`` · ``base=`` · ``get_serialized``, 모듈 ``CLUSTER_LAYOUT_MARK``,
69
+ ``ILC.MIMQ_VO_CLUSTER_LEN`` / ``MIMQ_VO_CLUSTER_NODE_LEN``. 바이트를 주면 ``TypeError``. 엔진 r3601 이 그 형식을 지웠고 0.22.0 부터
70
+ JSON(Property v2)으로만 주고받아 쓰는 경로가 없었다. 능력 선언 ``clsblob=2`` 는 그대로.
71
+ - **없앰**(호환 깨짐): ``ILClusterProperty.actingCoordinator`` · ``get_acting_coordinator`` / ``getActingCoordinator`` - 0.18.1 부터 폐기 예정,
72
+ 엔진 7.0.1.3484 부터 늘 ``None`` 이던 관측 칸이다(엔진 r3604 도 걷음). ``controllerLeader`` 를 읽는다 - 옛 이름으로 부르면 그렇게 안내한다.
73
+ - **더함**: ``ILClusterProperty.servicePorts`` / ``getServicePorts()`` (읽기 전용) - 응답한 서비스가 맡은 컨트롤러(ilcc) 자리의 포트 전부
74
+ (엔진 ``PORT`` "a,b", 맡은 자리가 없으면 빈 목록). 컨트롤러가 멈춰 있어도 나온다 - 지금 듣는 포트는 ``localCtrlPort``.
75
+ - 노드 ``status`` · ``nodeUuid`` · ``defTerm`` · ``defVersion`` 은 이름만 남는다(엔진 r3601 부터 와이어에 없어 늘 빈 값) - 문서를 고침.
76
+ - 문서 정정: 팔로워 거절 ``MIMQE_OPERATION_DENIED (STANDBY)`` 는 **토픽**의 생성 · 변경 · 삭제만이다(쿼터 · 디스크 가드 · 클러스터에는 없다).
77
+ - 엔진 r3603: 클러스터 서명(``signature``) 값의 재료가 정의의 공유 칸 전부로 넓어져 값이 바뀐다(키 · 형식 그대로, 클라 변경 없음).
78
+
63
79
  ## 0.22.1 변경 - 노드 추가 결과에 컨트롤러 기동 여부 (자바 v2.18.1 와 같음, started · wait 는 짝 엔진 7.0.1.3602 이상)
64
80
 
65
81
  - ``addNode`` 결과(``ILClusterNodeAddResult``)에 ``started`` · ``wait``(``is_started()``): 자리를 맡은 서비스가 컨트롤러를 **띄웠는가**와
66
82
  안 띄운 까닭(``MANUAL`` - 정의 STARTMODE 가 MANUAL 이라 :meth:`startCluster` 로 띄운다 · ``STOPPED`` - 사용자가 멈춰 둠 · ``FAILED`` -
67
83
  띄우다 실패, ilsvc 로그). 그 전 엔진은 이 칸을 주지 않아 맡았으면 띄운 것으로 본다(종전 동작).
68
- - 문서: 클러스터 팔로워 노드에 보낸 생성 · 변경 · 삭제는 ``MIMQE_OPERATION_DENIED (STANDBY, leader=..)``, 조회 · 목록은 팔로워도 답한다.
84
+ - 문서: **토픽**의 생성 · 변경 · 삭제를 클러스터 팔로워 노드에 보내면 ``MIMQE_OPERATION_DENIED (STANDBY, leader=..)``, 조회 · 목록은
85
+ 팔로워도 답한다. 쿼터 · 디스크 가드 · 클러스터(서비스가 받는 설정)에는 이 거절이 없다(배포 뒤 정정 - 처음에는 네 오브젝트 모두로 잘못 적었다).
69
86
 
70
87
  ## 0.22.0 변경 - 7 신규 오브젝트(쿼터 · 디스크 가드 · 토픽 · 클러스터) 관리를 Property v2(JSON)로 (자바 v2.18.0 와 같음, 짝 엔진 7.0.1.3601 이상)
71
88
 
@@ -10,12 +10,29 @@ lz4 배치 압축을 쓰려면 `pip install "jetstream-api[lz4]"`, zstd 배치
10
10
  ## 설치 방법
11
11
  pip install jetstream-api
12
12
 
13
+ ## 0.22.3 변경 - 옛 클러스터 블롭 코덱 · actingCoordinator 제거, servicePorts (자바 v2.18.3 와 같음, 짝 엔진 7.0.1.3603 이상 권장)
14
+
15
+ 0.22.2 는 시험 판(test.pypi)만 내고 버렸다 - actingCoordinator 제거를 묶어 0.22.3 으로 나간다.
16
+
17
+ - **없앰**(호환 깨짐): 옛 고정폭 클러스터 블롭 코덱 - ``ILClusterProperty(bytes)`` · ``ba=`` · ``get_serialized`` · ``get_serialized_length`` ·
18
+ ``LAYOUT_MARK``, ``ILClusterNode(bytes)`` · ``ba=`` · ``base=`` · ``get_serialized``, 모듈 ``CLUSTER_LAYOUT_MARK``,
19
+ ``ILC.MIMQ_VO_CLUSTER_LEN`` / ``MIMQ_VO_CLUSTER_NODE_LEN``. 바이트를 주면 ``TypeError``. 엔진 r3601 이 그 형식을 지웠고 0.22.0 부터
20
+ JSON(Property v2)으로만 주고받아 쓰는 경로가 없었다. 능력 선언 ``clsblob=2`` 는 그대로.
21
+ - **없앰**(호환 깨짐): ``ILClusterProperty.actingCoordinator`` · ``get_acting_coordinator`` / ``getActingCoordinator`` - 0.18.1 부터 폐기 예정,
22
+ 엔진 7.0.1.3484 부터 늘 ``None`` 이던 관측 칸이다(엔진 r3604 도 걷음). ``controllerLeader`` 를 읽는다 - 옛 이름으로 부르면 그렇게 안내한다.
23
+ - **더함**: ``ILClusterProperty.servicePorts`` / ``getServicePorts()`` (읽기 전용) - 응답한 서비스가 맡은 컨트롤러(ilcc) 자리의 포트 전부
24
+ (엔진 ``PORT`` "a,b", 맡은 자리가 없으면 빈 목록). 컨트롤러가 멈춰 있어도 나온다 - 지금 듣는 포트는 ``localCtrlPort``.
25
+ - 노드 ``status`` · ``nodeUuid`` · ``defTerm`` · ``defVersion`` 은 이름만 남는다(엔진 r3601 부터 와이어에 없어 늘 빈 값) - 문서를 고침.
26
+ - 문서 정정: 팔로워 거절 ``MIMQE_OPERATION_DENIED (STANDBY)`` 는 **토픽**의 생성 · 변경 · 삭제만이다(쿼터 · 디스크 가드 · 클러스터에는 없다).
27
+ - 엔진 r3603: 클러스터 서명(``signature``) 값의 재료가 정의의 공유 칸 전부로 넓어져 값이 바뀐다(키 · 형식 그대로, 클라 변경 없음).
28
+
13
29
  ## 0.22.1 변경 - 노드 추가 결과에 컨트롤러 기동 여부 (자바 v2.18.1 와 같음, started · wait 는 짝 엔진 7.0.1.3602 이상)
14
30
 
15
31
  - ``addNode`` 결과(``ILClusterNodeAddResult``)에 ``started`` · ``wait``(``is_started()``): 자리를 맡은 서비스가 컨트롤러를 **띄웠는가**와
16
32
  안 띄운 까닭(``MANUAL`` - 정의 STARTMODE 가 MANUAL 이라 :meth:`startCluster` 로 띄운다 · ``STOPPED`` - 사용자가 멈춰 둠 · ``FAILED`` -
17
33
  띄우다 실패, ilsvc 로그). 그 전 엔진은 이 칸을 주지 않아 맡았으면 띄운 것으로 본다(종전 동작).
18
- - 문서: 클러스터 팔로워 노드에 보낸 생성 · 변경 · 삭제는 ``MIMQE_OPERATION_DENIED (STANDBY, leader=..)``, 조회 · 목록은 팔로워도 답한다.
34
+ - 문서: **토픽**의 생성 · 변경 · 삭제를 클러스터 팔로워 노드에 보내면 ``MIMQE_OPERATION_DENIED (STANDBY, leader=..)``, 조회 · 목록은
35
+ 팔로워도 답한다. 쿼터 · 디스크 가드 · 클러스터(서비스가 받는 설정)에는 이 거절이 없다(배포 뒤 정정 - 처음에는 네 오브젝트 모두로 잘못 적었다).
19
36
 
20
37
  ## 0.22.0 변경 - 7 신규 오브젝트(쿼터 · 디스크 가드 · 토픽 · 클러스터) 관리를 Property v2(JSON)로 (자바 v2.18.0 와 같음, 짝 엔진 7.0.1.3601 이상)
21
38
 
@@ -1,6 +1,6 @@
1
1
  """iLink - Python client API for iLink M.O.M. (Message Oriented Middleware)."""
2
2
 
3
- __version__ = "0.22.1"
3
+ __version__ = "0.22.3"
4
4
 
5
5
  # --- Admin / Service ---
6
6
  from .admin import ILAdminService, ILAdminQmgr, ILLogBrowser, ILQueueBrowser
@@ -484,14 +484,8 @@ class ILC:
484
484
  # 노드(큐관리자)에게 묻는 런타임 관측 리포트 (OBS-1). TEXT | JSON
485
485
  MIMQ_GET_CLUSTER_RUNTIME = 3381
486
486
 
487
- # 클러스터 속성 블롭 레이아웃 (C++ ClusterProperty.hpp 정본)
488
- # 고정부(768) + 노드수(5) + 노드 x 384 - **가변 길이**라 고정폭 리스트 파서를 쓸 수 없다.
489
- # 판 이력: 256 -> 512(CLSTOPO-1, 3-서비스 토폴로지) -> 768(컴트롤러 정족수).
490
- # ★ 768 판부터는 블롭 앞머리에 판 표시가 있다(:data:`ilink.properties.CLUSTER_LAYOUT_MARK`).
491
- # 그 전 판은 표시가 없어 **레거시로 보고 거절**한다 - 길이로는 못 가른다
492
- # (512+5+256 과 768+5+0 이 둘 다 773 이다).
493
- MIMQ_VO_CLUSTER_LEN = 768
494
- MIMQ_VO_CLUSTER_NODE_LEN = 384
487
+ # 0.22.3: 클러스터 속성의 옛 고정폭 블롭(768 + 5 + 384 x N) 상수 MIMQ_VO_CLUSTER_LEN / MIMQ_VO_CLUSTER_NODE_LEN 을 없앴다 -
488
+ # 엔진 r3601 이 클러스터를 Property v2(JSON) 전용으로 바꾸며 그 형식을 지웠다.
495
489
  MIMQ_LIST_SIZE_LENGTH = 5
496
490
 
497
491
  # ------------------------------------------------------------------
@@ -983,8 +977,8 @@ class ILC:
983
977
  #: 능력 선언의 최대 길이(접두 포함). 넘으면 **자르지 않고 통째로 버린다** -
984
978
  #: 자르면 ``clsblob=2`` 가 ``clsblob=`` 가 되어 **읽히는데 뜻이 다른** 값이 된다.
985
979
  MIMQ_CLIENT_CAPS_MAX = 63
986
- #: 이 클라가 읽고 쓰는 클러스터 블롭 판. :data:`ilink.properties.CLUSTER_LAYOUT_MARK`
987
- #: 와 능력 선언이 **모두 이 값에서 나온다** - 판 번호를 두 곳에 적지 않는다.
980
+ #: 능력 선언(``CAP=clsblob=2``)의 클러스터 블롭 판. 0.22.3 부터 클라는 클러스터를 JSON 으로만 주고받지만(옛 블롭 코덱 제거)
981
+ #: 접속 능력 선언 토큰은 바꾸지 않는다 - 엔진이 핸드셰이크에서 읽는 값이다.
988
982
  MIMQ_CLUSTER_BLOB_VERSION = 2
989
983
  MIMQ_SESSION_STATUS_DEAD = 0
990
984
  MIMQ_SESSION_STATUS_IDLE = 1
@@ -3,7 +3,6 @@ from __future__ import annotations
3
3
  import datetime
4
4
  import functools
5
5
  import re
6
- import warnings
7
6
  from enum import IntEnum
8
7
  from typing import List
9
8
 
@@ -4789,8 +4788,9 @@ class ILPropertyResult(_AutoAccessor):
4789
4788
  엔진이 무엇을 적용했고 무엇을 무시했는지 돌려준다 - 모르는 키를 무시하는 규칙(U4)이 **조용한 무시**가 되지 않게
4790
4789
  하는 장치다. 무시된 키가 있으면 라이브러리는 WARNING 로그도 남긴다(결과를 돌려주지 않는 옛 메서드도 같다).
4791
4790
 
4792
- 클러스터의 팔로워 노드에 보낸 변경(CREATE / ALTER / DELETE)은 ``MIMQE_OPERATION_DENIED (STANDBY, leader=<리더>)`` 로
4793
- 거절된다(연결 유형과 무관, 엔진 r3601). 조회(GET / LIST)는 팔로워도 답한다.
4791
+ **토픽**의 생성 · 변경 · 삭제를 클러스터의 팔로워 노드(큐 관리자)에 보내면 ``MIMQE_OPERATION_DENIED (STANDBY, leader=<리더>)``
4792
+ 로 거절된다(연결 유형과 무관, 엔진 r3601) - 리더에 보낸다. 토픽 조회 · 목록은 팔로워도 답한다. 쿼터 · 디스크 가드 · 클러스터는
4793
+ 서비스(ilsvc)가 받는 설정이라 이 거절이 없다(쿼터는 서비스마다 따로 넣는다).
4794
4794
 
4795
4795
  Attributes:
4796
4796
  applied (list[tuple[str, str]]): 적용된 키와 반영 시점 ``(키, "NOW" | "RESTART")``.
@@ -5537,72 +5537,8 @@ class ILSpokeProperty(_AutoAccessor):
5537
5537
  )
5538
5538
 
5539
5539
 
5540
- #: 클러스터 와이어 레이아웃 **판 표시**. 오프셋 0, 폭 4, 값 ``"V2"``.
5541
- #:
5542
- #: ★ **첫 글자가 숫자가 아니어야 한다.** 옛(레이아웃 1) 블롭은 오프셋 0 에 ``Status`` 를
5543
- #: 왼쪽 정렬 십진수로 썼고 ``MIMQ_RUNNING`` 이 **2** 라, 판 표시를 ``"2"`` 로 두면
5544
- #: 옛 블롭이 **"판 2 와 정확히 일치" 로 읽힌다**(거절도 아니고 "낮은 판" 도 아니다).
5545
- #: 글자로 시작하면 옛 블롭이 만들 수 없는 값이 되어 두 방향이 모두 막힌다.
5546
- CLUSTER_LAYOUT_MARK = "V%d" % ILC.MIMQ_CLUSTER_BLOB_VERSION
5547
-
5548
- #: :data:`CLUSTER_LAYOUT_MARK` 의 숫자 부분. 비교는 이쪽으로 한다 - 문자열 비교는
5549
- #: ``"V10" < "V2"`` 로 갈린다.
5550
- _CLUSTER_LAYOUT_NUMBER = ILC.MIMQ_CLUSTER_BLOB_VERSION
5551
-
5552
- #: :class:`ILClusterNode` 와이어 레이아웃 ``(속성 이름, 폭)``. **오프셋은 순서에서 계산한다** -
5553
- #: 폭이 조정돼도 이 표 한 줄만 고치면 파싱·직렬화·보존이 같이 따라간다.
5554
- #: 이름이 ``None`` 인 자리는 **클라가 읽지도 쓰지도 않는 구간**이다(원본을 그대로 되보낸다).
5555
- _CLUSTER_NODE_FIELDS = (
5556
- ("nodeId", 5), ("qmgrName", 60), ("host", 128), ("port", 6),
5557
- ("ctrlPort", 6), ("status", 6), ("coord", 2), ("nodeUuid", 40),
5558
- ("defTerm", 10), ("defVersion", 10), (None, 111),
5559
- )
5560
-
5561
-
5562
- def _field_offsets(fields):
5563
- """``(이름, 폭)`` 표를 ``{이름: (오프셋, 폭)}`` 과 총길이로 편다.
5564
-
5565
- Args:
5566
- fields: ``(이름, 폭)`` 튜플들. 이름이 None 이면 예약 구간이다.
5567
-
5568
- Returns:
5569
- tuple: ``({이름: (오프셋, 폭)}, 총길이)``.
5570
- """
5571
- out, off = {}, 0
5572
- for name, width in fields:
5573
- if name is not None:
5574
- out[name] = (off, width)
5575
- off += width
5576
- return out, off
5577
-
5578
-
5579
- _NODE_OFF, _NODE_LEN = _field_offsets(_CLUSTER_NODE_FIELDS)
5580
-
5581
- #: :class:`ILClusterNode` 에서 **클라가 값을 만들어 쓰는** 자리. 나머지 이름 있는 자리는
5582
- #: 엔진이 채우는 값이라 **읽기만 하고 원본 바이트를 그대로 되돌려 보낸다.**
5583
- #:
5584
- #: ★ **읽기만 하는 자리를 다시 쓰지 않는 이유**: 다시 쓰면 **파싱 결함이 곧 쓰기 결함이
5585
- #: 된다.** ``defTerm`` 을 잘못 읽으면 사람이 잘못 볼 뿐이지만, 잘못 읽은 값을 되쓰면
5586
- #: 엔진 상태가 망가진다. 되보내지 않으면 그 경로 자체가 없다.
5587
- _NODE_WRITTEN = ("nodeId", "qmgrName", "host", "port", "ctrlPort", "coord")
5588
-
5589
-
5590
- def _pick_coord(ba, offset):
5591
- """``"C"`` / ``"-"`` / 빈칸을 ``True`` / ``False`` / ``None`` 으로 읽는다."""
5592
- text = ba_pick_string(ba, offset, 2).strip()
5593
- if text == "C":
5594
- return True
5595
- if text == "-":
5596
- return False
5597
- return None
5598
-
5599
-
5600
5540
  class ILClusterNode(_AutoAccessor):
5601
- """클러스터를 이루는 **노드 하나**. 고정폭 384 바이트 블롭.
5602
-
5603
- 레이아웃(판 ``V2``): NodeId(5) + QmgrName(60) + Host(128) + Port(6)
5604
- + CtrlPort(6) + Status(6) + Coord(2) + NodeUuid(40) + DefTerm(10) + DefVersion(10)
5605
- + Reserved(111)
5541
+ """클러스터를 이루는 **노드 하나**. 와이어는 클러스터 정의 JSON 의 ``NODES`` 원소다(0.22.0 Property v2).
5606
5542
 
5607
5543
  노드는 **역할로 나뉜다 - 종류로 나뉘지 않는다.**
5608
5544
 
@@ -5639,25 +5575,17 @@ class ILClusterNode(_AutoAccessor):
5639
5575
  ☠ 다만 **동작에 쓰이는 것은 컨트롤러 정족수가 켜진 뒤**다 - 그전에는
5640
5576
  저장만 되고 아무 일도 일어나지 않는다.
5641
5577
  ☠ 문법의 ``COORDS(1)`` 은 *"1번이 컨트롤러고 **나머지는 아니다**"* 라는
5642
- 완결된 진술이다. 블롭으로 같은 말을 하려면 나머지 노드를 **명시적으로
5643
- ``False``** 로 둬야 한다 - 빈칸으로 두면 *"모른다"* 가 되어 뜻이 다르다.
5578
+ 완결된 진술이다. 객체로 같은 말을 하려면 나머지 노드를 **명시적으로
5579
+ ``False``** 로 둬야 한다 - ``None`` 으로 두면 *"모른다"* 가 되어 뜻이 다르다.
5644
5580
  컨트롤러 노드에는 **``ctrl_port`` 가 반드시** 있어야 한다.
5645
5581
  ★ **``None`` 은 "모름"** 이고 ``False``("아니다")와 **다르다**. 서버가 안
5646
5582
  채웠거나 이 판이 모르는 값일 때 ``None`` 이다. 참/거짓으로 판정하지 말고
5647
5583
  ``is None`` 으로 먼저 가른다.
5648
- status (int | None): **읽기 전용.** 서버가 채우는 이 노드의 상태.
5649
- ★ **``None`` 은 "모름"** 이다 - 엔진이 아직 이 노드를 못 봤으면 빈칸으로 온다.
5650
- ``0`` 으로 읽지 않는다: 상태 코드에 0 이 없어(STOPPED 1 / RUNNING 2 /
5651
- STARTING 4) **0 을 주면 있지도 않은 상태를 지어내는 것**이 된다.
5652
- ※ :attr:`ILClusterProperty.status` 는 정의의 실행 상태라 엔진이 늘 채운다 -
5653
- 거기는 ``int`` 그대로다. **두 status 는 뜻이 다르다.**
5654
- nodeUuid (str): **읽기 전용.** 노드가 **첫 합류 때 스스로 찍는 신원**.
5655
- 같은 번호에 다른 장비를 넣으면 엔진이 이 값으로 잡아낸다 - 그래서 클라는
5656
- **읽기만 하고 받은 값을 그대로 되돌려 보낸다.**
5657
- defTerm (int | None): **읽기 전용.** 이 노드가 든 정의의 **임기**.
5658
- defVersion (int | None): **읽기 전용.** 이 노드가 든 정의의 **판 번호**.
5659
- ★ ``defTerm`` 과 **쌍**이다 - 한쪽만 값이면 프로토콜 오류다.
5660
- ★ **0 은 정상값**이다(선출이 한 번도 없었던 새 클러스터). 없음은 오직 ``None``.
5584
+ status (int | None): **읽기 전용.** 엔진 r3601(Property v2)부터 와이어에 없다 - 늘 ``None``.
5585
+ nodeUuid (str): **읽기 전용.** 엔진 r3601 부터 와이어에 없다 - 늘 ``""``.
5586
+ defTerm (int | None): **읽기 전용.** 엔진 r3601 부터 와이어에 없다 - 늘 ``None``. 정의의 판은
5587
+ :attr:`ILClusterProperty.defTerm` / :attr:`ILClusterProperty.defVersion` 으로 본다.
5588
+ defVersion (int | None): **읽기 전용.** 위와 같다 - 늘 ``None``.
5661
5589
 
5662
5590
  Example::
5663
5591
 
@@ -5677,25 +5605,24 @@ class ILClusterNode(_AutoAccessor):
5677
5605
  "ilccPort": ("ctrlPort", "0.17.0"),
5678
5606
  }
5679
5607
 
5680
- def __init__(self, qmgr_name: str | bytes | bytearray | None = None, host: str = "",
5681
- port: int = 0, ctrl_port: int = 0, node_id: int = 0,
5682
- ba: bytes | bytearray | None = None, base: int = 0) -> None:
5683
- """노드를 만든다. ``bytes`` 를 주면 서버 응답으로 보고 파싱한다.
5608
+ def __init__(self, qmgr_name: str | None = None, host: str = "",
5609
+ port: int = 0, ctrl_port: int = 0, node_id: int = 0) -> None:
5610
+ """노드를 만든다.
5684
5611
 
5685
5612
  Args:
5686
- qmgr_name (str | bytes | bytearray | None): 큐 관리자 이름. 비우면 컨트롤러
5687
- 전용 노드다. ``bytes`` 를 주면 서버 응답으로 보고 파싱한다(다른 속성
5688
- 클래스와 같은 관례).
5613
+ qmgr_name (str | None): 큐 관리자 이름. 비우면 컨트롤러 전용 노드다.
5689
5614
  host (str): 호스트(IP 또는 이름).
5690
5615
  port (int): 데이터 포트. 컨트롤러 전용 노드는 0.
5691
5616
  ctrl_port (int): 컨트롤러 포트(문법에서 주소 뒤 ``/9000`` 자리).
5692
5617
  node_id (int): 노드 번호(``NODE[n]`` 인덱스).
5693
- ba (bytes | bytearray | None): 서버가 보낸 고정폭 바이트.
5694
- base (int): ``ba`` 안에서 이 노드가 시작하는 위치.
5695
5618
 
5696
- Raises:
5697
- ILDeserializeException: 바이트가 384 바이트보다 짧을 때.
5619
+ Note:
5620
+ 0.22.3 에서 옛 고정폭 블롭 생성(``ILClusterNode(bytes)`` · ``ba=`` · ``base=``)과 ``get_serialized`` 를 없앴다 -
5621
+ 엔진 r3601 이 그 형식을 지웠고 0.22.0 부터 클라도 JSON 으로만 주고받는다.
5698
5622
  """
5623
+ if isinstance(qmgr_name, (bytes, bytearray)):
5624
+ # 0.22.3: 옛 고정폭 블롭 생성자는 없앴다 - 바이트를 이름으로 조용히 받지 않는다
5625
+ raise TypeError("ILClusterNode(bytes) was removed in 0.22.3 - the engine speaks JSON (Property v2) only")
5699
5626
  self.nodeId = node_id
5700
5627
  self.qmgrName = ""
5701
5628
  self.host = host
@@ -5706,69 +5633,8 @@ class ILClusterNode(_AutoAccessor):
5706
5633
  self.nodeUuid = ""
5707
5634
  self.defTerm = None
5708
5635
  self.defVersion = None
5709
- #: (내부) 서버가 준 원본 384바이트. **읽기만 하는 자리와 읽지도 쓰지도 않는
5710
- #: 구간을 되돌려 보내기 위해** 들고 있다(:meth:`get_serialized` 참조).
5711
- #: 직접 만든 객체는 빈 값.
5712
- self._rawBytes = b""
5713
-
5714
- if isinstance(qmgr_name, (bytes, bytearray)) and ba is None:
5715
- ba, qmgr_name = bytes(qmgr_name), None
5716
5636
  if qmgr_name is not None:
5717
5637
  self.qmgrName = qmgr_name
5718
- if ba is None:
5719
- return
5720
- if len(ba) - base < _NODE_LEN:
5721
- raise ILDeserializeException(
5722
- "invalid byte array length. fixed : [" + str(_NODE_LEN)
5723
- + "] / actual : [" + str(len(ba) - base) + "]")
5724
- o = _NODE_OFF
5725
- self.nodeId = ba_pick_int(ba, base + o["nodeId"][0], o["nodeId"][1])
5726
- self.qmgrName = ba_pick_string(ba, base + o["qmgrName"][0], o["qmgrName"][1])
5727
- self.host = ba_pick_string(ba, base + o["host"][0], o["host"][1])
5728
- self.port = ba_pick_int(ba, base + o["port"][0], o["port"][1])
5729
- self.ctrlPort = ba_pick_int(ba, base + o["ctrlPort"][0], o["ctrlPort"][1])
5730
- self.status = ba_pick_int_opt(ba, base + o["status"][0], o["status"][1])
5731
- self.coordinator = _pick_coord(ba, base + o["coord"][0])
5732
- self.nodeUuid = ba_pick_string(ba, base + o["nodeUuid"][0], o["nodeUuid"][1])
5733
- self.defTerm = ba_pick_int_opt(ba, base + o["defTerm"][0], o["defTerm"][1])
5734
- self.defVersion = ba_pick_int_opt(ba, base + o["defVersion"][0], o["defVersion"][1])
5735
- self._rawBytes = bytes(ba[base:base + _NODE_LEN])
5736
-
5737
- def get_serialized(self) -> bytes:
5738
- """이 객체를 서버 규격의 **고정폭 384 바이트**로 직렬화한다.
5739
-
5740
- Returns:
5741
- bytes: 서버로 그대로 보낼 수 있는 바이트열.
5742
-
5743
- Note:
5744
- ★ **클라가 값을 만들어 쓰는 자리만 다시 쓴다**(:data:`_NODE_WRITTEN`).
5745
- 읽기만 하는 자리(``status`` · ``nodeUuid`` · ``defTerm`` · ``defVersion``)와
5746
- 읽지도 쓰지도 않는 구간은 **원본을 그대로 되돌려 보낸다.** 서버가 준
5747
- 바이트로 만든 객체일 때만 그렇다. 0 으로 채운 버퍼에 아는 자리만 쓰면 그
5748
- 구간이 **0x00 으로 지워져 돌아간다** - 지우면 예외 없이 뜻이 바뀐다.
5749
-
5750
- ★ **미설정은 빈칸으로 쓴다. 0 을 쓰지 않는다.** ``coordinator`` 가 그렇다 -
5751
- 규격이 **"0 은 정상값이고 없음은 오직 빈칸"** 으로 정했다.
5752
-
5753
- 문자열은 **폭 − 1** 까지만 쓴다. 엔진이 C 문자열로 읽으므로 폭을 꽉 채우면
5754
- 끝을 못 찾고 다음 필드까지 읽는다.
5755
- """
5756
- o = _NODE_OFF
5757
- if len(self._rawBytes) == _NODE_LEN:
5758
- ba = bytearray(self._rawBytes)
5759
- for name in _NODE_WRITTEN: # 내가 쓰는 자리만 비우고 다시 쓴다
5760
- off, width = o[name]
5761
- ba[off:off + width] = bytes(width)
5762
- else:
5763
- ba = bytearray(_NODE_LEN)
5764
- ba_merge(self.nodeId, ba, o["nodeId"][0], o["nodeId"][1])
5765
- ba_merge(self.qmgrName, ba, o["qmgrName"][0], o["qmgrName"][1] - 1)
5766
- ba_merge(self.host, ba, o["host"][0], o["host"][1] - 1)
5767
- ba_merge(self.port, ba, o["port"][0], o["port"][1])
5768
- ba_merge(self.ctrlPort, ba, o["ctrlPort"][0], o["ctrlPort"][1])
5769
- if self.coordinator is not None: # None 은 빈칸(모름)으로 남긴다
5770
- ba_merge("C" if self.coordinator else "-", ba, o["coord"][0], o["coord"][1])
5771
- return bytes(ba)
5772
5638
 
5773
5639
  def get_address(self) -> str:
5774
5640
  """큐 관리자 주소 ``"host:port"``(데이터 포트).
@@ -5797,8 +5663,8 @@ class ILClusterNode(_AutoAccessor):
5797
5663
  Note:
5798
5664
  ★ :class:`_AutoAccessor` 가 자동으로 만드는 ``isX()`` 는 ``bool(값)`` 이라
5799
5665
  ``None`` 을 ``False``("아니다")로 바꾼다. 그래서 이 메서드를 손으로 둔다 -
5800
- **"모름" 을 "아니다" 로 바꾸지 않는다.** 엔진이 ``"C"`` / ``"-"`` / 빈칸
5801
- 세 값으로 인코딩한 뜻이 편의 계층에서 사라지면 안 된다.
5666
+ **"모름" 을 "아니다" 로 바꾸지 않는다.** 엔진이 ``COORD`` true / false / 키 없음
5667
+ 세 상태로 주는 뜻이 편의 계층에서 사라지면 안 된다.
5802
5668
  """
5803
5669
  return self.coordinator
5804
5670
 
@@ -6359,48 +6225,6 @@ class ILClusterControlResult(_AutoAccessor):
6359
6225
  s += " [" + ", ".join(self.skippedNodes) + "]"
6360
6226
  return s
6361
6227
 
6362
- #: :class:`ILClusterProperty` 고정부 와이어 레이아웃 ``(속성 이름, 폭)``.
6363
- #: :data:`_CLUSTER_NODE_FIELDS` 와 같은 규칙 - **오프셋은 순서에서 계산한다.**
6364
- #: 이름이 ``None`` 인 자리는 클라가 읽지도 쓰지도 않는 구간이다.
6365
- _CLUSTER_FIXED_FIELDS = (
6366
- ("layoutVersion", 4), ("clusterId", 40), ("name", 60), ("status", 6),
6367
- ("startMode", 20),
6368
- ("controllerHeartbeatIntervalMs", 10), ("controllerElectionTimeoutMs", 10),
6369
- ("heartbeatIntervalMs", 10), ("heartbeatMissCount", 5), ("electionDelayMs", 10),
6370
- ("replicas", 5), ("minSync", 5),
6371
- ("secretFile", 256),
6372
- ("controllerLeader", 6), ("defTerm", 10), ("defVersion", 10),
6373
- ("signature", 20), ("statusReason", 96),
6374
- # 엔진 r3473 이 예약 앞 12바이트에 이름을 줬다. **판 번호는 안 올랐다**(V2 그대로) -
6375
- # 이 클라는 모르는 자리를 원본째 되보내므로 이름이 붙기 전에도 아무 문제가 없었다.
6376
- # ☠ 둘 다 **관측 전용**이다: 엔진이 나갈 때만 채우고 들어올 때는 지운다.
6377
- # actingCoordinator 는 **폐기 예정**이다(0.18.1): r3481~r3483 은 controllerLeader 의 사본이었고,
6378
- # r3484 부터는 엔진이 채우지 않는다. 이 12바이트 중 앞 6바이트(583..588)는 **영구 결번** -
6379
- # 다른 값에 다시 쓰이지 않는다(옛 클라가 새 값을 이 이름으로 조용히 잘못 읽지 않도록).
6380
- ("actingCoordinator", 6), ("localCtrlPort", 6),
6381
- # 엔진 PLACEMENT-1: 예약 앞 20바이트에 **정의 칸 둘**. 판 표시는 V2 그대로 - 옛 블롭에는 0x00 이라
6382
- # 미설정으로 읽힌다. 빈칸으로 보내면 엔진이 저장값을 유지하고, "DEFAULT" 를 보내면 미설정으로 되돌린다.
6383
- ("placement", 12), ("autoRebalance", 8), (None, 153),
6384
- )
6385
-
6386
- _CLUSTER_OFF, _CLUSTER_LEN = _field_offsets(_CLUSTER_FIXED_FIELDS)
6387
-
6388
- #: :class:`ILClusterProperty` 에서 **클라가 값을 만들어 쓰는** 자리. 나머지 이름 있는
6389
- #: 자리는 엔진이 채우므로 **읽기만 하고 원본 바이트를 그대로 되돌려 보낸다**
6390
- #: (:data:`_NODE_WRITTEN` 와 같은 규칙과 같은 이유).
6391
- _CLUSTER_WRITTEN = ("layoutVersion", "name", "startMode",
6392
- "controllerHeartbeatIntervalMs", "controllerElectionTimeoutMs",
6393
- "heartbeatIntervalMs", "heartbeatMissCount", "electionDelayMs",
6394
- "replicas", "minSync", "placement", "autoRebalance")
6395
-
6396
- # 표에서 계산한 길이와 공개 상수가 어긋나면 **여기서 터진다.** 한쪽만 고치는 일을 막는다 -
6397
- # 상수를 써서 오프셋을 잡는 코드가 밖에 있어서 조용히 어긋나면 잘못된 자리를 파게 된다.
6398
- if (_NODE_LEN, _CLUSTER_LEN) != (ILC.MIMQ_VO_CLUSTER_NODE_LEN, ILC.MIMQ_VO_CLUSTER_LEN):
6399
- raise RuntimeError(
6400
- "cluster layout tables and ILC constants disagree : node %d vs %d, fixed %d vs %d"
6401
- % (_NODE_LEN, ILC.MIMQ_VO_CLUSTER_NODE_LEN, _CLUSTER_LEN, ILC.MIMQ_VO_CLUSTER_LEN))
6402
-
6403
-
6404
6228
  class ILFollowerStatus(_AutoAccessor):
6405
6229
  """리더가 본 **팔로워 하나**의 복제 상태. :class:`ILReplicationStatus` 의 :attr:`~ILReplicationStatus.followers` 항목이다.
6406
6230
 
@@ -6755,50 +6579,8 @@ class ILGroupDescription(ILGroupInfo):
6755
6579
  getOffset = get_offset
6756
6580
 
6757
6581
 
6758
- def _cluster_layout_number(ba, offset: int = 0) -> int:
6759
- """블롭 앞머리의 **판 표시**를 읽어 판 번호를 돌려준다.
6760
-
6761
- Args:
6762
- ba (bytes | bytearray): 고정부를 담은 바이트.
6763
- offset (int): 고정부가 시작하는 위치.
6764
-
6765
- Returns:
6766
- int: 판 번호. ``"V2"`` 면 2.
6767
-
6768
- Raises:
6769
- ILDeserializeException: 판 표시가 없거나(레거시) 형식이 틀렸을 때.
6770
-
6771
- Note:
6772
- ★ **판 표시가 숫자면 안 된다.** 옛 레이아웃은 오프셋 0 에 ``Status`` 를 십진수로
6773
- 썼고 ``MIMQ_RUNNING`` 이 **2** 라, 표시를 ``"2"`` 로 뒀으면 옛 블롭이
6774
- **"판 2 와 정확히 일치"** 로 읽혔다 - 거절도 아니고 "낮은 판" 도 아니다.
6775
- 길이로도 못 가른다(옛 512+5+256 과 새 768+5+0 이 둘 다 773 이다).
6776
- ``"V"`` 로 시작하면 옛 블롭이 **만들 수 없는 값**이 되어 두 방향이 다 막힌다.
6777
-
6778
- ★ **마커가 없는 것은 레거시이고 무조건 거절한다.** "낮으면 미설정으로 읽는다"
6779
- 는 **마커가 있는 판들 사이에서만** 쓰는 규칙이다 - 1→2 는 덧붙임이 아니라
6780
- 재배치라서(``Status`` 가 오프셋 0 에서 104 로 갔다) 이어 읽을 수가 없다.
6781
- """
6782
- off, width = _CLUSTER_OFF["layoutVersion"]
6783
- mark = ba_pick_string(ba, offset + off, width).strip()
6784
- if not mark or not mark.startswith("V") or not mark[1:].isdigit():
6785
- raise ILDeserializeException(
6786
- "unsupported cluster layout : this engine speaks the v2 cluster layout"
6787
- " (marker %r at offset 0) but the blob has no marker - it is the legacy"
6788
- " layout and cannot be read" % (mark,))
6789
- return int(mark[1:])
6790
-
6791
-
6792
6582
  class ILClusterProperty(_AutoAccessor):
6793
- """클러스터 정의 1개. 가변 길이 블롭.
6794
-
6795
- 직렬화 = 고정부(768) + 노드수(5) + 노드 x 384
6796
-
6797
- 고정부 레이아웃(판 ``V2``): LayoutVersion(4) + ClusterId(40) + Name(60) + Status(6)
6798
- + StartMode(20) + CtrlHbIntervalMs(10) + CtrlElectTimeoutMs(10) + HbIntervalMs(10)
6799
- + HbMissCount(5) + ElectionDelayMs(10) + Replicas(5) + MinSync(5) + SecretFile(256)
6800
- + ControllerLeader(6) + DefTerm(10) + DefVersion(10) + Signature(20)
6801
- + StatusReason(96) + ActingCoordinator(6) + LocalCtrlPort(6) + Reserved(173)
6583
+ """클러스터 정의 1개. 와이어는 Property v2 JSON(``_TYPE`` = ``CLUSTER``, 0.22.0)이다.
6802
6584
 
6803
6585
  **정의는 하나다.** 예전에는 클러스터 하나를 관리 서비스 셋(판사·MAIN·SUB)에 각자의
6804
6586
  정의로 만들었는데, 이제 **노드 목록 하나**에 전 노드를 적고 그중 컨트롤러를 맡을
@@ -6856,12 +6638,9 @@ class ILClusterProperty(_AutoAccessor):
6856
6638
  ★ ``defTerm`` 과 **쌍**이다. **0 은 정상값**이고 없음은 오직 ``None`` 이다.
6857
6639
  이 판은 **응답한 서비스가 저장한 사본의 판**이다. 그래서 서비스마다 다를 수 있고, 뒤처진 사본은 판이 낮다.
6858
6640
  signature (str): **읽기 전용.** 엔진이 정의에 붙이는 표식.
6859
- actingCoordinator (int | None): **읽기 전용.** 승격을 집행하는 조정자의
6860
- 노드 번호. 모르면 ``None`` - **0 은 노드 번호가 아니다.**
6861
- ☠ **폐기 예정(0.18.1).** 엔진 r3481~r3483 은 :attr:`controllerLeader` 의 **사본**이었고
6862
- (승격을 집행하는 자가 곧 컨트롤러 리더다), r3484 부터는 엔진이 채우지 않아 늘
6863
- ``None`` 이고, 이 자리는 다른 값에 다시 쓰이지 않는다. :attr:`controllerLeader` 를
6864
- 읽어라. 접근자 :meth:`get_acting_coordinator` 는 부를 때마다 경고를 낸다.
6641
+ servicePorts (list[int]): **읽기 전용.** 이 응답을 준 **서비스가 맡은** 컨트롤러(ilcc) 자리의 포트 전부
6642
+ (0.22.3, 엔진 r3601 의 ``PORT``). 설정값이라 컨트롤러가 멈춰 있어도 나온다. 맡은 자리가 없으면 빈 목록.
6643
+ :attr:`localCtrlPort` 는 그중 **지금 실제로 듣는** 포트 하나(관측값)다.
6865
6644
  localCtrlPort (int | None): **읽기 전용.** 이 응답을 준 **서비스가 실제로
6866
6645
  듣고 있는** ilcc 포트. 정의에 적힌 값이 아니라 **관측값**이다.
6867
6646
  ★ 둘 다 엔진이 **나갈 때만** 채운다 - 실어 보내도 무시된다.
@@ -6888,10 +6667,6 @@ class ILClusterProperty(_AutoAccessor):
6888
6667
  svc.createCluster(c)
6889
6668
  """
6890
6669
 
6891
- #: 이 클래스가 읽고 쓰는 와이어 레이아웃의 **판 표시**. 자바 ``ILClusterProperty.LAYOUT_MARK``
6892
- #: 와 **같은 자리에 같은 이름**으로 둔다 - 모듈 수준 :data:`CLUSTER_LAYOUT_MARK` 와 같은 값이다.
6893
- LAYOUT_MARK = CLUSTER_LAYOUT_MARK
6894
-
6895
6670
  # 엔진 ClusterProperty::Validate() 와 같은 규칙.
6896
6671
  _SPEC = {
6897
6672
  "name": _spec_maxlen(47),
@@ -6906,8 +6681,8 @@ class ILClusterProperty(_AutoAccessor):
6906
6681
  "defTerm": "엔진이 채우는 값이다",
6907
6682
  "defVersion": "엔진이 채우는 값이다",
6908
6683
  "signature": "엔진이 채우는 값이다",
6909
- "actingCoordinator": "관측값이다 - 엔진이 나갈 때만 채운다",
6910
6684
  "localCtrlPort": "관측값이다 - 답한 서비스가 실제로 듣는 포트다",
6685
+ "servicePorts": "답한 서비스가 맡은 자리라 엔진이 채운다",
6911
6686
  }
6912
6687
 
6913
6688
  _RENAMED = {
@@ -6915,24 +6690,22 @@ class ILClusterProperty(_AutoAccessor):
6915
6690
  "ilccPort": ("노드마다의 ILClusterNode.ctrlPort", "0.17.0"),
6916
6691
  "judge": ("노드마다의 ILClusterNode.is_coordinator", "0.17.0"),
6917
6692
  "coordinator": ("노드마다의 ILClusterNode.is_coordinator", "0.17.0"),
6693
+ # 0.22.3: 엔진 r3484 부터 늘 None 이던 관측 칸을 걷었다(엔진 r3604 도 걷음) - 승격을 집행하는 자는 컨트롤러 리더다
6694
+ "actingCoordinator": ("controllerLeader", "0.22.3"),
6918
6695
  "authorityHolder": ("controllerLeader (노드 번호다)", "0.17.0"),
6919
6696
  }
6920
6697
 
6921
- def __init__(self, name: str | bytes | bytearray | None = None, *,
6922
- is_auto_start: bool = True,
6923
- ba: bytes | bytearray | None = None) -> None:
6698
+ def __init__(self, name: str | None = None, *, is_auto_start: bool = True) -> None:
6924
6699
  """클러스터 정의를 만든다.
6925
6700
 
6926
6701
  Args:
6927
- name (str | bytes | bytearray | None): 클러스터 이름. ``bytes`` 를 주면 서버
6928
- 응답으로 보고 파싱한다(:class:`ILQueueProperty` 와 같은 관례).
6702
+ name (str | None): 클러스터 이름.
6929
6703
  is_auto_start (bool): 서비스가 뜰 때 같이 띄울지. 기본 True.
6930
- ba (bytes | bytearray | None): 서버가 보낸 바이트(키워드로도 줄 수 있다).
6931
- 주면 나머지 인자는 무시된다.
6932
6704
 
6933
- Raises:
6934
- ILDeserializeException: 바이트 길이가 규격보다 짧거나, **판 표시가 없거나
6935
- (레거시) 이 클라보다 높을 때.**
6705
+ Note:
6706
+ 0.22.3 에서 옛 고정폭 블롭 생성(``ILClusterProperty(bytes)`` · ``ba=``)과 ``get_serialized`` ·
6707
+ ``get_serialized_length`` · ``LAYOUT_MARK`` 를 없앴다 - 엔진 r3601 이 그 형식을 지웠고 0.22.0 부터 클라도 JSON 으로만
6708
+ 주고받는다(:meth:`ILAdminService.getClusterProperty` 가 채운다).
6936
6709
 
6937
6710
  Note:
6938
6711
  ★ **둘째 자리부터는 키워드로만 받는다.** 예전 둘째 자리는 ``ilcc_port`` 였다.
@@ -6941,7 +6714,8 @@ class ILClusterProperty(_AutoAccessor):
6941
6714
  나쁘다.** 키워드 전용으로 막아 ``TypeError`` 로 터지게 한다.
6942
6715
  """
6943
6716
  if isinstance(name, (bytes, bytearray)):
6944
- ba, name = name, ""
6717
+ # 0.22.3: 옛 고정폭 블롭 생성자는 없앴다 - 바이트를 이름으로 조용히 받지 않는다
6718
+ raise TypeError("ILClusterProperty(bytes) was removed in 0.22.3 - the engine speaks JSON (Property v2) only")
6945
6719
  self.name = name or ""
6946
6720
  self.autoStart = is_auto_start
6947
6721
  self.heartbeatIntervalMs = 1000
@@ -6961,76 +6735,10 @@ class ILClusterProperty(_AutoAccessor):
6961
6735
  self.defTerm = None
6962
6736
  self.defVersion = None
6963
6737
  self.signature = ""
6964
- self.actingCoordinator = None
6965
6738
  self.localCtrlPort = None
6966
- #: (내부) 서버가 준 고정부 원본. **읽기만 하는 자리와 읽지도 쓰지도 않는 구간을
6967
- #: 되돌려 보내기 위해** 들고 있다(:meth:`get_serialized` 참조).
6968
- self._rawBytes = b""
6739
+ self.servicePorts: list = []
6969
6740
  self.nodes: list[ILClusterNode] = []
6970
6741
 
6971
- if ba is None:
6972
- return
6973
- if len(ba) < _CLUSTER_LEN + ILC.MIMQ_LIST_SIZE_LENGTH:
6974
- raise ILDeserializeException(
6975
- "invalid byte array length. minimum : ["
6976
- + str(_CLUSTER_LEN + ILC.MIMQ_LIST_SIZE_LENGTH)
6977
- + "] / actual : [" + str(len(ba)) + "]")
6978
-
6979
- ver = _cluster_layout_number(ba)
6980
- if ver > _CLUSTER_LAYOUT_NUMBER:
6981
- raise ILDeserializeException(
6982
- "unsupported cluster layout : the server speaks %r but this client"
6983
- " knows %r - upgrade the client. nothing was parsed."
6984
- % ("V" + str(ver), CLUSTER_LAYOUT_MARK))
6985
- if ver < _CLUSTER_LAYOUT_NUMBER:
6986
- # 규칙은 "낮으면 없던 필드는 미설정" 인데, V2 가 **마커가 있는 첫 판**이라
6987
- # 이 가지는 도달할 수 없다. 도달했다면 블롭이 손상된 것이지 옛 판이 아니다.
6988
- raise ILDeserializeException(
6989
- "unsupported cluster layout : %r is below %r, and %r is the first"
6990
- " layout that carries a marker at all - the blob is damaged"
6991
- % ("V" + str(ver), CLUSTER_LAYOUT_MARK, CLUSTER_LAYOUT_MARK))
6992
-
6993
- o = _CLUSTER_OFF
6994
- self.clusterId = ba_pick_string(ba, o["clusterId"][0], o["clusterId"][1])
6995
- self.name = ba_pick_string(ba, o["name"][0], o["name"][1])
6996
- self.status = ba_pick_int(ba, o["status"][0], o["status"][1])
6997
- # 정확히 "AUTO" 만 자동이다(엔진 규칙). 그 밖은 전부 MANUAL.
6998
- self.autoStart = ba_pick_string(ba, o["startMode"][0], o["startMode"][1]).strip() == "AUTO"
6999
- self.controllerHeartbeatIntervalMs = ba_pick_int_opt(
7000
- ba, o["controllerHeartbeatIntervalMs"][0], o["controllerHeartbeatIntervalMs"][1])
7001
- self.controllerElectionTimeoutMs = ba_pick_int_opt(
7002
- ba, o["controllerElectionTimeoutMs"][0], o["controllerElectionTimeoutMs"][1])
7003
- self.heartbeatIntervalMs = ba_pick_int(ba, o["heartbeatIntervalMs"][0],
7004
- o["heartbeatIntervalMs"][1])
7005
- self.heartbeatMissCount = ba_pick_int(ba, o["heartbeatMissCount"][0],
7006
- o["heartbeatMissCount"][1])
7007
- self.electionDelayMs = ba_pick_int(ba, o["electionDelayMs"][0], o["electionDelayMs"][1])
7008
- self.replicas = ba_pick_int_opt(ba, o["replicas"][0], o["replicas"][1])
7009
- self.minSync = ba_pick_int_opt(ba, o["minSync"][0], o["minSync"][1])
7010
- self.placement = ba_pick_string(ba, o["placement"][0], o["placement"][1]).strip() or None
7011
- self.autoRebalance = ba_pick_string(ba, o["autoRebalance"][0], o["autoRebalance"][1]).strip() or None
7012
- self.secretFile = ba_pick_string(ba, o["secretFile"][0], o["secretFile"][1])
7013
- self.controllerLeader = ba_pick_int_opt(ba, o["controllerLeader"][0],
7014
- o["controllerLeader"][1])
7015
- self.defTerm = ba_pick_int_opt(ba, o["defTerm"][0], o["defTerm"][1])
7016
- self.defVersion = ba_pick_int_opt(ba, o["defVersion"][0], o["defVersion"][1])
7017
- self.signature = ba_pick_string(ba, o["signature"][0], o["signature"][1])
7018
- self.actingCoordinator = ba_pick_int_opt(ba, o["actingCoordinator"][0],
7019
- o["actingCoordinator"][1])
7020
- self.localCtrlPort = ba_pick_int_opt(ba, o["localCtrlPort"][0],
7021
- o["localCtrlPort"][1])
7022
- _sr_off, _sr_len = o["statusReason"]
7023
- self.statusReason = bytes(ba[_sr_off:_sr_off + _sr_len]).split(b"\x00")[0].decode(
7024
- "utf-8", "replace")
7025
- self._rawBytes = bytes(ba[:_CLUSTER_LEN])
7026
-
7027
- offset = _CLUSTER_LEN
7028
- count = ba_pick_int(ba, offset, ILC.MIMQ_LIST_SIZE_LENGTH)
7029
- offset += ILC.MIMQ_LIST_SIZE_LENGTH
7030
- for _ in range(count):
7031
- self.nodes.append(ILClusterNode(ba=ba, base=offset))
7032
- offset += _NODE_LEN
7033
-
7034
6742
  # ── 조회 ────────────────────────────────────────────────────────────
7035
6743
  # ── Property v2(JSON, 엔진 설계 §13-4) ──────────────────────────────────────
7036
6744
  #: 0.22.0: ALTER 로 보낼 수 있는 칸(세터 · 필드 대입으로 바꾼 것만 간다). 종류는 아래 _pv2_* 가 직접 다룬다
@@ -7080,7 +6788,13 @@ class ILClusterProperty(_AutoAccessor):
7080
6788
  object.__setattr__(self, "signature", t(obj, "SIGNATURE", "STR") or "")
7081
6789
  object.__setattr__(self, "controllerLeader", t(obj, "CONTROLLERLEADER", "INT"))
7082
6790
  object.__setattr__(self, "localCtrlPort", t(obj, "LOCALCTRLPORT", "INT"))
7083
- object.__setattr__(self, "actingCoordinator", None)
6791
+ # 0.22.3: PORT = 이 서비스가 맡은 ilcc 자리 포트 "a,b"(맡은 자리가 없으면 "0")
6792
+ ports = []
6793
+ for x in (t(obj, "PORT", "STR") or "").split(","):
6794
+ x = x.strip()
6795
+ if x.isdigit() and int(x) > 0:
6796
+ ports.append(int(x))
6797
+ object.__setattr__(self, "servicePorts", ports)
7084
6798
  nodes = []
7085
6799
  for e in obj.get("NODES", []) or []:
7086
6800
  if not isinstance(e, dict):
@@ -7174,26 +6888,6 @@ class ILClusterProperty(_AutoAccessor):
7174
6888
 
7175
6889
  getBrokerNodes = get_broker_nodes
7176
6890
 
7177
- def get_acting_coordinator(self):
7178
- """**폐기 예정(0.18.1).** 승격을 집행하는 조정자의 노드 번호(:attr:`actingCoordinator`).
7179
-
7180
- 엔진 r3481~r3483 은 이 값이 :attr:`controllerLeader` 의 **사본**이었다 - 승격을 집행하는 자가
7181
- 곧 컨트롤러 리더다. r3484 부터는 엔진이 이 칸을 채우지 않아 늘 ``None`` 이고, 그 자리는
7182
- 다른 값에 다시 쓰이지 않는다.
7183
-
7184
- Returns:
7185
- int | None: 노드 번호. 모르면 ``None``.
7186
-
7187
- Warns:
7188
- DeprecationWarning: 부를 때마다. :attr:`controllerLeader` 를 읽어라.
7189
- """
7190
- warnings.warn("ILClusterProperty.getActingCoordinator() is deprecated - read controllerLeader"
7191
- " (always None from engine r3484)",
7192
- DeprecationWarning, stacklevel=2)
7193
- return self.actingCoordinator
7194
-
7195
- getActingCoordinator = get_acting_coordinator
7196
-
7197
6891
  def _validate_addresses(self) -> None:
7198
6892
  """노드 주소가 서로 겹치는지 **모든 짝**을 본다.
7199
6893
 
@@ -7283,74 +6977,6 @@ class ILClusterProperty(_AutoAccessor):
7283
6977
  return ILC.MIMQ_STARTING_STR
7284
6978
  return str(self.status)
7285
6979
 
7286
- # ── 직렬화 ──────────────────────────────────────────────────────────
7287
- def get_serialized_length(self) -> int:
7288
- """직렬화했을 때의 바이트 길이.
7289
-
7290
- Returns:
7291
- int: ``768 + 5 + 384 x 노드수``. 엔진은 길이를 따로 검사하지 않으므로
7292
- 정확해야 한다.
7293
- """
7294
- return _CLUSTER_LEN + ILC.MIMQ_LIST_SIZE_LENGTH + len(self.nodes) * _NODE_LEN
7295
-
7296
- def get_serialized(self) -> bytes:
7297
- """이 객체를 서버 규격의 바이트로 직렬화한다.
7298
-
7299
- Returns:
7300
- bytes: 서버로 그대로 보낼 수 있는 바이트열.
7301
-
7302
- Note:
7303
- ★ **클라가 값을 만들어 쓰는 자리만 다시 쓴다**(:data:`_CLUSTER_WRITTEN`).
7304
- ``clusterId`` · ``secretFile`` · ``signature`` · ``controllerLeader`` ·
7305
- ``(defTerm, defVersion)`` 은 **읽기만 하고 받은 바이트를 그대로 되보낸다** -
7306
- 0 으로 지워 보내면 엔진이 신원과 판 번호를 잃는다.
7307
-
7308
- ★ **미설정은 빈칸으로 쓴다. 0 을 쓰지 않는다.** ``replicas`` / ``minSync``
7309
- 가 그렇다 - **"말하지 않았다" 를 "3 이라고 말했다" 로 바꾸면** 노드를 더할 때
7310
- 거절당한다(엔진이 명시된 RF 와 노드 수가 어긋난다고 본다).
7311
-
7312
- ★ 늘 적용되는 값은 **빠짐없이** 싣는다 - 자동 기동(비우면 MANUAL 로
7313
- 저장된다)과 노드 감시 하트비트 셋(0 을 보내면 100/1/0 으로 바뀐다).
7314
- """
7315
- ba = bytearray(self.get_serialized_length())
7316
- o = _CLUSTER_OFF
7317
- if len(self._rawBytes) == _CLUSTER_LEN:
7318
- # 읽기만 하는 자리와 모르는 자리를 원본 그대로 되보낸다(노드와 같은 규칙).
7319
- ba[:_CLUSTER_LEN] = self._rawBytes
7320
- for name in _CLUSTER_WRITTEN:
7321
- off, width = o[name]
7322
- ba[off:off + width] = bytes(width)
7323
- else:
7324
- # 서버가 준 바이트가 없는 **새 객체만** status 를 쓴다. 0x00 으로 두면
7325
- # 엔진이 0 으로 읽는데 **0 은 어느 상태도 아니다**(STOPPED 는 1 이다).
7326
- # 왔복일 때는 받은 값을 그대로 나른다 - 읽기만 하는 자리를 다시 쓰지 않는다.
7327
- ba_merge(self.status, ba, o["status"][0], o["status"][1])
7328
- ba_merge(CLUSTER_LAYOUT_MARK, ba, o["layoutVersion"][0], o["layoutVersion"][1] - 1)
7329
- ba_merge(self.name, ba, o["name"][0], o["name"][1] - 1)
7330
- ba_merge("AUTO" if self.autoStart else "MANUAL", ba,
7331
- o["startMode"][0], o["startMode"][1])
7332
- ba_merge(self.heartbeatIntervalMs, ba, o["heartbeatIntervalMs"][0],
7333
- o["heartbeatIntervalMs"][1])
7334
- ba_merge(self.heartbeatMissCount, ba, o["heartbeatMissCount"][0],
7335
- o["heartbeatMissCount"][1])
7336
- ba_merge(self.electionDelayMs, ba, o["electionDelayMs"][0], o["electionDelayMs"][1])
7337
- for _name in ("controllerHeartbeatIntervalMs", "controllerElectionTimeoutMs",
7338
- "replicas", "minSync"):
7339
- _v = getattr(self, _name)
7340
- if _v is not None: # 0 은 "0" 으로, None 은 빈칸으로
7341
- ba_merge(_v, ba, o[_name][0], o[_name][1])
7342
- for _name in ("placement", "autoRebalance"):
7343
- _v = getattr(self, _name)
7344
- if _v: # None / "" 은 빈칸 = 엔진이 저장값 유지
7345
- ba_merge(str(_v), ba, o[_name][0], o[_name][1])
7346
- offset = _CLUSTER_LEN
7347
- ba_merge(len(self.nodes), ba, offset, ILC.MIMQ_LIST_SIZE_LENGTH)
7348
- offset += ILC.MIMQ_LIST_SIZE_LENGTH
7349
- for node in self.nodes:
7350
- ba[offset:offset + _NODE_LEN] = node.get_serialized()
7351
- offset += _NODE_LEN
7352
- return bytes(ba)
7353
-
7354
6980
  def __str__(self) -> str:
7355
6981
  """사람이 읽는 요약(운영 화면 출력용).
7356
6982
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jetstream-api
3
- Version: 0.22.1
3
+ Version: 0.22.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,12 +60,29 @@ lz4 배치 압축을 쓰려면 `pip install "jetstream-api[lz4]"`, zstd 배치
60
60
  ## 설치 방법
61
61
  pip install jetstream-api
62
62
 
63
+ ## 0.22.3 변경 - 옛 클러스터 블롭 코덱 · actingCoordinator 제거, servicePorts (자바 v2.18.3 와 같음, 짝 엔진 7.0.1.3603 이상 권장)
64
+
65
+ 0.22.2 는 시험 판(test.pypi)만 내고 버렸다 - actingCoordinator 제거를 묶어 0.22.3 으로 나간다.
66
+
67
+ - **없앰**(호환 깨짐): 옛 고정폭 클러스터 블롭 코덱 - ``ILClusterProperty(bytes)`` · ``ba=`` · ``get_serialized`` · ``get_serialized_length`` ·
68
+ ``LAYOUT_MARK``, ``ILClusterNode(bytes)`` · ``ba=`` · ``base=`` · ``get_serialized``, 모듈 ``CLUSTER_LAYOUT_MARK``,
69
+ ``ILC.MIMQ_VO_CLUSTER_LEN`` / ``MIMQ_VO_CLUSTER_NODE_LEN``. 바이트를 주면 ``TypeError``. 엔진 r3601 이 그 형식을 지웠고 0.22.0 부터
70
+ JSON(Property v2)으로만 주고받아 쓰는 경로가 없었다. 능력 선언 ``clsblob=2`` 는 그대로.
71
+ - **없앰**(호환 깨짐): ``ILClusterProperty.actingCoordinator`` · ``get_acting_coordinator`` / ``getActingCoordinator`` - 0.18.1 부터 폐기 예정,
72
+ 엔진 7.0.1.3484 부터 늘 ``None`` 이던 관측 칸이다(엔진 r3604 도 걷음). ``controllerLeader`` 를 읽는다 - 옛 이름으로 부르면 그렇게 안내한다.
73
+ - **더함**: ``ILClusterProperty.servicePorts`` / ``getServicePorts()`` (읽기 전용) - 응답한 서비스가 맡은 컨트롤러(ilcc) 자리의 포트 전부
74
+ (엔진 ``PORT`` "a,b", 맡은 자리가 없으면 빈 목록). 컨트롤러가 멈춰 있어도 나온다 - 지금 듣는 포트는 ``localCtrlPort``.
75
+ - 노드 ``status`` · ``nodeUuid`` · ``defTerm`` · ``defVersion`` 은 이름만 남는다(엔진 r3601 부터 와이어에 없어 늘 빈 값) - 문서를 고침.
76
+ - 문서 정정: 팔로워 거절 ``MIMQE_OPERATION_DENIED (STANDBY)`` 는 **토픽**의 생성 · 변경 · 삭제만이다(쿼터 · 디스크 가드 · 클러스터에는 없다).
77
+ - 엔진 r3603: 클러스터 서명(``signature``) 값의 재료가 정의의 공유 칸 전부로 넓어져 값이 바뀐다(키 · 형식 그대로, 클라 변경 없음).
78
+
63
79
  ## 0.22.1 변경 - 노드 추가 결과에 컨트롤러 기동 여부 (자바 v2.18.1 와 같음, started · wait 는 짝 엔진 7.0.1.3602 이상)
64
80
 
65
81
  - ``addNode`` 결과(``ILClusterNodeAddResult``)에 ``started`` · ``wait``(``is_started()``): 자리를 맡은 서비스가 컨트롤러를 **띄웠는가**와
66
82
  안 띄운 까닭(``MANUAL`` - 정의 STARTMODE 가 MANUAL 이라 :meth:`startCluster` 로 띄운다 · ``STOPPED`` - 사용자가 멈춰 둠 · ``FAILED`` -
67
83
  띄우다 실패, ilsvc 로그). 그 전 엔진은 이 칸을 주지 않아 맡았으면 띄운 것으로 본다(종전 동작).
68
- - 문서: 클러스터 팔로워 노드에 보낸 생성 · 변경 · 삭제는 ``MIMQE_OPERATION_DENIED (STANDBY, leader=..)``, 조회 · 목록은 팔로워도 답한다.
84
+ - 문서: **토픽**의 생성 · 변경 · 삭제를 클러스터 팔로워 노드에 보내면 ``MIMQE_OPERATION_DENIED (STANDBY, leader=..)``, 조회 · 목록은
85
+ 팔로워도 답한다. 쿼터 · 디스크 가드 · 클러스터(서비스가 받는 설정)에는 이 거절이 없다(배포 뒤 정정 - 처음에는 네 오브젝트 모두로 잘못 적었다).
69
86
 
70
87
  ## 0.22.0 변경 - 7 신규 오브젝트(쿼터 · 디스크 가드 · 토픽 · 클러스터) 관리를 Property v2(JSON)로 (자바 v2.18.0 와 같음, 짝 엔진 7.0.1.3601 이상)
71
88
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "jetstream-api"
7
- version = "0.22.1"
7
+ version = "0.22.3"
8
8
  description = "Python client API for iLink M.O.M. (Message Oriented Middleware)"
9
9
  authors = [{name = "snowjeans"}]
10
10
  readme = "README.md"
File without changes