Files
multi-agent-mux/docs/contracts/issue-5/phase1-benchmark.md
T
GodopuandClaude Opus 5 6ad6f08cc6 docs(issue-5): fold in the two non-blocking residuals from cross-review round 2
Both reviewers passed and each left one documentation item.

Grok: the benchmark's fork table still named the shell path `-m
lib_py.delegation.cli`, which is what R1 replaced. It now names the actual
bootstrap, and says why the module-execution form was abandoned. The fork count
is unchanged either way.

Claude (O-4): inside a freeze tree, _delegate_py_bin walks up from its own
location for a .venv and finds none, so it falls back to a system python that
has no paho. Harmless in phase 1, because nothing in production calls the frozen
lib.sh helpers; it becomes real in phase 3 when the loop process starts doing
exactly that. Recorded on P3-02 rather than fixed here, since the fix belongs
with the code that will depend on it. Verified the premise: the current freeze
root has no .venv.

No code changed. The unanimous PASS was issued on 95f3e9a.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UrDNJkHmm18uu7n7JwV5Qd
2026-09-06 17:21:12 +09:00

92 lines
4.6 KiB
Markdown

# 📊 Issue #5 Phase 1 — 장수 MQTT 연결 벤치마크 실측
- **문서 버전**: Rev.1
- **측정 일시**: 2026-09-06
- **기준 커밋**: Phase 1 커밋 4 (`feat(issue-5): persistent MQTT publisher prototype and benchmark harness`)
- **하니스**: `python -m lib_py.delegation.bench --n <N> --transport {fake|broker}`
- **근거 대상**: `implementation_plan.md` §5 "MQTT 연결 재사용 p50 단축" 항목
---
## 1. 측정 환경
| 항목 | 값 |
|---|---|
| Python | 3.9.6 (`.venv`) |
| paho-mqtt | 2.1.0 |
| 플랫폼 | darwin-arm64 |
| 로컬 브로커 | **없음** (`mosquitto`, `nats-server`, `docker` 모두 미설치) |
| 실브로커 | `broker.hivemq.com:1883` (PoC 기본 공용 브로커, TLS 없음) |
> ⚠️ 로컬 브로커가 없으므로 실브로커 수치는 공용 인터넷 브로커에 대한 **1회 측정치**이며, 네트워크 상태에 좌우된다. **게이트 조건이 아니다.** 게이트가 걸린 것은 아래 §2의 결정적 지표뿐이다.
---
## 2. 결정적 지표 — 가짜 전송 (CI 단정 대상)
합성 연결 지연 50 ms, N = 50.
| 지표 | OneShot | Persistent |
|---|---:|---:|
| 발행 수 | 50 | 50 |
| **연결 수립 횟수** | **50** | **1** |
| 실패 | 0 | 0 |
| 발행당 지연 p50 | 54.813 ms | **0.002 ms** |
| 발행당 지연 p95 | 55.049 ms | 0.003 ms |
| 발행당 지연 max | 55.075 ms | 51.110 ms (최초 연결) |
`tests/test_issue5_phase1_delegation.py::test_bench_fake_transport_schema_and_counts`가 "OneShot = N, Persistent = 1" 및 p50 감소를 단정한다. 네트워크 접촉 0.
---
## 3. 정보성 지표 — 실브로커 (게이트 아님)
`broker.hivemq.com:1883`, N = 5.
| 지표 | OneShot | Persistent |
|---|---:|---:|
| 연결 수립 횟수 | 5 | 1 |
| 실패 | 0 | 0 |
| 발행당 지연 p50 | 1892.728 ms | **275.992 ms** |
| 발행당 지연 p95 | 1942.762 ms | 875.412 ms |
| 발행당 지연 max | 1942.762 ms | 875.412 ms (최초 연결) |
p50 기준 약 **85% 단축**. 연결 수립이 발행 지연의 지배적 요소임을 보여준다.
### 3-1. 이 프로토타입의 실측 동기
Rev.1 계획 job의 `started` 이벤트는 payload timestamp 05:58:49Z에서 모니터 수신 05:59:01.487Z까지 **약 12.5초**가 걸렸고, 원인은 `CONNECT_ACK_TIMEOUT=10` 1회 실패 후 재연결이었다. 본 job(`ddcaf930`)의 `started` 발행에서도 같은 CONNACK 경고가 재현됐다. 1회성 연결은 이 비용을 매 이벤트마다 다시 지불한다.
---
## 4. 프로세스 포크 수 (셸 경로, 분석값)
`delegate_publish_event` 1회당:
| 구현 | fork |
|---|---:|
| 레거시 (`_delegate_publish_event_legacy`) | 3 (`$(_delegate_py_bin)`, `$(_delegate_script)`, python) |
| 신규 (`_delegation_py` → `python -c` + `sys.path.insert(0, SKILL_DIR)`) | 3 (`$(_delegate_py_bin)`, `$(_delegate_script)`, python) |
> 신규 경로는 `-m`이 아니라 `_delegation_py`의 명시 부트스트랩이다(교차 리뷰 R1 대응: `-m`은 cwd가 `PYTHONPATH`보다 우선하므로 freeze 보장이 성립하지 않았다). fork 수는 그와 무관하게 동일하다.
>
> 계획서 D8은 신규 경로가 2 fork일 것으로 예상했으나, **실제 구현은 3 fork로 동일하다.** `_delegate_script publish_event.py` 조회를 유지했기 때문이다.
>
> **이 조회가 실제로 보장하는 것 (교차 리뷰 `cc3468cb` 정정)**: 초기 서술은 이 조회가 "비치명 계약(C-02)을 만드는 유일한 지점"이라고 했으나 부정확하다. rc 0 자체는 `delegate_publish_event`의 `|| true`만으로도 보장되고, C-02도 rc만 단정한다. 조회가 보장하는 것은 **위임 스크립트 트리가 없을 때 인터프리터를 아예 띄우지 않아 stderr에 트레이스백을 남기지 않는다는 정숙성과, 레거시 본체와의 동작 동일성**이다(인터프리터 미호출 단정은 C-01에 있다). 유지 결정 자체는 세 리뷰어 전원이 지지했으며, fork 절감은 Phase 4에서 이 계약과 함께 재검토한다.
---
## 5. 프로덕션 적용 범위
**Phase 1에서 장수 연결은 프로덕션 경로에 쓰이지 않는다.** CLI와 셸 헬퍼는 발행 1회 후 프로세스가 종료되므로 재사용 이득이 없고, 두 경로 모두 `OneShotPublisher`를 계속 쓴다. 첫 소비자는 Phase 2a의 장수 엔진이다.
## 6. 재현 명령
```bash
export PYTHONPATH="$PWD/.agents/skills"
.venv/bin/python -m lib_py.delegation.bench --n 50 --transport fake --connect-latency-ms 50
# 실브로커(임시 레지스트리 필요)
.venv/bin/python -m lib_py.delegation.cli submit --prompt bench --registry-dir .mam/jobs
.venv/bin/python -m lib_py.delegation.bench --n 5 --transport broker --job <id> --registry-dir .mam/jobs
```