# TeaQL Runtime 架构设计:增量账本引擎 (Incremental Delta Ledger)
## 1. 架构初衷与“图查分”的偏移反思
TeaQL 的核心初衷是提供极其轻量、明确且高性能的数据持久化机制。
在项目的最初设计中,我们在实体内部引入了 `EntityRoot`,用于隐式地在 Aggregate (聚合) 内部共享变更上下文 (`ChangeSetStack`)。这本质上是 **Unit of Work (工作单元)** 与 **Event Sourcing (事件溯源)** 的变体。
然而在后续的框架演进中,架构出现了一定程度的“偏移 (Drift)”。我们引入了类似 Hibernate 的快照比对机制,通过构建 `GraphMutationPlan` 来深度遍历整个对象图,对比 `original_values` 与 `current_values` 从而计算出变更。
这种“图遍历查分”不仅带来了巨大的递归性能开销,还引发了诸如**循环引用去重 (`visited_nodes` 机制)**、**无意义的空更新 (Empty Updates)** 以及**乐观锁并发冲突**等一系列复杂的衍生 Bug。
如今,我们将回归最纯粹的初始设计:**彻底抛弃深度图遍历,使用“扁平化的增量账本”作为 Runtime 保存引擎的唯一事实来源。**
## 2. 核心设计:扁平化增量账本 (Flat Incremental Ledger)
在这个架构下,`save_entity` 不再做任何探索与对比动作,而是退化为一个极速的“账单消费器”。
### 2.1 隐式的树级共享账本
- 每个实体对象底层持有 `root: EntityRoot`。
- 通过生成的 Setter 方法(如 `update_name`),变更被直接写入 `EntityRoot` 中的 `EntityChangeSet` (一个以 `EntityKey` 为键的 Map)。
- 当父对象附加子对象(如 `attach_root_recursive`)时,子对象共享父对象的 `EntityRoot` 引用。因此,整个图的所有修改,**都被扁平化地汇总在这唯一的一个字典表里**。
### 2.2 O(1) 的修改发现与下发
保存时,Runtime 直接读取这个扁平的字典表,无需关心对象的树形嵌套结构。谁在表里,谁就生成 SQL 执行。没有修改的实体在账单中不存在,从而实现真正的零损耗。
### 2.3 客户端全局发号器 (Client-side ID Generator) 与纯粹扁平化
针对“如何处理关联新增实体的主外键依赖”这一难题,我们的最终解法是:引入内部全局 U64 ID Generator。
所有实体在实例化(`runtime_new`)的瞬间,即被赋予全局唯一的 U64 ID。因此:
- 关系赋值变成普通的字面量赋值,例如 `log.update_task_id(123456)`。
- 账本中记录的外键关系全是**真实且最终的 ID**,不依赖数据库的自增返回。
- Runtime 在下发 SQL 时,**彻底去除了延迟引用与拓扑排序的复杂度**。执行引擎无需关心实体插入的先后顺序,可以直接并发、批量地下发 Batch Insert/Update SQL,达到极致的执行效率。
## 3. 智能批处理机制 (Intelligent Batching)
基于这套扁平账单,我们在执行层发挥出了强大的合并特性(这一特性我们早期就已具备,在扁平账本下将释放更大威力):
1. **批量插入 (Batch Insert)**:账单中所有同类型的新实体,自动拼合成 `INSERT INTO ... VALUES (...), (...), ...`,最大化写入吞吐。
2. **批量删除 (Batch Delete)**:将 `deleted_keys` 中相同实体的删除动作合并为 `DELETE FROM ... WHERE id IN (...)`。
3. **基于签名的批量更新 (Signature-based Batch Update)**:
- 即使是同一张表的不同实体,如果它们被更新的字段不同(比如 A 更新了 name,B 更新了 status),传统 ORM 无法合并。
- 我们的引擎会将**更新字段的组合(指纹/Signature)**进行分组。所有同样只更新了 `name` 字段的实体会被并入同一个 Batch Statement 中。这一特性对于微服务下乱序并发状态更新有着极为可观的性能提升。
## 4. 可观测性创新:基于业务意图的更新追踪与二次修改侦测
这是我们在这套机制中加入的一个极具诊断价值的独创细节。
### 4.1 问题的产生
在正常的业务代码中,同一个事物在一个请求周期内,针对某一个字段的赋值理应只有一次(例如 `task.update_status(READY)`)。
然而,同一个字段往往会被不规范的代码覆盖赋值多次(二次修改)。这往往是业务流程混乱或无目标性动作的先兆,是隐藏 Bug 的重灾区。
### 4.2 诊断记录机制与意图追踪 (Intent-Driven Tracking)
`EntityRoot` 除了维护属性状态,还持有着当前的业务上下文:`comment`。
我们的 `EntityChangeSet` 在内部维护着一张细粒度的**修改流水表 (update_history)**。
- 当调用 Setter 时,系统会将修改的**字段名、旧值、新值,以及此刻 `EntityRoot` 中的 `comment`(业务意图)** 一并打包推入历史流。
- 如果发生二次覆盖,历史流会清晰记录:
* 第一次修改: `status` -> 1, 意图: `"执行自动化状态流转"`
* 第二次修改: `status` -> 2, 意图: `"管理员强制纠偏状态"`
- **生成时机**:这份意图流水的拦截与生成,发生在系统正式的数据库 Audit(如 `TaskExecutionLog` 持久化)**之前**。它是构建高级 Audit 的原材料。
- **跨系统追踪号 (Change ID)**:在未来,我们会为每一次带有意图的组合变更分配一个唯一的**变更编号 (Change ID)**,使其能够跨越多个系统进行核对与追踪。
### 4.3 面向 AI 编程时代的可观测性延展
引入极细粒度的“意图-变更映射”并不仅仅是为了找 Bug。
在 **AI 辅助编程 (AI Coding)** 全面普及的未来,系统通常由 AI 和人类共同编写与维护。这就要求系统底层动作的**透明化 (Transparency)** 达到极高的标准。
通过精确记录每一处数据修改背后的业务意图,我们能够为未来更高级的线上与离线基础设施(如自动化排错平台、可观测性诊断工具)提供最核心的数据支撑,使系统的运行轨迹彻底白盒化。
## 5. 乐观锁 (Optimistic Locking) 与标记删除 (Logical Delete) 的安全闭环
在企业级应用中,所有的删除(Delete)往往都是标记删除(更新 `version = -1` 或 `is_deleted = true`),且所有的增删改操作都必须受限于乐观锁保护。如果单纯抛弃图遍历而生成无版本的 Batch SQL,会导致灾难性的“脏更新”和“脏删除”。增量账本通过引入静态版本锚点完美解决了此问题。
### 5.1 版本号注册表 (Version Registry)
在 `EntityRoot` / `RootContext` 内部,维护一张 `original_versions: BTreeMap<EntityKey, i64>` 的注册表。当任何实体被加载到内存或挂载时,系统立刻将其原始版本号登记在册。这是绝对的防线。
### 5.2 安全组装 Batch SQL
无论是普通的更新还是标记删除,执行器在读取扁平的账本(`ChangeSet` 和 `deleted_keys`)时,都必须去 `original_versions` 里查阅该实体的基准版本。
- **更新**:翻译为 `UPDATE ... SET ... version = version + 1 WHERE id = ? AND version = {基准版本}`
- **标记删除**:翻译为 `UPDATE ... SET version = -1 WHERE id = ? AND version = {基准版本}`
如果有任何实体在操作间隙被其他事务修改,批处理 SQL 的影响行数会返回 `0`,从而精准抛出 `OptimisticLockConflict`。
### 5.3 严格拦截“版本号篡改”与“漂移”
版本号是底层框架的神圣元数据。借助我们在 4.2 节设计的“意图追踪流水”,系统能轻松实现针对版本号的最高级别告警:
1. **拦截人工篡改**:如果发现 `version` 字段作为被更新的属性出现在了修改流水中(即业务代码手动调用了 `update_version`),框架将发出 `FATAL` 级告警,拦截这种极易破坏乐观锁的违规操作。
2. **侦测上下文漂移**:如果同一个对象在单次请求周期内发生基准版本号变更,同样意味着数据上下文已失控,系统会精准曝光这一危险行为。
---
*注:此架构文档确立了 TeaQL Runtime 未来的核心执行逻辑准则,即从“声明式的状态侦查”向“确定性的增量事件账本”彻底转变。当前为理论固化阶段,优先解决现有系统最高优先级的 Bug,本重构暂缓实施。*