feat(messaging): 브로커 중립 메시징 플랫폼 24개 leaf 추가
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개가 남음.
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
# Kafka 4.3.x in KRaft mode.
|
||||
#
|
||||
# Single broker on purpose: this compose file exists to reproduce the platform's Stable profile
|
||||
# locally, not to model a production cluster. The settings below are the ones the profile guard
|
||||
# enforces, so a local run fails the same way a misconfigured deployment would.
|
||||
services:
|
||||
kafka:
|
||||
image: apache/kafka:4.3.0
|
||||
container_name: messaging-kafka
|
||||
ports:
|
||||
- "9092:9092"
|
||||
environment:
|
||||
KAFKA_NODE_ID: 1
|
||||
KAFKA_PROCESS_ROLES: broker,controller
|
||||
KAFKA_LISTENERS: PLAINTEXT://:9092,CONTROLLER://:9093
|
||||
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092
|
||||
KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER
|
||||
KAFKA_CONTROLLER_QUORUM_VOTERS: 1@localhost:9093
|
||||
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT
|
||||
# acks=all is only a durability guarantee when more than one replica must acknowledge.
|
||||
# With a single broker the platform still requires acks=all; min.insync.replicas is 1 here
|
||||
# and is expected to be 2 in any environment that claims replication evidence.
|
||||
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
|
||||
KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1
|
||||
KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
|
||||
KAFKA_MIN_INSYNC_REPLICAS: 1
|
||||
# Topology is created by infrastructure, never by the application.
|
||||
KAFKA_AUTO_CREATE_TOPICS_ENABLE: "false"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "/opt/kafka/bin/kafka-broker-api-versions.sh --bootstrap-server localhost:9092 >/dev/null 2>&1"]
|
||||
interval: 5s
|
||||
timeout: 10s
|
||||
retries: 20
|
||||
@@ -0,0 +1,21 @@
|
||||
# NATS 2.14.x with JetStream, Experimental tier.
|
||||
#
|
||||
# JetStream is mandatory: core NATS is fire-and-forget with no persistence and no acknowledgement,
|
||||
# so an at-least-once destination configured against it would report success for messages that were
|
||||
# never stored. The adapter's validator refuses that combination.
|
||||
services:
|
||||
nats:
|
||||
image: nats:2.14-alpine
|
||||
container_name: messaging-nats
|
||||
ports:
|
||||
- "4222:4222"
|
||||
- "8222:8222"
|
||||
command:
|
||||
- "--jetstream"
|
||||
- "--store_dir=/data"
|
||||
- "--http_port=8222"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "wget -q -O- http://localhost:8222/healthz || exit 1"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 20
|
||||
@@ -0,0 +1,27 @@
|
||||
# PostgreSQL 16 for the Outbox and Inbox.
|
||||
#
|
||||
# logical replication is enabled so the optional Debezium CDC relay can be exercised against the
|
||||
# same database the polling relay uses; the two must produce an identical wire contract.
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:16-alpine
|
||||
container_name: messaging-postgres
|
||||
ports:
|
||||
- "5432:5432"
|
||||
environment:
|
||||
POSTGRES_DB: messaging
|
||||
POSTGRES_USER: messaging
|
||||
POSTGRES_PASSWORD: messaging
|
||||
command:
|
||||
- "postgres"
|
||||
- "-c"
|
||||
- "wal_level=logical"
|
||||
- "-c"
|
||||
- "max_replication_slots=4"
|
||||
- "-c"
|
||||
- "max_wal_senders=4"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U messaging -d messaging"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 20
|
||||
@@ -0,0 +1,17 @@
|
||||
# Pulsar 4.0 LTS, Experimental tier.
|
||||
#
|
||||
# Present so the Experimental adapter can be exercised, not because it is supported. The adapter
|
||||
# stays disabled unless backend.messaging.experimental.pulsar=true.
|
||||
services:
|
||||
pulsar:
|
||||
image: apachepulsar/pulsar:4.0.3
|
||||
container_name: messaging-pulsar
|
||||
ports:
|
||||
- "6650:6650"
|
||||
- "8080:8080"
|
||||
command: bin/pulsar standalone --no-functions-worker --no-stream-storage
|
||||
healthcheck:
|
||||
test: ["CMD", "bin/pulsar-admin", "brokers", "healthcheck"]
|
||||
interval: 10s
|
||||
timeout: 10s
|
||||
retries: 20
|
||||
@@ -0,0 +1,22 @@
|
||||
# RabbitMQ 4.3.x.
|
||||
#
|
||||
# Quorum queues are the default for durable work queues in this platform, so the classic mirroring
|
||||
# policy is deliberately absent: classic mirrored queues can lose acknowledged messages during a
|
||||
# partition, which is precisely the guarantee a durable work queue exists to provide.
|
||||
services:
|
||||
rabbitmq:
|
||||
image: rabbitmq:4.3-management
|
||||
container_name: messaging-rabbitmq
|
||||
ports:
|
||||
- "5672:5672"
|
||||
- "15672:15672"
|
||||
environment:
|
||||
RABBITMQ_DEFAULT_USER: messaging
|
||||
RABBITMQ_DEFAULT_PASS: messaging
|
||||
# Publisher confirms and returns are client-side settings; the profile guard enforces them.
|
||||
RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS: "-rabbit consumer_timeout 1800000"
|
||||
healthcheck:
|
||||
test: ["CMD", "rabbitmq-diagnostics", "-q", "check_running"]
|
||||
interval: 5s
|
||||
timeout: 10s
|
||||
retries: 20
|
||||
@@ -0,0 +1,35 @@
|
||||
# TLS material
|
||||
|
||||
Production profiles require TLS **and** hostname verification; `MessageSecurityValidator` fails
|
||||
startup without either.
|
||||
|
||||
No key material is committed here, and none should be. Certificates are issued by the deployment's
|
||||
own PKI and mounted at runtime; a keystore in a repository is a credential in a repository, and
|
||||
rotating it means a commit.
|
||||
|
||||
## Local development
|
||||
|
||||
The compose files in the sibling directories run plaintext listeners deliberately. They exist to
|
||||
reproduce the *messaging* semantics locally, not the transport security, and running them with
|
||||
`production: false` in the destination profile is what keeps the validator honest — a profile marked
|
||||
`production: true` against a plaintext broker must fail, and that is a test, not an inconvenience.
|
||||
|
||||
## Generating a local CA for TLS testing
|
||||
|
||||
```bash
|
||||
openssl req -x509 -newkey rsa:4096 -sha256 -days 30 -nodes \
|
||||
-keyout ca.key -out ca.crt -subj "/CN=messaging-local-ca"
|
||||
|
||||
openssl req -newkey rsa:4096 -nodes -keyout broker.key -out broker.csr \
|
||||
-subj "/CN=localhost"
|
||||
|
||||
openssl x509 -req -in broker.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
|
||||
-out broker.crt -days 30 -sha256 \
|
||||
-extfile <(printf "subjectAltName=DNS:localhost,IP:127.0.0.1")
|
||||
```
|
||||
|
||||
The `subjectAltName` is not optional. Hostname verification is required in production profiles, and
|
||||
a certificate without a SAN fails it — which is the correct outcome, not something to work around by
|
||||
disabling the check.
|
||||
|
||||
Generated files are ignored by `.gitignore` in this directory.
|
||||
@@ -0,0 +1,19 @@
|
||||
# Toxiproxy, for the failures that matter most.
|
||||
#
|
||||
# The platform's hardest guarantee is that a lost confirmation is reported as AMBIGUOUS rather than
|
||||
# guessed. A healthy broker will not lose one on request, so the chaos suite puts a proxy in front of
|
||||
# it and severs the connection after the record was accepted but before the acknowledgement arrives.
|
||||
services:
|
||||
toxiproxy:
|
||||
image: ghcr.io/shopify/toxiproxy:2.12.0
|
||||
container_name: messaging-toxiproxy
|
||||
ports:
|
||||
- "8474:8474" # control API
|
||||
- "19092:19092" # proxied Kafka
|
||||
- "15673:15673" # proxied RabbitMQ
|
||||
- "15433:15433" # proxied PostgreSQL
|
||||
healthcheck:
|
||||
test: ["CMD", "/toxiproxy-cli", "list"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 20
|
||||
Reference in New Issue
Block a user