<!-- task=m-performance-hotspot-optimization/09_dart_tcp_latency_heartbeat plan=0 tag=REFACTOR -->

# Plan - REFACTOR

## 이 파일을 읽는 구현 에이전트에게

`CODE_REVIEW-cloud-G07.md`의 구현 에이전트 소유 섹션 작성은 필수다. 구현 후 검증 명령을 실행하고 실제 출력, 계획 대비 변경, 설계 결정을 채운 뒤 active 파일을 그대로 두고 리뷰 준비를 보고한다. 사용자 결정, 사용자 소유 외부 환경, 범위 충돌로 막히면 리뷰 stub의 `사용자 리뷰 요청`에 근거를 기록하고 멈춘다. 직접 사용자에게 질문하거나 `USER_REVIEW.md`/`complete.log`를 만들지 않는다.

## 배경

Dart TCP는 40ms대 fixed latency, burst/sustained throughput 저하가 반복된다. 1MB/buffer-copy 개선은 끝났지만 `tcpNoDelay`, per-packet `flush()`, heartbeat timer churn은 아직 같은 경로에 남아 있다. 이 계획은 세 후보를 한 번에 분리하고, 안정성 hard gate를 유지한 채 완료 가능한 개선만 적용한다.

## 사용자 리뷰 요청 흐름

구현 중 사용자 결정이 필요한 경우 active review stub의 `사용자 리뷰 요청` 섹션에 exact decision/evidence/resume condition을 기록한다. 구현 에이전트는 채팅으로 직접 질문하지 않는다.

## Roadmap Targets

- Milestone: `agent-roadmap/milestones/performance-hotspot-optimization.md`
- Task ids:
  - `dart-tcp-fixed-latency`: Dart TCP 40ms대 고정 지연 원인 분리
  - `dart-tcp-burst-sustained`: Dart TCP burst/sustained 처리량 저하 분리
  - `dart-heartbeat-cost`: Dart heartbeat timer churn 성능 경로 분리
- Completion mode: check-on-pass

## 분석 결과

### 읽은 파일

- `agent-ops/rules/project/rules.md`
- `agent-ops/rules/common/rules-roadmap.md`
- `agent-ops/skills/common/router.md`
- `agent-ops/skills/common/plan/SKILL.md`
- `agent-test/local/rules.md`
- `agent-test/local/dart-smoke.md`
- `agent-test/local/proto-socket-performance-baseline.md`
- `agent-test/local/proto-socket-full-matrix.md`
- `agent-roadmap/current.md`
- `agent-roadmap/milestones/performance-hotspot-optimization.md`
- `agent-ops/rules/project/domain/dart/rules.md`
- `dart/lib/src/protobuf_client.dart`
- `dart/lib/src/protobuf_server.dart`
- `dart/lib/src/heartbeat_mixin.dart`
- `dart/lib/src/base_client.dart`
- `dart/lib/src/communicator.dart`
- `dart/lib/src/ws_protobuf_client_io.dart`
- `dart/lib/src/ws_protobuf_client_web.dart`
- `dart/lib/src/ws_protobuf_server.dart`
- `dart/lib/src/transport.dart`
- `dart/bench/stress.dart`
- `dart/test/socket_test.dart`
- `dart/test/communicator_test.dart`
- `dart/pubspec.yaml`

### 테스트 환경 규칙

`test_env=local`. `dart-smoke`는 Dart 구현 변경에 `cd dart && dart pub get && dart test && dart compile js test/browser_ws_import_compile.dart -o /tmp/proto_socket_browser_ws_import_compile.js`를 요구한다. `proto-socket-performance-baseline`은 성능 관련 코드 변경 후 `bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_performance.sh --quick`을 요구한다. 좁은 병목 확인은 `run_stress.sh --quick --lang dart --transport tcp --profile ...`를 보조로 사용한다.

### 테스트 커버리지 공백

- `tcpNoDelay` 적용: 기존 테스트 없음. socket option 자체는 직접 관찰이 어려우므로 focused stress row와 기존 TCP 통합 테스트로 확인한다.
- per-packet `flush()` 변경: existing `socket_test.dart`가 TCP/SSL/request/large fragmented frame을 커버하지만 drain timing 회귀는 stress row로 확인해야 한다.
- heartbeat interval 0 정책: 기존 stress 주석은 0이 즉시 발화라고 기록한다. interval 0 disabled 전환 시 단위 테스트를 추가한다.

### 심볼 참조

rename/remove 없음. `sendHeartBeat` 호출 위치는 `dart/lib/src/protobuf_client.dart:56,110`, `dart/lib/src/ws_protobuf_client_io.dart:49,71`, `dart/lib/src/ws_protobuf_client_web.dart` 동일 수신/초기화 경로다.

### 분할 판단

공유 task group은 `m-performance-hotspot-optimization`이다. 이 plan은 `09_dart_tcp_latency_heartbeat` 단일 subtask다. `tcpNoDelay`, flush, heartbeat는 같은 TCP latency/burst/sustained row에 함께 나타나고, 개별 적용 전후를 같은 focused stress 명령으로 비교해야 하므로 한 리뷰 단위가 더 안전하다. isolate gateway는 별도 ownership/risk라 `10/11/12`로 분리한다.

### 범위 결정 근거

이미 완료한 `_TcpFrameBuffer`/large payload copy 개선은 수정하지 않는다. WS latency 개선, isolate gateway 연결, Kotlin/TypeScript 작업은 제외한다. protocol schema와 generated packets는 건드리지 않는다.

### 빌드 등급

`cloud-G07`. 성능 진단, socket option, timer semantics, benchmark evidence가 핵심이며 terminal benchmark-style 검증이 필요하다.

