docs(acm2-75): 精简不变量与声明边界,去掉无依据条目
审改 specification 管道/航班域/投影与 CLM:作废与需求重复或依据不足的 INV/CLM,白话重写保留条款,并同步架构、实现与引用注释。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -12,7 +12,7 @@ import org.apache.kafka.clients.producer.ProducerRecord
|
||||
* Kafka 真实投递端口:通过 [ProducerRegistry] 拿 default 生产者,D2 参数
|
||||
* (acks=all / enable-idempotence=true / max-in-flight=1)由 `KafkaD3Check` 启动自检。
|
||||
*
|
||||
* 发送同步等 broker 确认(`Future.get()`),语义是至少一次——满足 INV-10 / C-29。
|
||||
* 发送同步等 broker 确认(`Future.get()`),语义是至少一次——满足 C-9 / US-08。
|
||||
* 仅在 `msgx.stubs != true` 时装配;stub 模式走 `StubDeliveryPort`。
|
||||
*/
|
||||
@Requires(property = "msgx.stubs", notEquals = "true")
|
||||
|
||||
@@ -31,7 +31,7 @@ class InboxService(
|
||||
data class Receipt(val msgId: Long, val receivedAt: Instant)
|
||||
|
||||
fun accept(rawXml: String): Receipt {
|
||||
// 接收结果边界 = insertRaw 是否成功返回 ID(C-28 只承诺到落信)。只有这一步失败才返回失败;
|
||||
// 接收结果边界 = insertRaw 是否成功返回 ID(C-8 只承诺到落信)。只有这一步失败才返回失败;
|
||||
// 之后的读取/入队失败若冒泡成 HTTP 失败,客户端重试会在信箱里多写一行。
|
||||
val id = inbox.insertRaw(rawXml)
|
||||
val now = clock.instant()
|
||||
@@ -49,7 +49,7 @@ class InboxService(
|
||||
// 入队时间单独传本地时钟:它是超期判据 R 的比较对象,不能借用库方时间(PRE-4)。
|
||||
procState.insertIfAbsent(id, receivedAt, enqueuedAt = now)
|
||||
} catch (e: Exception) {
|
||||
// PG 入队失败不回退接收结果:原文已在信箱,轮询会按 ID 补建,且只建一条(INV-1、US-01)。
|
||||
// PG 入队失败不回退接收结果:原文已在信箱,轮询会按 ID 补建,且只建一条(INV-2b、US-01)。
|
||||
pgEnqueueFailures.incrementAndGet()
|
||||
log.error("compat accepted but PG enqueue failed msgId={}; poller will re-create", id, e)
|
||||
}
|
||||
|
||||
@@ -36,22 +36,22 @@ CREATE TABLE PIPELINE_LOCK (
|
||||
INSERT INTO PIPELINE_LOCK (LOCK_ID, UPDATED_AT) VALUES (1, now());
|
||||
|
||||
-- ② 处理伴生状态:与共享信箱 CMINMSGS 一一对应(MSG_ID = CMINMSGS_ID)。
|
||||
-- 一信一行、一身份一记录(INV-9);MSG_ID 已有成功终态即判重放,直接记幂等成功。
|
||||
-- MSG_ID 主键(INV-2b);IDENTITY_KEY 唯一(implementation「消息、身份与决策」);MSG_ID 已有成功终态即判重放,直接记幂等成功。
|
||||
-- 处理终态与回填意图在本表同表同行:不存在第二处落账,也不存在独立的回填待办表(INV-8)。
|
||||
-- 状态不可逆:已提交的 SUCCEEDED 不因回填或投递失败回改(INV-6)。
|
||||
-- 已提交的 SUCCEEDED 不因写信箱处理时间或发 Kafka 失败回改(US-03 AC3)。
|
||||
CREATE TABLE PROC_STATE (
|
||||
MSG_ID BIGINT NOT NULL PRIMARY KEY,
|
||||
STATE VARCHAR(16) NOT NULL, -- PENDING/FAILED/SUCCEEDED/SKIPPED/DEAD
|
||||
IDENTITY_KEY VARCHAR(200), -- SNDR|TYPE|STYP|SEQN,解码后首次绑定
|
||||
ATTEMPTS INT NOT NULL DEFAULT 0,
|
||||
NEXT_ATTEMPT_AT TIMESTAMP(6) WITH TIME ZONE, -- 退避到期时刻;未到期不得被后续消息越过(INV-3)
|
||||
NEXT_ATTEMPT_AT TIMESTAMP(6) WITH TIME ZONE, -- 退避到期时刻;未到期不得被后续消息越过(US-03 AC1)
|
||||
ERROR_CLASS VARCHAR(20), -- MALFORMED/PROTOCOL/INFRA/UNSUPPORTED/EXHAUSTED
|
||||
LAST_ERROR VARCHAR(1000),
|
||||
UPDATED_AT TIMESTAMP(6) WITH TIME ZONE NOT NULL,
|
||||
-- ==== 时间基准 ====
|
||||
RECEIVED_AT TIMESTAMP(6) WITH TIME ZONE, -- 复制自信箱 DATE_RECEIVED:库方时钟、可为 NULL,仅用于对账与展示(PRE-4)
|
||||
ENQUEUED_AT TIMESTAMP(6) WITH TIME ZONE NOT NULL DEFAULT now(), -- 本地入队时间:非空、与判据 NOW 同源,是超期期限 R 的唯一比较对象(PRE-4、CLM-4)
|
||||
-- ==== 回填事实(INV-7/INV-8)====
|
||||
-- ==== 回填事实(INV-8;写规则见 US-10)====
|
||||
BACKFILL_AT TIMESTAMP(6) WITH TIME ZONE, -- 非空 = 处理标记已确认写入
|
||||
BACKFILL_NEXT_AT TIMESTAMP(6) WITH TIME ZONE, -- 非空 = 还欠一次回填;与终态同语句写下
|
||||
BACKFILL_ATTEMPTS INT NOT NULL DEFAULT 0,
|
||||
@@ -60,7 +60,7 @@ CREATE TABLE PROC_STATE (
|
||||
BACKFILL_ABANDONED_REASON VARCHAR(64), -- MISSING_ROW(确定性)/ TRANSIENT_DEADLINE(暂时性耗尽 R)
|
||||
CONSTRAINT uk_proc_identity UNIQUE (IDENTITY_KEY)
|
||||
);
|
||||
CREATE INDEX idx_proc_head ON PROC_STATE (STATE, MSG_ID); -- 主泵队头查询(严格 FIFO,INV-3)
|
||||
CREATE INDEX idx_proc_head ON PROC_STATE (STATE, MSG_ID); -- 主泵队头查询(US-03 AC1)
|
||||
-- 回填扫描:只覆盖仍需自动回填的行;按 (ATTEMPTS, MSG_ID) 轮转,避免最旧失败行长期占满批次。
|
||||
CREATE INDEX idx_proc_backfill_due ON PROC_STATE (BACKFILL_ATTEMPTS, MSG_ID)
|
||||
WHERE BACKFILL_AT IS NULL AND BACKFILL_ABANDONED_AT IS NULL;
|
||||
@@ -85,7 +85,7 @@ CREATE TABLE FLIGHT_SCHD (
|
||||
FLID VARCHAR(32) NOT NULL PRIMARY KEY, -- AODB 实例 ID(Number(1-12));不得由航班号或资源号推断
|
||||
OPERATION_DAY DATE NULL, -- 运营保障日:未由日计划收录时为 NULL;非空后不可改变(INV-12)
|
||||
STATE VARCHAR(8) NOT NULL, -- ACTIVE/DELETED,无第三态;删除只由 FDEL 或受控历史清理触发(INV-15)
|
||||
STATE_VERSION BIGINT NOT NULL DEFAULT 0, -- 每次成功写入 +1,重复消息不重复推进(INV-13)
|
||||
STATE_VERSION BIGINT NOT NULL DEFAULT 0, -- 航班写代次;成功写入时推进
|
||||
LAST_MSG_ID BIGINT NULL, -- 最近一次成功写入的消息 ID
|
||||
-- ==== SCHD.FLTR 标量字段(保持 AODB 字符串原样;未携带不隐式清空,INV-14)====
|
||||
ALCD VARCHAR(64) NULL, -- 航空公司代码
|
||||
@@ -278,7 +278,7 @@ CREATE INDEX idx_flight_delay_flid ON FLIGHT_DELAY (FLID);
|
||||
CREATE INDEX idx_flight_bridge_flid ON FLIGHT_BRIDGE_OP (FLID);
|
||||
CREATE INDEX idx_flight_chock_flid ON FLIGHT_CHOCK_OP (FLID);
|
||||
|
||||
-- ⑥ 统一投递事件 outbox(INV-10、INV-17):KAFKA:schd 发整态、KAFKA:msg 只通知变化;
|
||||
-- ⑥ 统一投递事件 outbox(C-9、INV-17b):KAFKA:schd 发整态、KAFKA:msg 只通知变化;
|
||||
-- tombstone 仅在 ACTIVE→DELETED 时与删除同事务登记,投递失败按退避持续重试。
|
||||
-- 目标级全序投递是当前实现,按 FLID 保序见 CLM-7。
|
||||
CREATE TABLE MSG_EVENT (
|
||||
|
||||
@@ -13,7 +13,7 @@ import java.time.Instant
|
||||
|
||||
/**
|
||||
* 处理失败的落账规矩:没到尝试上限才写 FAILED 退避重试;一到上限立刻写 DEAD(EXHAUSTED) 终态,
|
||||
* 不允许同一次失败调用再把它覆写回 FAILED(INV-6 终态不可逆、INV-8 终态与回填意图同一次写入)。
|
||||
* 不允许同一次失败调用再把它覆写回 FAILED(US-03 AC3、INV-8 本地结束与「还要更新信箱」同一次写入)。
|
||||
*/
|
||||
class ProcFailureTest {
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import org.junit.jupiter.api.Test
|
||||
import java.time.Instant
|
||||
|
||||
/**
|
||||
* 兼容入口的接收边界(`C-28`、`INV-1`):`insertRaw` 返回 ID 即"已落信"。
|
||||
* 兼容入口的接收边界(`C-8`):`insertRaw` 返回 ID 即"已落信"。
|
||||
* 此后的接收时间读取失败或 PG 入队失败都只留痕/计数,不改接收结果——否则客户端重试会在
|
||||
* 信箱里多写一行;缺的 `PROC_STATE` 由收报轮询按 `US-01` 补建,且只建一条。
|
||||
*/
|
||||
|
||||
@@ -364,7 +364,7 @@ class BackfillServiceTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* `INV-7`:超期判据是**本地入队时间** `ENQUEUED_AT`,与 `RECEIVED_AT` 无关。
|
||||
* 超期判据是**本地入队时间** `ENQUEUED_AT`,与 `RECEIVED_AT` 无关。
|
||||
* 上游没给接收时间(NULL)不再让 `R` 兜底失效——这正是引入 `ENQUEUED_AT` 要消除的窗口。
|
||||
*/
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user