mirror of
https://gitee.com/pnoker/iot-dc3.git
synced 2026-10-31 02:01:51 +08:00
docs: retire three landed design proposals
mq-abstraction (four broker adapters now tck-gated), storage-abstraction (flag day, postgres-only runtime), and tsdb-abstraction (single implementation, no historical adapters) all describe designs that live in the code now; their semantics are enforced by tests, not prose. relational-r2dbc stays: it is the design baseline the r2dbc migration test handoff still validates against. cross-references in the comparison docs and mcp-runtime-overhaul updated to plain wording. Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
+11
-39
@@ -13,16 +13,11 @@
|
||||
| 开发工具 | [development.md](./development.md) Make 开发、启动与质量命令 | 有效 |
|
||||
| 设计/架构 | [design/relational-r2dbc.md](./design/relational-r2dbc.md) R2DBC 关系访问层 | 已落地,待全量实库/拓扑验收 |
|
||||
| 质量/测试 | [r2dbc-migration-test-handoff.md](./r2dbc-migration-test-handoff.md) R2DBC 全平台测试交接 | 待执行,发布硬门禁 |
|
||||
| 质量/测试 | [dc3-web-low-defects-handoff.md](./dc3-web-low-defects-handoff.md) dc3-web 遗留缺陷修复交接 | LOW 项已修复;「需人工确认」5 项待裁决 |
|
||||
| 设计/架构 | [design/tsdb-abstraction.md](./design/tsdb-abstraction.md) 时序存储 Port | 已落地 |
|
||||
| 设计/架构 | [design/mq-abstraction.md](./design/mq-abstraction.md) 消息中间件 Port | 已落地 |
|
||||
| 设计/架构 | [design/storage-abstraction.md](./design/storage-abstraction.md) 存储抽象总纲 | 部分被取代(§3/§4) |
|
||||
| 设计/架构 | [design/mcp-runtime-overhaul.md](./design/mcp-runtime-overhaul.md) MCP 运行时重构 | 提案,未实施 |
|
||||
| 设计/架构 | [design/dc3-client-sdk.md](./design/dc3-client-sdk.md) 客户端 SDK | 提案(phase 0 已落地) |
|
||||
| 设计/架构 | [design/frontend-three-terminal-ux.md](./design/frontend-three-terminal-ux.md) 前端三终端 UX ADR | ADR |
|
||||
| 分析对比 | [comparison-dgiot.md](./comparison-dgiot.md) DG-IoT 对比借鉴 | 开放评审,未排期 |
|
||||
| 分析对比 | [comparison-supos-free.md](./comparison-supos-free.md) supOS-Free 对比借鉴 | 开放评审,未排期 |
|
||||
| 分析对比 | [dc3-thing-model-review.md](./dc3-thing-model-review.md) 物模型代码级核实 | 事实判定(非路线图) |
|
||||
|
||||
---
|
||||
|
||||
@@ -54,14 +49,11 @@
|
||||
|
||||
## 二、设计与架构文档(design/)
|
||||
|
||||
### 存储演进主线(取代链,先看这张图再读文档)
|
||||
### 存储演进主线
|
||||
|
||||
```text
|
||||
storage-abstraction.md(2026-08-17 总纲)
|
||||
├─ §1-2 三层存储模型、§6 统一配置面 .......... 仍有效(总纲骨架)
|
||||
├─ §3 关系方言机制(MyBatis databaseId 双方言)──已落地 R1/R2,后继──→ relational-r2dbc.md(R2DBC 重写,已批准)
|
||||
└─ §4 时序存储 Port .......................... 已被取代──→ tsdb-abstraction.md(已落地)
|
||||
```
|
||||
存储/时序/MQ 三个抽象设计(storage-abstraction、tsdb-abstraction、mq-abstraction)已全部落地并退役:
|
||||
语义现由源码与 TCK 门禁守护(四 MQ 适配器、PostgreSQL TSDB 实现、单关系方言)。演进链的终点是
|
||||
[relational-r2dbc.md](./design/relational-r2dbc.md) —— 现行关系访问层的唯一设计基线。
|
||||
|
||||
### 4. [基于 Spring Data R2DBC 的关系访问层设计](./design/relational-r2dbc.md)
|
||||
|
||||
@@ -75,34 +67,19 @@ storage-abstraction.md(2026-08-17 总纲)
|
||||
- **读什么**:当前已证实与未证实边界、已知阻断问题、全 Store 实库矩阵、数据库组合、分页/流式协议和发布硬门禁。
|
||||
- **注意**:当前单元/TCK 基线不能替代本文要求的全链路证据;任一必测项失败、跳过或无证据都阻断发布。
|
||||
|
||||
### 5. [时序存储抽象(TSDB Port)](./design/tsdb-abstraction.md)
|
||||
|
||||
- **一句话**:关系与时序统一为 PostgreSQL/TimescaleDB + R2DBC,`dc3-tsdb-core` 定义历史 Port,单一实现由 Data 域承载并接受 PostgreSQL TCK。
|
||||
- **读什么**:port 必须承载的语义(§4)、核心 API(§6);能力差异的落地细节对应 [tsdb-stores.md](./tsdb-stores.md)。
|
||||
|
||||
### 6. [消息中间件抽象(MQ Port)](./design/mq-abstraction.md)
|
||||
|
||||
- **一句话**: center ↔ driver 异步平面的 broker 抽象,`dc3-mq-core` + 四适配器(RabbitMQ/Kafka/Pulsar/MQTT 5)+ TCK,已交付。
|
||||
- **读什么**:核心 API(§6)、订阅模式与目的地映射(§7)、逐 broker 的硬语义(§8:延迟消息、死信、MQTT 两平面分离)。
|
||||
|
||||
### 7. [统一 PostgreSQL R2DBC 存储设计](./design/storage-abstraction.md)
|
||||
|
||||
- **一句话**:统一 PostgreSQL、TimescaleDB、R2DBC、JSONB、UTC 时间、租户隔离和事务边界的现行总纲。
|
||||
- **读什么**:运行时拓扑、数据格式、分页、并发、取消和发布门禁。
|
||||
|
||||
### 8. [MCP 运行时全面重构:内聚的授权契约](./design/mcp-runtime-overhaul.md) *提案*
|
||||
### 5. [MCP 运行时全面重构:内聚的授权契约](./design/mcp-runtime-overhaul.md) *提案*
|
||||
|
||||
- **一句话**:现状一次 `tools/call` 要三次网关→auth 冗余往返且有正确性缺口;目标一次内聚往返、响应式非阻塞、真实 input schema、异步审计。
|
||||
- **读什么**:§2.3 的三冗余一缺口(问题陈述最精彩)、§5 目标契约、§9 备选方案。
|
||||
- **关联**:实施依赖 relational-r2dbc 的 auth 响应式改造节奏。
|
||||
|
||||
### 9. [dc3-sdk:框架无关客户端 SDK](./design/dc3-client-sdk.md) *提案(phase 0 已落地)*
|
||||
### 6. [dc3-sdk:框架无关客户端 SDK](./design/dc3-client-sdk.md) *提案(phase 0 已落地)*
|
||||
|
||||
- **一句话**:网关 HTTP 契约现在被 dc3-web(30 个 api 模块)和 dc3-cli(手写 fetch)重复实现,未来 native app 会是第三份;
|
||||
目标一个 TypeScript SDK 实现一次,三端消费。
|
||||
- **读什么**:边界规则(可 lint 强制)、迁移计划;依赖前端 ADR 的边界纪律。
|
||||
|
||||
### 10. [前端三终端 UX 架构(ADR)](./design/frontend-three-terminal-ux.md)
|
||||
### 7. [前端三终端 UX 架构(ADR)](./design/frontend-three-terminal-ux.md)
|
||||
|
||||
- **一句话**:dc3-web 从桌面优先(1280px 硬底线)转向桌面/平板/手机三终端架构,用公理化推导替代零散 `@media` 补丁。
|
||||
- **读什么**:公理、分层模型、断点契约、边界复用纪律——前端任何新页面开工前都该过一遍。
|
||||
@@ -111,19 +88,14 @@ storage-abstraction.md(2026-08-17 总纲)
|
||||
|
||||
## 三、分析与对比(参考性,未进实施排期)
|
||||
|
||||
### 11. [DG-IoT 与 IoT DC3 对比借鉴](./comparison-dgiot.md)
|
||||
### 8. [DG-IoT 与 IoT DC3 对比借鉴](./comparison-dgiot.md)
|
||||
|
||||
- **一句话**:跨项目对比提炼可借鉴点,并澄清双方物模型差异;开放评审中,**不是承诺路线图**。
|
||||
|
||||
### 12. [supOS-Free 与 IoT DC3 对比借鉴](./comparison-supos-free.md)
|
||||
### 9. [supOS-Free 与 IoT DC3 对比借鉴](./comparison-supos-free.md)
|
||||
|
||||
- **一句话**:同上,对标 supOS-Free;结论与 storage/mq 抽象设计互相印证。
|
||||
|
||||
### 13. [DC3 物模型(Profile)代码级核实与最终判定](./dc3-thing-model-review.md)
|
||||
|
||||
- **一句话**:用 grep 全仓 + 逐文件读源码的方式回答"DC3 的 Profile 是否等价于物模型",证据落到类/方法/表名,给出最终判定。
|
||||
- **价值**:回答"DC3 有没有物模型"这一常见问题的权威出处。
|
||||
|
||||
---
|
||||
|
||||
## 按任务找文档(推荐阅读路径)
|
||||
@@ -131,8 +103,8 @@ storage-abstraction.md(2026-08-17 总纲)
|
||||
| 你想做什么 | 按顺序读 |
|
||||
|------------|----------|
|
||||
| 部署 / 换库 / 换 broker | [db-dialects](./db-dialects.md) → [tsdb-stores](./tsdb-stores.md) → [mq-brokers](./mq-brokers.md) |
|
||||
| 理解存储层现状与演进 | [storage-abstraction §1–2](./design/storage-abstraction.md) → [tsdb-abstraction](./design/tsdb-abstraction.md) → [db-dialects](./db-dialects.md) → [relational-r2dbc](./design/relational-r2dbc.md) |
|
||||
| 理解存储层现状与演进 | [db-dialects](./db-dialects.md) → [relational-r2dbc](./design/relational-r2dbc.md) → [tsdb-stores](./tsdb-stores.md) |
|
||||
| 验收全平台 R2DBC 迁移 | [relational-r2dbc 全文](./design/relational-r2dbc.md) → [r2dbc-migration-test-handoff](./r2dbc-migration-test-handoff.md) → 根目录 AGENTS.md |
|
||||
| 前端 / 客户端开发 | [frontend-three-terminal-ux](./design/frontend-three-terminal-ux.md) → [dc3-client-sdk](./design/dc3-client-sdk.md) |
|
||||
| MCP / AI 集成 | [mcp-runtime-overhaul](./design/mcp-runtime-overhaul.md) → [relational-r2dbc §5 D13](./design/relational-r2dbc.md)(OAuth/MCP 聚合拆分) |
|
||||
| 了解项目定位 / 竞品差异 | [comparison-dgiot](./comparison-dgiot.md) → [comparison-supos-free](./comparison-supos-free.md) → [dc3-thing-model-review](./dc3-thing-model-review.md) |
|
||||
| 了解项目定位 / 竞品差异 | [comparison-dgiot](./comparison-dgiot.md) → [comparison-supos-free](./comparison-supos-free.md)(物模型能力缺口见 issue #258) |
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
| **Status** | Analysis / proposal — for reference, not a committed roadmap |
|
||||
| **Date** | 2026-08-18 |
|
||||
| **Scope** | 跨项目对比(DG-IoT ↔ IoT DC3),提炼可借鉴点,并澄清双方物模型差异 |
|
||||
| **Related** | [comparison-supos-free.md](./comparison-supos-free.md)、[storage-abstraction.md](./design/storage-abstraction.md)、[mq-abstraction.md](./design/mq-abstraction.md) |
|
||||
| **Related** | [comparison-supos-free.md](./comparison-supos-free.md) |
|
||||
| **Discussion** | 开放评审,未进入实施排期 |
|
||||
|
||||
> 说明:本文中 **DG-IoT**(`dgiot/dgiot`,gitee: `dgiiot/dgiot`)为国内首款轻量级开源工业物联网平台。
|
||||
@@ -71,7 +71,7 @@
|
||||
### 2.7 数据通道 / 转发
|
||||
|
||||
- DG-IoT 的“数据通道”抽象很实用:MQTT/Kafka/HTTP/WebSocket 多通道转发 + MES/WMS/ERP/ESB 多源接入。
|
||||
- DC3 有数据桥接驱动 + `mq-abstraction.md`(Proposed),方向一致但 **缺“通道”这一层编排抽象**。
|
||||
- DC3 有数据桥接驱动 + 已落地的 MQ 端口抽象(dc3-common-mq,四适配器受 TCK 门禁),方向一致但 **缺“通道”这一层编排抽象**。
|
||||
|
||||
### 2.8 部署与交付
|
||||
|
||||
@@ -147,7 +147,7 @@ Device = Driver(协议) + Profile(物模型) ← 设备是「协议接入」
|
||||
|
||||
| 优先级 | 借鉴点 | DG-IoT 做法 | DC3 现状 → 落地建议 |
|
||||
|--------|-----------------------------|-----------------------------------------------------------|----------------------------------------------------------------------------------------------------|
|
||||
| P0 | **数据通道抽象** | MQTT/Kafka/HTTP/WS 多通道转发 + MES/WMS/ERP/ESB 多源接入 | 数据桥接驱动 + `mq-abstraction.md`(Proposed) → 在 MQ 端口之上加一层「通道」编排,落地 Kafka 适配器 |
|
||||
| P0 | **数据通道抽象** | MQTT/Kafka/HTTP/WS 多通道转发 + MES/WMS/ERP/ESB 多源接入 | 数据桥接驱动 + 已落地的 MQ 端口抽象 → 在 MQ 端口之上加一层「通道」编排,落地 Kafka 适配器 |
|
||||
| P0 | **规则引擎/数据处理增强** | 160+ 函数、清洗/标准化/实时流 | 仅告警规则 → 扩展规则引擎支持数据清洗/转换/转发,复用 `dc3-common-data` |
|
||||
| P1 | **组态页面低代码** | 数据通道→组态全流程低代码 | home 写死 → 可配置 widget 看板 + 组态/大屏 |
|
||||
| P1 | **物模型贯通** | 物模型一次建模,贯通规则/通道/组态 | Profile 独立 → 打通「模型→规则→通道→看板」链路,落成 `profileShareFlag` 模板市场 |
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
| **Status** | Analysis / proposal — for reference, not a committed roadmap |
|
||||
| **Date** | 2026-08-18 |
|
||||
| **Scope** | 跨项目对比(supOS-Free ↔ IoT DC3),提炼可借鉴点并落地建议 |
|
||||
| **Related** | [storage-abstraction.md](./design/storage-abstraction.md)、[mq-abstraction.md](./design/mq-abstraction.md) |
|
||||
| **Related** | — |
|
||||
| **Discussion** | 开放评审,未进入实施排期 |
|
||||
|
||||
> 说明:本文中 “superos-free” 即 **supOS-Free**(蓝卓数字科技/中控体系的“工厂操作系统”免费版)。
|
||||
@@ -79,7 +79,7 @@
|
||||
#### 3.1 多模态 / 可插拔存储
|
||||
|
||||
- **supOS 做法**:关系 + 时序(TDengine/TimescaleDB)+ 非结构化 + 文件多模态底座。
|
||||
- **DC3 现状**:[storage-abstraction.md](./design/storage-abstraction.md) 已设计 `dc3.db.type` 关系方言 +
|
||||
- **DC3 现状**:已落地的存储抽象(`dc3.db.type` 关系方言,flag day 收敛 PostgreSQL)+
|
||||
`dc3.repository.type` 时序端口(目标 PostgreSQL/MySQL + TimescaleDB/TDengine/InfluxDB/IoTDB),状态为 Proposed — not yet
|
||||
implemented。
|
||||
- **落地建议**:优先实现 **TDengine 时序适配器**(supOS 主打,国内工业客户常点名),并把 `RepositoryService`
|
||||
@@ -95,7 +95,7 @@
|
||||
#### 3.3 可插拔消息总线
|
||||
|
||||
- **supOS 做法**:EMQX(MQTT)+ Kafka(数据总线)+ Node-RED(边缘计算)。
|
||||
- **DC3 现状**:[mq-abstraction.md](./design/mq-abstraction.md) 已设计 `dc3-common-mq` 端口 + 每 broker 一个适配器(RabbitMQ
|
||||
- **DC3 现状**:已落地的 `dc3-common-mq` 端口 + 每 broker 一个适配器(RabbitMQ
|
||||
默认,目标 Kafka/RocketMQ/Pulsar),同样 Proposed。
|
||||
- **落地建议**:先落地 **Kafka 适配器**——工业客户常要求 Kafka 对接大数据平台,是 DC3 在“数据总线”上对标 supOS 的关键一环。
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
| **日期** | 2026-08-18 |
|
||||
| **范围** | MCP 运行时平面:`mcp_runtime.proto`、`McpRuntimeFacade`、`McpGatewayController`,auth 侧的 `OAuthMcpRuntimeServiceImpl` + `McpRuntimeServer` |
|
||||
| **目标** | 每个 MCP 方法一次内聚的 gateway→auth 往返;响应式、非阻塞;真实 input schema;异步审计 |
|
||||
| **相关** | [`mq-abstraction.md`](./mq-abstraction.md) —— 异步审计通道(若采用)所用的 broker port |
|
||||
| **相关** | dc3-common-mq 的 broker port(已落地)—— 异步审计通道(若采用)所用 |
|
||||
| **讨论** | 实施启动前开放评审 |
|
||||
|
||||
## 1. 摘要
|
||||
|
||||
@@ -1,142 +0,0 @@
|
||||
# IoT DC3 消息 Port 设计
|
||||
|
||||
| 项目 | 决策 |
|
||||
|---|---|
|
||||
| 状态 | 已落地;RabbitMQ、Kafka、Pulsar、MQTT 5 四个适配器均受 TCK 门禁 |
|
||||
| 范围 | center、driver、common 之间的异步消息平面 |
|
||||
| 目标 | 一个业务 Port、一个明确的 completion 契约、四个可验证实现 |
|
||||
| 非目标 | broker 间兼容层、同步桥接、业务代码感知具体 broker |
|
||||
|
||||
## 1. 第一性原理
|
||||
|
||||
消息消费的正确性不是“回调被调用”,而是“业务副作用完成后消息才被结算”。因此消费 API 的最小正确
|
||||
抽象是 `Publisher<DeliveryDisposition>`:适配器订阅它,Publisher 完成前绝不 ACK、提交 offset 或发送
|
||||
PUBACK。所有实现都遵循同一状态机:
|
||||
|
||||
```text
|
||||
DELIVERED
|
||||
├─ Publisher complete -> ACK
|
||||
├─ MqPoisonException -> DEAD_LETTER
|
||||
├─ other error -> REQUEUE
|
||||
├─ cancellation/incomplete -> no settlement
|
||||
└─ conflicting disposition -> adapter error, no settlement
|
||||
```
|
||||
|
||||
业务 listener 必须返回 `Mono<Void>` 或 `Flux<?>`,不得在 listener 内部调用 `subscribe()`,也不得取得
|
||||
broker 原生 acknowledgment。`Dc3ListenerProcessor` 是唯一的契约执行点,负责 MDC/request-id、异常分类和
|
||||
默认 `ACK`。
|
||||
|
||||
### 1.1 至少一次与幂等
|
||||
|
||||
Port 明确提供至少一次,不承诺 exactly-once。业务写库、发通知、执行设备命令等副作用必须以 tenant +
|
||||
业务 delivery key 建立幂等约束。重投发生在以下窗口:进程崩溃、Publisher 取消、网络断开、Kafka 重新发布
|
||||
成功但原 offset 尚未提交。重复不是适配器 bug,而是至少一次模型的可观察结果。
|
||||
|
||||
### 1.2 batch 语义
|
||||
|
||||
批量 listener 接收 `List<WireMqDelivery>`,并对整批返回一个 disposition。默认完成才 ACK 整批;错误按
|
||||
统一规则重投或死信整批。适配器不得把批量拆成多个互相矛盾的结算结果。需要单条隔离时由业务显式拆分并
|
||||
设计幂等键,不能假设 broker 支持部分提交。
|
||||
|
||||
## 2. 模块边界
|
||||
|
||||
```text
|
||||
dc3-mq-core
|
||||
├─ BrokerAdapter / BrokerCapabilities
|
||||
├─ MessageSender / ReactiveMessageSender
|
||||
├─ Dc3Listener / MqListener / Dc3ListenerProcessor
|
||||
├─ MqTopic / SubscriptionSpec / WireMqDelivery
|
||||
└─ DeliveryDisposition / retry policy / batch properties
|
||||
▲
|
||||
├─ dc3-mq-rabbitmq
|
||||
├─ dc3-mq-kafka
|
||||
├─ dc3-mq-pulsar
|
||||
└─ dc3-mq-mqtt
|
||||
```
|
||||
|
||||
业务模块只能引用 `dc3-mq-core`。适配器负责物理拓扑、客户端生命周期、序列化、重投和 broker 原生确认。
|
||||
启动时适配器发布 `BrokerCapabilities`,文档和测试只采用该声明,不根据理论能力推断行为。
|
||||
|
||||
## 3. 发送契约
|
||||
|
||||
`ReactiveMessageSender.sendConfirmed(WireMqMessage)` 返回 `Mono<Void>`,完成表示 broker 已确认发布;错误
|
||||
表示发布未被确认。发送方必须在业务完成链中组合该 Publisher,不得 fire-and-forget。结果发布、发件箱
|
||||
重发布和延迟调度均遵循同一确认语义。发送失败不能被转换为成功完成,否则消费方会永久丢失业务结果。
|
||||
|
||||
消息 envelope 使用 JSON body 与统一 headers:
|
||||
|
||||
| Header | 用途 |
|
||||
|---|---|
|
||||
| `dc3-type` | payload 类型 |
|
||||
| `request-id` | 跨 broker trace |
|
||||
| `tenant-id` | 运维过滤;业务 tenant 仍在 payload/命令中显式传递 |
|
||||
| `dc3-correlation-id` | 业务关联键 |
|
||||
| `dc3-partition-key` | 无原生 key 字段时的镜像 |
|
||||
| `dc3-redelivery-count` | 适配器重投计数 |
|
||||
|
||||
## 4. 四个适配器的真实映射
|
||||
|
||||
| 能力 | RabbitMQ | Kafka | Pulsar | MQTT 5 |
|
||||
|---|---|---|---|---|
|
||||
| 任意延迟 | Port 本地调度 | Port 本地调度 | Port 本地调度 | Port 本地调度 |
|
||||
| 死信 | 原生 DLX | Port 无原生队列 | Port 无原生队列 | Port 无原生队列 |
|
||||
| 广播 | 原生交换机/队列 | topic + consumer group | subscription 模式 | `$share` 组 |
|
||||
| 消费确认 | delivery tag | offset 提交 | message id | 手动 PUBACK |
|
||||
| 发布确认 | publisher confirm | producer `acks=all` | send future | QoS 1 PUBACK |
|
||||
| 批量 | 原生 consumer batch | poll batch | receive batch | 不支持 |
|
||||
| 订阅过期 | queue expiry | 不支持 | 不支持 | 不支持 |
|
||||
| 顺序 | 无 | 同一 key 分区内 | 无 | 无 |
|
||||
|
||||
### 4.1 RabbitMQ
|
||||
|
||||
Rabbit 适配器在 channel 上启用 publisher confirms 和 returns。业务 Publisher 完成后才确认 delivery
|
||||
tag;重投使用 `basicNack(requeue=true)`,poison 分支使用死信发布成功后再拒绝原消息。连接、channel 和
|
||||
consumer 的关闭都取消未完成 Publisher,不产生 ACK。
|
||||
|
||||
### 4.2 Kafka
|
||||
|
||||
Kafka 使用 `acks=all` producer 和手动 acknowledgment listener。Kafka 没有 per-message nack;`REQUEUE`
|
||||
通过重新发布到同一逻辑 topic 后提交原 offset 实现,原子性边界由 broker 决定,因此业务必须幂等。消费组
|
||||
offset 持久存在,不提供订阅过期语义。TCK 使用真实 Apache Kafka 验证 Publisher completion、停止重投和
|
||||
重启窗口。
|
||||
|
||||
### 4.3 Pulsar
|
||||
|
||||
Pulsar 使用 persistent topic、shared/exclusive subscription 和原生 message id ack。`REQUEUE` 使用
|
||||
negative acknowledgement,poison 使用适配器的死信发布路径。consumer close 会使未 ack 消息重新可见;
|
||||
Publisher 未完成时不调用 ack/nack。
|
||||
|
||||
### 4.4 MQTT 5
|
||||
|
||||
MQTT 适配器使用 QoS 1、共享订阅和客户端手动 acknowledgment。只有 disposition 已完成且 ACK/死信动作成功
|
||||
后才发送 PUBACK;取消或异常不发送 PUBACK,依靠 broker 重投。协议没有原生批量、延迟、DLQ 或订阅过期,
|
||||
这些能力不在 Port 中伪造。
|
||||
|
||||
## 5. 取消、失败和毒消息
|
||||
|
||||
- `Mono/Flux` cancellation 是未完成事务:适配器释放客户端资源,不结算消息。
|
||||
- transient failure 返回 `REQUEUE`;适配器按自身 broker 语义重投,并保留 redelivery count。
|
||||
- `MqPoisonException` 只用于不可恢复的 wire contract/schema 数据;死信发布失败时仍不得 ACK 原消息。
|
||||
- 同一 delivery 同时产生两个 disposition 是程序错误,抛出冲突异常并保持未结算。
|
||||
- adapter stop 必须等待或取消所有 in-flight completion;不能以 stop 作为隐式成功。
|
||||
|
||||
## 6. 配置与生命周期
|
||||
|
||||
`dc3.mq.type` 只激活一个适配器。所有 adapter bean 在 `start()` 建立消费者,在 `stop()` 取消订阅并释放
|
||||
客户端。连接错误通过 lifecycle Publisher 暴露,不能在后台线程吞掉。业务线程不调用 `block()`、
|
||||
`boundedElastic()` 或同步等待来“适配” broker 客户端。
|
||||
|
||||
## 7. TCK 发布门禁
|
||||
|
||||
`dc3-mq-tck` 对四个适配器执行同一套契约:发布确认、单条/批量消费、key 路由、广播/负载均衡、Publisher
|
||||
completion、重投、poison、取消和生命周期。真实 broker 测试必须验证“消息已投递但 Publisher 未完成,停止
|
||||
consumer 后使用同 group 重建仍可重投”。任何适配器未通过完整 TCK,都不属于支持矩阵。
|
||||
|
||||
```bash
|
||||
mvn -s .mvn/settings.xml -pl dc3-mq/dc3-mq-tck -am verify
|
||||
```
|
||||
|
||||
## 8. 决策结果
|
||||
|
||||
删除无法满足上述 completion 契约的实现,而不是在 Port 中保留同步兼容分支。部署、接口和业务代码都以这套
|
||||
统一异步语义为准;不存在旧 API、旧 ACK 路径或 broker 专属业务分支。
|
||||
@@ -1,82 +0,0 @@
|
||||
# 设计:统一 PostgreSQL R2DBC 存储
|
||||
|
||||
| 项目 | 决策 |
|
||||
| --- | --- |
|
||||
| 状态 | 已落地(flag day) |
|
||||
| 范围 | Auth、Manager、Data、Agentic、运行时操作、审计和时序历史 |
|
||||
| 关系库 | PostgreSQL 18 |
|
||||
| 访问层 | Spring Boot R2DBC + `DatabaseClient` |
|
||||
| 时序扩展 | TimescaleDB,仍位于同一 PostgreSQL 实例 |
|
||||
| 分页 | 关系查询 offset;历史样本签名 cursor |
|
||||
| 兼容策略 | 不保留 JDBC/MyBatis、旧 `R<T>`、旧页码协议或双写适配层 |
|
||||
|
||||
## 1. 第一性原理
|
||||
|
||||
平台的核心问题不是“支持多少数据库”,而是让租户数据在并发写入、取消、重试和跨服务调用下保持可证明的一致性。多套数据库方言会复制事务、锁、JSON、分页、种子和测试语义;收益只有在明确的部署需求存在时才成立。当前没有这种需求,因此选择一个能同时覆盖关系、时序、JSON 和向量能力的 PostgreSQL,并把其语义直接暴露在一个响应式存储边界内。
|
||||
|
||||
## 2. 运行时拓扑
|
||||
|
||||
```text
|
||||
HTTP/WebFlux ─┐
|
||||
gRPC ├─> Reactive service ─> Repository port ─> DatabaseClient
|
||||
Agentic/MCP ──┘ │
|
||||
└─> PostgreSQL 18
|
||||
├─ dc3_* schemas
|
||||
├─ TimescaleDB hypertables
|
||||
└─ pgvector / AGE capabilities
|
||||
```
|
||||
|
||||
Spring Boot 只创建一个连接池和一个 `R2dbcTransactionManager`。服务通过 facade 访问其他域;控制器和业务代码不感知连接 URL、驱动或传输细节。
|
||||
|
||||
## 3. 数据格式
|
||||
|
||||
- ID 使用 UUIDv7 生成的 64 位逻辑标识,写入和 gRPC/HTTP 表示保持稳定。
|
||||
- 时间使用 UTC `Instant`,数据库统一 `TIMESTAMPTZ(6)`;禁止本地时区 `LocalDateTime` 作为跨服务协议。
|
||||
- 扩展字段使用规范化 JSONB(稳定键序、无未定义数字/时间类型);所有参数使用绑定变量。
|
||||
- 响应直接返回资源或 `items` 集合;错误使用 RFC 9457 `application/problem+json`,gRPC 使用标准 status code。
|
||||
|
||||
## 4. 分页与排序
|
||||
|
||||
### 4.1 关系列表
|
||||
|
||||
```json
|
||||
{
|
||||
"offset": 0,
|
||||
"limit": 50,
|
||||
"sort": [{"field": "createTime", "direction": "DESC"}]
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"items": [],
|
||||
"offset": 0,
|
||||
"limit": 50,
|
||||
"total": 0,
|
||||
"hasNext": false
|
||||
}
|
||||
```
|
||||
|
||||
`limit` 强制为 `1..200`;`offset + limit` 防止整数溢出。排序字段先过 endpoint 白名单,再映射为固定 SQL 列。count 与 items 在同一个 R2DBC 事务快照中顺序执行,不使用 `Mono.zip` 跨连接并发查询。
|
||||
|
||||
### 4.2 历史样本
|
||||
|
||||
历史点值按 `(device_time, series_id, message_id)` 生成签名不透明 cursor。请求只携带 `cursor` 和 `limit`,服务端验证租户、窗口和签名;不接受 `page`、`current`、`size`、`records` 或 SQL 列名。
|
||||
|
||||
## 5. 租户与并发
|
||||
|
||||
每个 tenant-scoped repository 方法都接收 `tenantId`,SQL 同时带租户谓词。跨表 join、correlation、series 聚合先限定租户再 join。软删除默认过滤;写入使用幂等键或唯一约束;更新/删除使用版本条件并检查 `rowsUpdated`。驱动租约采用 fencing token,过期 claim 使用行锁和单事务。
|
||||
|
||||
## 6. 响应式与取消
|
||||
|
||||
所有 repository/service/facade/controller 方法返回 `Mono`/`Flux`。R2DBC publisher 的取消信号释放连接、终止下游 gRPC/SSE,并把 Agentic 消息状态落为 `CANCELLED`;禁止 `.block()`、`Schedulers.boundedElastic()` 和“同步客户端外包 Reactor”。长任务返回 `202 OperationAccepted`,状态由 `dc3_operation` 查询或 watch。
|
||||
|
||||
## 7. 测试门禁
|
||||
|
||||
- `check_r2dbc_migration.py`:MyBatis/JDBC/旧分页/旧响应/阻塞桥接均为零。
|
||||
- PostgreSQL schema fingerprint:种子脚本、运行时表和 JSON/时间格式变更必须同步更新指纹。
|
||||
- `dc3-db-tck`:真实 PostgreSQL 容器验证租户隔离、软删除、乐观锁、幂等、fencing、分页和取消边界。
|
||||
- Web/CLI contract:只发送 `offset`、`limit`、`sort` 或 `cursor`,只读取 `items`、`total`、`hasNext`/`nextCursor`;删除统一 HTTP `DELETE`。
|
||||
- gRPC contract:普通 CRUD unary,真实流才 server-streaming;错误使用 `NOT_FOUND`、`INVALID_ARGUMENT`、`PERMISSION_DENIED`、`ALREADY_EXISTS` 等标准状态。
|
||||
|
||||
任何门禁失败都阻止发布。该设计不提供兼容层或渐进双写;需要其他数据库时必须重新评审并建立独立契约,而不是在当前运行时偷偷增加分支。
|
||||
@@ -1,157 +0,0 @@
|
||||
# TSDB 抽象(当前架构)
|
||||
|
||||
> 状态:已落地。本文描述源码中的唯一实现,不保留历史适配器方案。
|
||||
|
||||
## 1. 第一性原理
|
||||
|
||||
平台要解决的是三件事:可靠接收设备样本、按租户和序列读取历史、提供可预测的聚合查询。数据库不是
|
||||
业务边界;业务边界是可取消、可超时、可验证的响应式 Port。因此:
|
||||
|
||||
- 数据面只选择一个技术栈:PostgreSQL/TimescaleDB + R2DBC;
|
||||
- 传输和存储都使用 Reactor,取消必须能到达数据库请求;
|
||||
- 错误通过异常/transport status 表达,不把错误塞入成功 payload;
|
||||
- 不做兼容层、双写和运行时多库分支,避免语义漂移和测试矩阵爆炸。
|
||||
|
||||
## 2. 组件关系
|
||||
|
||||
```text
|
||||
driver -> data ingest service -> R2dbcTsdbStore -> PostgreSQL/TimescaleDB
|
||||
-> dc3_point_value_latest (relational projection)
|
||||
gateway -> data facade -> PointValueService -> TsdbStore
|
||||
```
|
||||
|
||||
`dc3-tsdb-core` 仅包含模型和 Port。`dc3-common-data` 是实现和唯一生产消费者;其他中心通过 facade
|
||||
和 gRPC/HTTP 契约访问,不直接接触 `DatabaseClient`。
|
||||
|
||||
## 3. 领域模型
|
||||
|
||||
```java
|
||||
record SeriesKey(long tenantId, long deviceId, long pointId) {}
|
||||
record SeriesFilter(long tenantId, List<SeriesKey> series) {}
|
||||
record PointValueSample(
|
||||
SeriesKey series,
|
||||
Instant deviceTime,
|
||||
Instant receiveTime,
|
||||
String rawValue,
|
||||
String calValue,
|
||||
Double numericValue,
|
||||
int quality,
|
||||
String messageId,
|
||||
int schemaVersion,
|
||||
String driverNode,
|
||||
long sequence,
|
||||
long fencingToken,
|
||||
long driverId) {}
|
||||
```
|
||||
|
||||
`SeriesFilter` 的 `tenantId` 永远存在;`series` 为空仅表示已授权的租户级扫描。序列 ID 和租户 ID 必须
|
||||
为正数,所有构造路径都执行校验。样本时间使用 UTC 微秒 `Instant`;原始值和数值投影同时保存,禁止
|
||||
用默认值掩盖解析失败。
|
||||
|
||||
## 4. 响应式 Port
|
||||
|
||||
`io.github.pnoker.common.tsdb.spi.TsdbStore` 定义:
|
||||
|
||||
```java
|
||||
Mono<Integer> append(List<PointValueSample> samples);
|
||||
Mono<Map<SeriesKey, List<PointValueSample>>> last(
|
||||
SeriesFilter filter, int limit, TsdbDeadline deadline);
|
||||
Mono<CursorPage<PointValueSample>> history(
|
||||
SeriesFilter filter, TimeWindow window, Cursor cursor,
|
||||
int pageSize, TsdbDeadline deadline);
|
||||
Mono<Map<SeriesKey, WindowAggregate>> aggregate(
|
||||
SeriesFilter filter, AggregateFunction fn, TimeWindow window,
|
||||
Double percentile, TsdbDeadline deadline);
|
||||
Mono<Map<SeriesKey, List<BucketAggregate>>> bucketedAggregate(
|
||||
SeriesFilter filter, AggregateFunction fn, TimeWindow window,
|
||||
Duration bucketWidth, Double percentile, TsdbDeadline deadline);
|
||||
Mono<Long> count(SeriesFilter filter, TimeWindow window, TsdbDeadline deadline);
|
||||
```
|
||||
|
||||
分析操作还包括 `seriesCounts`、`seriesLastSeen`、`latencyHistogram` 和 `correlation`。每个操作都返回
|
||||
`Mono` 或 `Flux`;Port 不暴露 `Future.get`、JDBC `ResultSet` 或阻塞迭代器。
|
||||
|
||||
## 5. R2DBC 实现
|
||||
|
||||
`R2dbcTsdbStore` 使用 `DatabaseClient` 绑定参数执行 SQL,使用已注入的 `R2dbcDialect` 处理时间和方言
|
||||
差异,使用 `TransactionalOperator` 包围需要原子性的写入和读快照。表固定为
|
||||
`dc3_history.dc3_point_value`,实现不读取环境变量来选择其他存储。
|
||||
|
||||
### 写入不变量
|
||||
|
||||
1. `samples` 不得为 `null`、空或含 `null`,数量不得超过 `capabilities.maxAppendBatch`。
|
||||
2. 每个样本的租户、设备、点、driver identity、fencing token 和时间都经过正数/范围校验。
|
||||
3. Upsert 使用 `ON CONFLICT`,绑定全部参数,不拼接用户输入。
|
||||
4. 返回接受数量;任意行失败时整批失败,不返回部分成功。
|
||||
|
||||
### 读取不变量
|
||||
|
||||
- `tenant_id` 是所有 WHERE 和 join 的第一过滤条件;series 集合必须与 filter 的租户一致;
|
||||
- `last`、`history` 和 count 上限硬失败,不自动 clamp;
|
||||
- cursor 是 `(deviceTime, series, messageId)` 的稳定锚点,使用严格小于比较支持降序翻页;
|
||||
- bucket width、percentile、deadline 必须为有限正值;
|
||||
- sort、聚合函数和表名均来自枚举/白名单,不接收任意 SQL 片段;
|
||||
- 每个查询挂接 deadline,取消时释放 R2DBC publisher 和事务。
|
||||
|
||||
## 6. 分页契约
|
||||
|
||||
TSDB 历史查询使用 cursor:
|
||||
|
||||
```json
|
||||
{
|
||||
"items": [ ... ],
|
||||
"nextCursor": "opaque-base64"
|
||||
}
|
||||
```
|
||||
|
||||
管理实体查询使用偏移分页:
|
||||
|
||||
```json
|
||||
{
|
||||
"items": [ ... ],
|
||||
"offset": 0,
|
||||
"limit": 50,
|
||||
"total": 123,
|
||||
"hasNext": true
|
||||
}
|
||||
```
|
||||
|
||||
两者不能混用;客户端不得发送 `page/current/pages/records`。offset、limit 和 sort 只在关系查询边界
|
||||
出现,历史大窗口不得通过不断增大 offset 模拟 cursor。
|
||||
|
||||
## 7. 一致性与租户隔离
|
||||
|
||||
最新值投影、历史写入和幂等 outbox 在 Data 域内按明确事务边界处理。跨服务调用只能经 facade;gRPC
|
||||
请求携带 tenant ID,服务端再次校验资源归属。缓存键必须包含 tenant ID、series identity 和 schema
|
||||
version。任何 `tenant_id IS NULL` 分支都视为缺陷,除非数据模型明确声明全局记录。
|
||||
|
||||
## 8. 错误、取消与超时
|
||||
|
||||
业务错误在服务层抛出 `NotFoundException`、`RequestException` 等,由 gRPC server 映射为
|
||||
`NOT_FOUND`、`INVALID_ARGUMENT`、`FAILED_PRECONDITION` 或 `INTERNAL`。成功响应不含应用层错误信封。
|
||||
客户端将 transport status 转为 Reactor error 或空结果(仅对明确的 not-found 查询)。
|
||||
|
||||
HTTP 流式接口将断开映射为 Reactor cancel;MCP/agentic 流程在取消时停止工具调用并写入最终状态,不能
|
||||
把取消误记为成功。所有跨服务请求设置 deadline,重试仅允许在幂等操作且未观察到副作用时执行。
|
||||
|
||||
## 9. 能力声明
|
||||
|
||||
R2DBC 实现声明微秒精度、租户级扫描、分桶聚合和相关性分析能力;rollup 为 `NONE`,不会伪装成已物化
|
||||
聚合。能力由 `TsdbCapabilities` 暴露,调用方在不支持时明确拒绝或选择正确的原始扫描路径,不能静默
|
||||
返回近似数据。
|
||||
|
||||
## 10. 测试门槛
|
||||
|
||||
- `dc3-tsdb-core`:模型、cursor 比较、percentile 和 deadline 参数契约;
|
||||
- `dc3-common-data`:每个 R2DBC store 的租户、软删除、乐观锁、冲突、空输入和取消负向测试;
|
||||
- integration:真实 PostgreSQL 下验证事务原子性、COUNT/items 快照一致性、微秒边界和跨租户拒绝;
|
||||
- gRPC/HTTP contract:验证直接 payload、标准 status、offset 分页字段和 cursor 不透明性;
|
||||
- 前端/CLI:验证空页、最后一页、total 变化、流取消及不再发送旧字段。
|
||||
|
||||
```bash
|
||||
mvn -s .mvn/settings.xml -q -DskipTests compile
|
||||
mvn -s .mvn/settings.xml -q test \
|
||||
-pl dc3-tsdb/dc3-tsdb-core,dc3-common/dc3-common-data -am
|
||||
```
|
||||
|
||||
新增数据库后必须先提供真实异步驱动和完整契约测试;在此之前不添加第二个适配器或兼容 API。
|
||||
Reference in New Issue
Block a user