messaging-superpowers-package 설계서/계획서 기반 구현. registry를 19 → 43 leaf로 확장하고 src/messaging 아래 24개 leaf를 등록. - core-api: M1 publish/consume + M2 batch·delayed·pause-resume - policy/transport-spi: 재시도 결정, DLQ orchestration, admission control, lifecycle - kafka·rabbit(Stable): contiguous commit, confirm/return 상관, 배치, 보안 설정 - pulsar·nats(Experimental): 기본 비활성, live 인증 없음을 코드로 기록 - outbox/inbox/claim-check: 트랜잭션 결합, lease, 무결성 검증 - admin: plan → approve → execute를 타입으로 강제 - 문서 9종, infra compose 7종, JMH 벤치마크 3종 검증: 아키텍처 게이트 3종 통과, 24개 leaf 전부 check 통과, messaging 테스트 604개 통과/0 실패. 미완: 계획서가 요구한 실 브로커 IT 40개 중 7개만 작성. Rabbit 13 / Outbox 6 / Inbox 4 / NATS·Pulsar·Share 5 / testkit 2 / starter·admin 3, 그리고 TLS·ACL 2개가 남음.
98 lines
3.7 KiB
Markdown
98 lines
3.7 KiB
Markdown
# Experimental 정책
|
|
|
|
## Stable과 Experimental의 차이
|
|
|
|
**Stable**은 공통 Contract Suite(`MessagingAdapterContract`)를 변경 없이 통과한 어댑터다.
|
|
컴파일되는 어댑터가 아니라, 아래 7가지를 실제로 증명한 어댑터다.
|
|
|
|
```text
|
|
publishesAndConfirms
|
|
returnsAmbiguousWhenConfirmIsLost
|
|
redeliversWhenSettlementIsLost
|
|
preservesMessageIdAcrossRetryAndDlq
|
|
keepsSourceUnsettledWhenDlqPublishFails
|
|
rejectsOversizedPayloadBeforeTransport
|
|
stopsAcceptingNewWorkDuringShutdown
|
|
```
|
|
|
|
**Experimental**은 아직 그 증명이 끝나지 않은 어댑터다.
|
|
|
|
## 규칙
|
|
|
|
### 1. 기본 비활성
|
|
|
|
```yaml
|
|
messaging.experimental.kafka-share: false
|
|
messaging.experimental.pulsar: false
|
|
messaging.experimental.nats: false
|
|
```
|
|
|
|
활성화하지 않으면 validator가 `MessagingCapabilityUnavailableException`을 던진다.
|
|
Contract Suite가 아직 증명 중인 어댑터가 누군가의 기본 설정 때문에 load-bearing이 되어서는 안 된다.
|
|
|
|
### 2. Stable 모듈이 Experimental 모듈에 의존하지 않는다
|
|
|
|
Gradle 의존 그래프로 강제된다. `messaging-spring-boot-starter`의 `allowed_dependencies`에
|
|
`messaging-kafka-share-experimental`, `messaging-pulsar-experimental`,
|
|
`messaging-nats-experimental`, `messaging-spring-cloud-stream-bridge`가 **없다**.
|
|
|
|
`verifyCleanArchitectureDependencies`가 위반을 빌드 실패로 만든다.
|
|
|
|
### 3. Core 계약을 바꾸지 않는다
|
|
|
|
Experimental 어댑터는 브로커의 차이를 `MessagingCapabilities`로 표현할 뿐,
|
|
`messaging-core-api`의 타입을 바꾸지 않는다.
|
|
|
|
### 4. 없는 기능을 광고하지 않는다
|
|
|
|
| 어댑터 | 광고하지 않는 것 | 이유 |
|
|
|---|---|---|
|
|
| Kafka Share Group | orderedStream, keyedOrdering, replay, brokerTransaction | 경쟁 소비자 + 개별 ack는 partition 순서를 유지할 수 없다 |
|
|
| Pulsar | brokerTransaction | Pulsar에 있지만 플랫폼 Contract Suite로 증명되지 않았다 |
|
|
| Pulsar (Shared) | keyedOrdering | round-robin 분배 |
|
|
| NATS JetStream | nativeDeadLetter | delivery limit 초과 시 terminate할 뿐 라우팅하지 않는다 |
|
|
| NATS JetStream | keyedOrdering | subject 기반 모델에 per-key 순서가 없다 |
|
|
|
|
`false`인 capability를 요구하는 profile은 startup에서 실패한다.
|
|
조용히 약화되지 않는다.
|
|
|
|
### 5. 명시적 거부
|
|
|
|
| 조합 | 결과 |
|
|
|---|---|
|
|
| Kafka Share Group + ordering != NONE | 거부 |
|
|
| Kafka Share Group + pause/resume | `MessagingCapabilityUnavailableException` |
|
|
| Pulsar Shared + ordering=KEY | 거부 (Key_Shared 필요) |
|
|
| Pulsar + ordering=DESTINATION | 거부 |
|
|
| NATS Core + AT_LEAST_ONCE | 거부 (JetStream 필요) |
|
|
| NATS ordered consumer + 경쟁 워커 > 1 | 거부 |
|
|
| NATS + ordering=KEY | 거부 |
|
|
|
|
## Spring Cloud Stream bridge
|
|
|
|
Experimental이 아니라 **Optional**이다. 위험이 다르다.
|
|
|
|
Stream은 자체 binder 설정을 소유하므로, binding이 destination profile이 모르는
|
|
serializer·error handling·acknowledgement mode를 조용히 획득할 수 있다.
|
|
|
|
따라서 브리지는 **플랫폼 보장에 의존하지 않는 destination에만** 허용한다.
|
|
|
|
```text
|
|
ordering scope 선언 → 거부
|
|
retry policy 선언 → 거부
|
|
dead letter 선언 → 거부
|
|
```
|
|
|
|
이 셋 중 하나라도 필요하면 native adapter를 쓴다. 거기서만 실제로 강제되기 때문이다.
|
|
|
|
## 승격 조건
|
|
|
|
Experimental → Stable로 올리려면 전부 필요하다.
|
|
|
|
1. `MessagingAdapterContract` 7개 테스트를 변경 없이 통과
|
|
2. 장애 주입(연결 끊김, confirm 유실, settlement 유실) 하에서 통과
|
|
3. 지원 브로커 버전 범위 명시 및 CI 검증
|
|
4. `support-matrix.md`의 capability 표 갱신
|
|
5. ADR 작성
|
|
6. 기본 활성화 여부에 대한 별도 결정
|