## 구현 체크리스트

- [ ] TCP connect/connectSecure 경로에서 TCP no-delay 적용 가능 여부를 구현하고 Dart TCP focused roundtrip row로 원인을 분리한다.
- [ ] `_TcpSocketTransport.writePacket`의 per-packet `flush()` 비용을 안전하게 줄이거나 유지 사유를 문서화하고 burst/sustained row로 검증한다.
- [ ] heartbeat interval 0을 disabled semantics로 정리하거나 유지 사유를 문서화하고 timer churn 테스트를 추가한다.
- [ ] `run_stress.sh --quick --lang dart --transport tcp --profile roundtrip,burst,sustained`와 `run_performance.sh --quick` 결과를 기록한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [REFACTOR-1] TCP no-delay and flush policy

문제: `ProtobufClient.connect`는 `Socket.connect` 결과를 그대로 반환하고(`dart/lib/src/protobuf_client.dart:20-22`), `_TcpSocketTransport.writePacket`은 모든 packet마다 `flush()`를 await한다(`dart/lib/src/protobuf_client.dart:129-139`). 이 조합은 TCP fixed latency와 burst throughput 후보로 남아 있다.

해결 방법:

```dart
// Before: dart/lib/src/protobuf_client.dart:20
static Future<Socket> connect(String host, int port) =>
    Socket.connect(host, port);
```

```dart
// After sketch
static Future<Socket> connect(String host, int port) async {
  final socket = await Socket.connect(host, port);
  socket.setOption(SocketOption.tcpNoDelay, true);
  return socket;
}
```

flush는 제거 전후를 focused stress로 비교한다. 안정성/close 회귀가 있으면 `flush()`는 유지하고 원인을 문서화한다.

수정 파일 및 체크리스트:
- [ ] `dart/lib/src/protobuf_client.dart`: no-delay helper와 flush 정책 적용 또는 유지 근거
- [ ] `dart/test/socket_test.dart`: TCP 통합 회귀가 깨지지 않는지 확인

테스트 작성: no-delay 직접 단위 테스트는 skip한다. Dart `Socket.setOption` 성공 여부는 public observable contract가 아니므로 stress row와 integration test로 검증한다.

중간 검증:

```bash
cd dart && dart test test/socket_test.dart -r expanded
```

### [REFACTOR-2] Heartbeat disabled semantics and churn test

문제: `sendHeartBeat()`는 frame 수신마다 기존 checker를 `responded()`로 취소하고 새 `ResponseChecker.second`를 만든다(`dart/lib/src/heartbeat_mixin.dart:20-35`). `dart/bench/stress.dart:35-39`는 interval 0이 비활성이 아니라 즉시 발화라서 stress가 1일 값을 쓰는 우회 상태라고 기록한다.

해결 방법:

```dart
// Before: dart/lib/src/heartbeat_mixin.dart:20
void sendHeartBeat() {
  if (isAlive) {
    _heartbeatChecker?.responded();
```

```dart
// After sketch
bool get _heartbeatEnabled => _heartbeatIntervalTime > 0 && _heartbeatWaitTime > 0;
void sendHeartBeat() {
  if (!isAlive || !_heartbeatEnabled) return;
  ...
}
```

stress harness의 `_heartbeatSeconds`는 0으로 바꾸고, interval 0 연결이 heartbeat timeout으로 닫히지 않는 테스트를 추가한다.

수정 파일 및 체크리스트:
- [ ] `dart/lib/src/heartbeat_mixin.dart`: interval/wait 0 disabled semantics
- [ ] `dart/bench/stress.dart`: heartbeat 우회값 제거
- [ ] `dart/test/socket_test.dart`: heartbeat disabled 유지 테스트

테스트 작성: 작성한다. TCP 클라이언트/server heartbeat interval 0 케이스가 500ms 이상 유지되고 send/receive가 가능한지 검증한다.

중간 검증:

```bash
cd dart && dart test test/socket_test.dart -r expanded
```

### [REFACTOR-3] Focused performance evidence

문제: fixed latency/burst/sustained task는 기능 테스트만으로 완료할 수 없다. `dart/bench/stress.dart:362-460`이 roundtrip/burst/sustained rows를 제공하므로 같은 host에서 변경 후 row를 기록해야 한다.

해결 방법: focused stress를 먼저 실행하고, 이후 performance quick으로 project-wide 성능 회귀를 확인한다. 결과 파일에서 Dart TCP concurrency 16/64/256, burst quick, sustained quick row의 p99/throughput/stability를 review stub에 붙인다.

수정 파일 및 체크리스트:
- [ ] 코드 변경 없음. 검증 산출물 수집.

테스트 작성: skip. 이 항목은 benchmark evidence 수집이다.

중간 검증:

```bash
bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_stress.sh --quick --lang dart --transport tcp --profile roundtrip,burst,sustained
```

## 수정 파일 요약

| 파일 | 항목 |
|---|---|
| `dart/lib/src/protobuf_client.dart` | REFACTOR-1 |
| `dart/lib/src/heartbeat_mixin.dart` | REFACTOR-2 |
| `dart/bench/stress.dart` | REFACTOR-2, REFACTOR-3 |
| `dart/test/socket_test.dart` | REFACTOR-1, REFACTOR-2 |

## 최종 검증

```bash
cd dart && dart test test/socket_test.dart test/communicator_test.dart -r expanded
cd dart && dart compile js test/browser_ws_import_compile.dart -o /tmp/proto_socket_browser_ws_import_compile.js
bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_stress.sh --quick --lang dart --transport tcp --profile roundtrip,burst,sustained
bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_performance.sh --quick
```

모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
