feat: jpa, messaging, notification, mongo, graphql 어댑터터 구현체 추가
This commit is contained in:
@@ -1,9 +1,18 @@
|
||||
# 설정 레퍼런스
|
||||
|
||||
> **Prefix.** Every property below binds under `app.messaging`, which is the prefix the deployed
|
||||
> runtime and the `APP_MESSAGING_*` environment variables already use. Earlier revisions of this
|
||||
> page documented a bare `messaging` prefix and the starter bound `backend.messaging`; neither
|
||||
> bound what this page describes, so a deployment configured from it changed nothing. A key under
|
||||
> either of the old prefixes now fails startup with a message naming the key — see
|
||||
> `MessagingPrefixMigrationValidator`.
|
||||
|
||||
|
||||
## Destination profile
|
||||
|
||||
```yaml
|
||||
messaging:
|
||||
app:
|
||||
messaging:
|
||||
destinations:
|
||||
order-events:
|
||||
broker: kafka-primary
|
||||
@@ -77,7 +86,8 @@ messaging:
|
||||
### Kafka
|
||||
|
||||
```yaml
|
||||
messaging:
|
||||
app:
|
||||
messaging:
|
||||
brokers:
|
||||
kafka-primary:
|
||||
type: kafka
|
||||
@@ -96,7 +106,8 @@ messaging:
|
||||
### RabbitMQ
|
||||
|
||||
```yaml
|
||||
messaging:
|
||||
app:
|
||||
messaging:
|
||||
brokers:
|
||||
rabbit-primary:
|
||||
type: rabbitmq
|
||||
@@ -117,7 +128,8 @@ messaging:
|
||||
## 보안
|
||||
|
||||
```yaml
|
||||
messaging:
|
||||
app:
|
||||
messaging:
|
||||
security:
|
||||
kafka-primary:
|
||||
producer: { type: SASL_SCRAM, credential-id: kafka-producer }
|
||||
@@ -135,7 +147,8 @@ messaging:
|
||||
기본값은 전부 `false`다.
|
||||
|
||||
```yaml
|
||||
messaging:
|
||||
app:
|
||||
messaging:
|
||||
experimental:
|
||||
kafka-share: false
|
||||
pulsar: false
|
||||
@@ -147,7 +160,8 @@ messaging:
|
||||
## Backpressure
|
||||
|
||||
```yaml
|
||||
messaging:
|
||||
app:
|
||||
messaging:
|
||||
backpressure:
|
||||
global-limit: 512
|
||||
per-destination-limit: 64 # global-limit 이하여야 한다
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
# 기존 runtime → 신규 messaging platform cutover (MSG-015)
|
||||
|
||||
## 왜 기계적 매핑이 안 되는가
|
||||
|
||||
두 outbox 모델의 enum 이름이 겹치는데 의미가 반대다.
|
||||
|
||||
| 모델 | retryable | terminal |
|
||||
|---|---|---|
|
||||
| 기존 `OutboxEventStatus` | `FAILED` (`next_attempt_at` 보유) | `DEAD` |
|
||||
| 신규 `OutboxStatus` | `AMBIGUOUS` | `FAILED`, `EXHAUSTED` |
|
||||
|
||||
이름으로 매핑하면 **확정 거절이 무한 재시도**가 되고 **불확정이 park**된다. 그래서 application은
|
||||
자기 어휘(`OutboxPublishOutcome`)만 쓰고, 변환은 bridge adapter가 한다.
|
||||
|
||||
## 지금 반영된 것
|
||||
|
||||
- `OutboxPublishOutcome` — `CONFIRMED` / `AMBIGUOUS` / `REJECTED_BEFORE_SEND` /
|
||||
`REJECTED_AFTER_BROKER`. application이 소유하는 canonical 결과 타입이며, "리턴 or throw"만 가능한
|
||||
기존 어댑터를 위해 `OutboxMessagePublishPort.publishForOutcome`의 default가 `CONFIRMED`를 돌려준다.
|
||||
- `APPLICATION_DOES_NOT_DEPEND_ON_THE_MESSAGING_PLATFORM` ArchUnit 규칙 — application-core가
|
||||
`dev.caskeleton.messaging..`를 import하면 빌드가 깨진다.
|
||||
- 반대 방향(신규 `PublishResult` → application outcome) 매핑 규칙을 테스트로 고정.
|
||||
|
||||
## 남은 것
|
||||
|
||||
- `messaging-platform-bridge` outbound leaf: validated application event → platform envelope,
|
||||
`PublishResult` → `OutboxPublishOutcome`. registry에 leaf를 추가하는 변경이라 별도 커밋.
|
||||
- golden contract 테스트: event/message ID, type, schema revision, partition/order/correlation/
|
||||
causation/tenant/trace, payload digest, wire version이 bytes 단위로 보존되는지.
|
||||
- 단일 publication authority: 기존 `OutboxPublicationAuthority` fence를 재사용해 writer/relay가
|
||||
동시에 ACTIVE가 되지 않도록. **dual write/publish는 금지** — 한 business fact가 두 durable store와
|
||||
두 relay로 나가는 상태가 cutover에서 가장 위험하다.
|
||||
- 첫 cutover 범위는 **transport만** 교체(저장소는 기존 유지). storage migration은 shadow read →
|
||||
authority switch → old backlog drain 순서로 별도 release.
|
||||
@@ -41,9 +41,26 @@ failed로 표시하면 broker가 이미 가지고 있을 수 있는 메시지를
|
||||
### lease
|
||||
|
||||
```text
|
||||
status IN ('PENDING','AMBIGUOUS') AND (lease_expires_at IS NULL OR lease_expires_at <= now)
|
||||
status IN ('PENDING','AMBIGUOUS','IN_FLIGHT')
|
||||
AND (lease_expires_at IS NULL OR lease_expires_at <= now)
|
||||
AND next_attempt_at <= now
|
||||
AND attempts < maxAttempts
|
||||
```
|
||||
|
||||
`IN_FLIGHT`가 목록에 있는 것이 핵심이다. relay가 publish 도중 죽으면 row는 `IN_FLIGHT`로 남는데,
|
||||
이를 제외하면 그 메시지는 **영원히** 발행되지 않는다 — outbox가 막으려던 바로 그 실패다. 대신
|
||||
lease가 만료됐을 때만 회수하므로, 살아 있는 relay가 들고 있는 row는 회수되지 않는다.
|
||||
|
||||
회수는 **같은 `message_id`로** 이루어지고 `lease_token`이 1 증가한다. 새 id를 발급하면 "전달됐을
|
||||
수도 있는 메시지"가 "확실히 두 번째"가 되기 때문이다 (위의 AMBIGUOUS 논의와 같은 이유).
|
||||
|
||||
이 문단의 근거는 실제 PostgreSQL 컨테이너 레인이다:
|
||||
|
||||
- `OutboxPostgresIT#anExpiredLeaseBecomesClaimableAgain` — 만료된 lease의 재회수
|
||||
- `OutboxPostgresIT#anExpiryReclaimKeepsTheMessageIdAndAdvancesTheToken` — 같은 id, 증가한 token
|
||||
- `OutboxPostgresIT#aLeasedRowIsInvisibleToASecondRelayInstance` — 살아 있는 lease는 회수 불가
|
||||
- `OutboxPostgresIT#aSupersededRelayCannotOverwriteTheOutcomeOfTheOneThatReplacedIt` — fencing
|
||||
|
||||
partial index `ix_messaging_outbox_claimable`이 이 쿼리를 backlog 크기에 비례하게 유지한다.
|
||||
PUBLISHED row는 retention job이 지울 때까지 쌓이기 때문이다.
|
||||
|
||||
|
||||
@@ -3,11 +3,28 @@
|
||||
플랫폼이 **무엇을 보장하는지**와 **무엇을 보장하지 않는지**를 브로커별로 고정한다.
|
||||
여기 없는 조합은 지원되지 않는다.
|
||||
|
||||
> **인증 근거.** 이 표의 버전은 이 저장소의 컨테이너 레인이 실제로 실행한 이미지다. 이전 판은
|
||||
> Kafka 4.2/4.3을 선언했지만 fixture는 `apache/kafka:4.1.0`, lockfile client는 4.1.1이었다 — 표와
|
||||
> 코드 상수가 서로 일치했을 뿐 어느 쪽도 실행된 적이 없었다. 장애 시나리오 커버리지도 마찬가지로
|
||||
> `BrokerFailureMatrix.shipped()` 하드코딩이 아니라 레인이 낸 증거(`BrokerCertificationEvidence`)에서
|
||||
> 나온다. 증거가 없는 조합은 `NOT_COVERED`다 (MSG-014).
|
||||
|
||||
> **모듈 이름과 런타임 편입.** `messaging-outbox-jdbc-postgresql` / `messaging-inbox-jdbc-postgresql`은
|
||||
> 이전에 `-jpa`로 불렸다. 구현은 Spring JDBC이고 SQL은 PostgreSQL 전용(`?::jsonb`,
|
||||
> `FOR UPDATE SKIP LOCKED`, `ON CONFLICT`, `TIMESTAMPTZ`)이므로, 그 이름은 쓰지 않는 기술을
|
||||
> 광고하고 vendor 중립 port(`messaging-reliability-api`)의 위치를 가렸다 (MSG-023).
|
||||
>
|
||||
> 또한 registry의 messaging leaf는 모두 `runtime_memberships`가 비어 있다. 이는 **build-only /
|
||||
> incubating** — 어느 composition root에도 편입되지 않았다는 뜻이며, 아래의 등급과는 다른 축이다.
|
||||
> 등급은 "무엇이 증명되었는가", membership은 "무엇이 실행되는가"를 말한다. 애플리케이션에 배선하려면
|
||||
> registry를 먼저 바꾸고 `verifyRuntimeModuleMembership`을 통과시켜야 한다. 자세한 규칙은
|
||||
> `src/messaging/CLAUDE.md`가 소유한다.
|
||||
|
||||
## 브로커 등급
|
||||
|
||||
| 브로커 | 등급 | 인증 기준 | Stable 기능 | 제한 |
|
||||
|---|---|---|---|---|
|
||||
| Kafka | Stable | 4.2+ / 4.3.x | producer idempotence, consumer group, batch, pause/resume, replay, transaction capability | Share Group은 Experimental |
|
||||
| Kafka | Stable | 4.1.x | producer idempotence, consumer group, batch, pause/resume, replay, transaction capability | Share Group은 Experimental |
|
||||
| RabbitMQ | Stable | 4.3.x | exchange/routing, publisher confirm, mandatory return, manual ACK, quorum queue, retry queue, DLQ | stream 및 특수 plugin 미지원 |
|
||||
| Pulsar | Experimental | 4.0 LTS + 4.2 | typed publish/consume, Shared, Key_Shared, schema | transaction 미승격, 기본 비활성 |
|
||||
| NATS JetStream | Experimental | 2.14.x | stream, durable consumer, explicit ACK, dedupe, replay | native DLQ 없음(플랫폼이 대행), 기본 비활성 |
|
||||
|
||||
Reference in New Issue
Block a user