feat: add messaging R2 polling producer
This commit is contained in:
@@ -45,6 +45,22 @@ application-core 포트(`MessagePublisher` / `OutboxMessagePublishPort`) 뒤에
|
||||
그대로 전파하고 checked 예외는 cause를 보존해 감싼다. 성공 DEBUG나 실패 WARN을 남기지 않는다.
|
||||
반환 타입을 void 로 둬 broker SDK 타입이 어댑터 밖으로 새지 않는다(B7).
|
||||
|
||||
## Legacy R0 characterization
|
||||
|
||||
현재 `KafkaSender.send(...)`와 `MessageBroker.send(...)`의 정상 void 반환은 호출이 예외 없이
|
||||
끝났다는 뜻일 뿐 broker acknowledgement 증거가 아니다. relay는 이 정상 반환 뒤 legacy
|
||||
`PUBLISHED`를 기록한다. sender 예외는 fail-closed outbox 경로에서 전파되어 relay의
|
||||
`FAILED/DEAD` 전이를 유발한다.
|
||||
|
||||
broker 설정이 blank면 두 포트는 각각 `DisabledMessagePublisher`와
|
||||
`DisabledOutboxMessagePublisher`에 바인딩된다. broker를 선택했지만 project-supplied sender가
|
||||
없거나 선택 ID와 활성 `MessageBroker.brokerId()`가 다르면 startup이 실패한다.
|
||||
|
||||
publisher 정상 반환 뒤 DB mark가 실패하면 row는 `IN_FLIGHT`에 남아 timeout reclaim 후 같은
|
||||
event가 다시 publish될 수 있다. 현재 consumer/inbox가 없으므로 이 duplicate 가능 구간을
|
||||
중복 안전 전달로 표현하지 않는다. 또한 legacy FIFO gate는 `occurred_at`의 strict less-than 비교만
|
||||
사용하므로 동일 aggregate의 timestamp 동률 row는 서로를 gate하지 않는다.
|
||||
|
||||
## OutboxRelayFailureReport 구조화 ERROR
|
||||
|
||||
`MessagingConfig`는 broker 활성 여부와 무관하게 정확히 하나의
|
||||
@@ -58,3 +74,86 @@ ERROR 하나로 렌더링한다.
|
||||
payload/idempotency key/envelope/exception-derived field는 받거나 렌더링하지 않고 cause만 throwable로
|
||||
붙인다. logging 내부 `RuntimeException`은 adapter와 use case 양쪽에서 방어하므로 persisted
|
||||
FAILED/DEAD outcome을 바꾸지 않는다.
|
||||
|
||||
## Closed local binding compiler R1
|
||||
|
||||
`contract`, `destination`, `config` package는 명시적으로 주입된
|
||||
`IntegrationEventContractContribution` 목록만 사용해 contract catalog와 deployment binding을
|
||||
로컬에서 컴파일한다. payload record의 exact `Class` token, 선언 순서, schema resource/hash를
|
||||
검증하며 scan, `Class.forName`, assignable discovery, raw JSON/tree discovery를 하지 않는다.
|
||||
catalog/settings/schema digest는 정렬된 semantic identity와 length-prefixed UTF-8/raw SHA-256
|
||||
bytes로 계산한다.
|
||||
|
||||
physical topic과 bootstrap server는 `DestinationBindingSettings`와
|
||||
`CompiledPublicationBinding`에만 존재한다. application contract에는 broker, topic, credential
|
||||
설정이 유출되지 않는다. logical destination과 physical topic은 각각 deployment 안에서 unique다.
|
||||
compiled publication binding은 package-local compiler만 만들 수 있고 active descriptor는 public
|
||||
static compile 경로로만 생성한다. compiled integration-event contract도 catalog compiler만
|
||||
package-local constructor로 만들 수 있다. partition key v1은 canonical non-null tenant scope,
|
||||
logical destination, aggregate type/id를 strict UTF-8 REPORT + domain-separated
|
||||
length-prefixed SHA-256으로 계산하고 lowercase hex US-ASCII bytes를 반환한다. malformed
|
||||
surrogate는 replacement 문자로 바꾸지 않고 거부한다.
|
||||
|
||||
adapter 내부 compiler/card/binding 타입은 direct 또는 generic argument 형태의 public instance
|
||||
response surface로 노출하지 않는다.
|
||||
cross-package adapter composition이 정확한 compiled identity를 확인해야 할 때만 public static
|
||||
bridge를 사용하며, instance accessor는 package-private이고 외부에 필요한 상태는 boolean/String/
|
||||
core value로만 제공한다. 이는 broker SDK뿐 아니라 adapter 자체의 조립 타입도 B7 반환 경계를
|
||||
통해 application consumer로 새지 않게 한다.
|
||||
|
||||
`MessagingCapabilityCardRegistry`는 first R2 tuple의 11개 ID와 각 role을 닫힌 Java vocabulary로
|
||||
제공할 뿐이다. readiness registry의 `not-implemented` 상태를 승격하거나 R2/`ACTIVE_READY`를
|
||||
주장하지 않는다. `DISABLED + empty catalog/bindings`는 client, thread, scheduler, network,
|
||||
filesystem resource가 정확히 0인 descriptor다. `ACTIVE`는 empty catalog, missing/duplicate/extra
|
||||
binding, unknown/mismatched card와 ordering/schema/security 완화를 resource 생성 전에 거부한다.
|
||||
|
||||
## Deterministic local JSON Schema encoder candidate
|
||||
|
||||
`envelope` package는 exact final `IntegrationPayload` record만 받아 frozen v1 field order로 UTF-8
|
||||
JSON을 쓴다. `Map`, raw JSON, `JsonNode`, polymorphic typing, custom serializer, assignable search는
|
||||
입력 경로에 없다. contract compiler가 선언된 generic type graph를 scalar/enum,
|
||||
`Optional<T>`/`List<T>`/exact final nested record로만 재귀 고정하며 `Object`, interface, raw/wildcard,
|
||||
generic record와 runtime record discovery를 거부한다. writer는 이 compiled graph를 따라서 각
|
||||
record accessor를 정확히 한 번 읽은 immutable snapshot을 만들고 payload를 정확히 한 번 encode한다.
|
||||
schema validation에 건넨 바로 그 payload bytes를 raw JSON API 없이 envelope suffix에 삽입한다.
|
||||
depth, string character/UTF-8 byte, array, object, number, payload/envelope byte limit는 writer와
|
||||
parser 양쪽에서 적용한다. output byte limit는 generator가 쓰는 bounded stream에서 allocation 전에
|
||||
강제하고, list는 size를 먼저 검사한 뒤 bounded iterator로 한 번만 snapshot한다. mutation/concurrent
|
||||
access와 극단적 `BigDecimal` scale도 큰 배열/String 생성 전에 fail-closed 한다. duplicate key,
|
||||
malformed UTF-8, trailing data, unpaired surrogate와 non-finite number는 replacement나 coercion
|
||||
없이 거부한다.
|
||||
|
||||
`LocalJsonSchemaRegistry`는 호출자가 명시적으로 제공한 exact bytes와 SHA-256만 startup에서
|
||||
Draft 2020-12 meta-schema로 검사하고 precompile한다. root `$id`와 absolute `$ref`는 lowercase
|
||||
exact `urn` scheme만 허용하고 `$ref`는 동일 문서 fragment 또는 제공된 exact URN map으로 닫힌다.
|
||||
HTTP/HTTPS/file/classpath/resource/jar 및 unknown absolute scheme fetch와 YAML loader는 제공하지
|
||||
않는다. public validation boundary도 UTF-8 bytes만 받는다. format assertion은 활성이다. 이 Task 6
|
||||
closed subset은 scope 추적의 모호성을 제거하기 위해 root 외 nested `$id`를 value type과 무관하게
|
||||
거부하고, `$dynamicRef`, `$dynamicAnchor`, deprecated `$recursiveRef`/`$recursiveAnchor`, `$anchor`
|
||||
키워드를 어느 위치에서도 지원하지 않는다.
|
||||
|
||||
Draft 2020-12 authority는 `draft/2020-12/schema` 1개와 `meta/*` 8개의 exact checked-in bytes 및
|
||||
digest로 pin한다. registry startup은 9개 digest, 각 `$id`, NetworkNT runtime schema tree를
|
||||
대조하고 하나라도 다르면 fail-closed 한다. Spring Boot executable/fat/nested JAR 배치를 깨뜨리는
|
||||
`CodeSource` regular-file/JAR 가정은 하지 않는다. NetworkNT 3.0.2 artifact provenance는 strict
|
||||
Gradle dependency lock과 `verifyJsonSchemaRuntimeGraph`가 담당한다. 이 검증은 business schema
|
||||
registry나 runtime remote resolution 경로를 넓히지 않는다.
|
||||
|
||||
공통 build evidence manifest는 수동 구조 검사만으로 PASS하지 않는다. test/build 전용
|
||||
`MessagingEvidenceManifestSchemaValidator`가 exact common schema와 생성된 manifest bytes를 같은
|
||||
pinned NetworkNT/`LocalJsonSchemaRegistry`로 Draft 2020-12 검증하며, common schema 자체도
|
||||
meta-schema 검증을 통과해야 한다. `verifyMessagingContracts`는 JSON-only qualification과 그
|
||||
manifest schema validation을 명시적으로 선행해 CLI task 나열 순서와 무관하게 combined manifest가
|
||||
`build/messaging-evidence/contracts-schema/manifest.json`의 최종 소유자가 된다.
|
||||
|
||||
NetworkNT 3.0.2의 adopted regex 구현은 JDK regex 실행시간을 강제 중단시키는 별도 engine/timeout을
|
||||
제공하지 않는다. 따라서 현재 후보는 regex 입력 길이와 전체 document 구조를 먼저 제한하고,
|
||||
pathological pattern의 작은 repository corpus를 회귀 테스트한다. 이는 JSON Schema Test
|
||||
Suite/Bowtie 전체 호환이나 hostile regex 시간 상한 증명이 아니며 evidence의
|
||||
`regex-engine-timeout`, `consumer-compatibility-full-suite` unsupported claim으로 남는다. 테스트
|
||||
runtime은 remote corpus를 내려받지 않는다.
|
||||
|
||||
이 Task의 acceptance는 **deterministic local wire contract candidate**다. encoder/catalog는 기존
|
||||
`MessagingConfig`, runtime append, `KafkaSender` 또는 legacy broker selection에 연결하지 않았다.
|
||||
Kafka ACK, durable outbox R2, external topic attestation은 아직 구현되지 않았고 R0 runtime authority와
|
||||
동작은 그대로다.
|
||||
|
||||
Reference in New Issue
Block a user