Files
go-trustlog/api/persistence/sql
ryan 88f80ffa5e feat: 新增数据库持久化模块(Persistence),实现 Cursor + Retry 双层架构
## 核心功能

### 1. 数据库持久化支持
- 新增完整的 Persistence 模块 (api/persistence/)
- 支持三种持久化策略:
  * StrategyDBOnly - 仅落库,不存证
  * StrategyDBAndTrustlog - 既落库又存证(推荐)
  * StrategyTrustlogOnly - 仅存证,不落库
- 支持多数据库:PostgreSQL, MySQL, SQLite

### 2. Cursor + Retry 双层架构
- CursorWorker:第一道防线,快速发现新记录并尝试存证
  * 增量扫描 operation 表(基于时间戳游标)
  * 默认 10 秒扫描间隔,批量处理 100 条
  * 成功更新状态,失败转入重试队列
- RetryWorker:第二道防线,处理失败记录
  * 指数退避重试(1m → 2m → 4m → 8m → 16m)
  * 默认最多重试 5 次
  * 超限自动标记为死信

### 3. 数据库表设计
- operation 表:存储操作记录,支持可空 IP 字段
- trustlog_cursor 表:Key-Value 模式,支持多游标
- trustlog_retry 表:重试队列,支持指数退避

### 4. 异步最终一致性
- 应用调用立即返回(仅落库)
- CursorWorker 异步扫描并存证
- RetryWorker 保障失败重试
- 完整的监控和死信处理机制

## 修改文件

### 核心代码(11个文件)
- api/persistence/cursor_worker.go - Cursor 工作器(新增)
- api/persistence/repository.go - 数据仓储层(新增)
- api/persistence/schema.go - 数据库 Schema(新增)
- api/persistence/strategy.go - 策略管理器(新增)
- api/persistence/client.go - 客户端封装(新增)
- api/persistence/retry_worker.go - Retry 工作器(新增)
- api/persistence/config.go - 配置管理(新增)

### 修复内部包引用(5个文件)
- api/adapter/publisher.go - 修复 internal 包引用
- api/adapter/subscriber.go - 修复 internal 包引用
- api/model/envelope.go - 修复 internal 包引用
- api/model/operation.go - 修复 internal 包引用
- api/model/record.go - 修复 internal 包引用

### 单元测试(8个文件)
- api/persistence/*_test.go - 完整的单元测试
- 测试覆盖率:28.5%
- 测试通过率:49/49 (100%)

### SQL 脚本(4个文件)
- api/persistence/sql/postgresql.sql - PostgreSQL 建表脚本
- api/persistence/sql/mysql.sql - MySQL 建表脚本
- api/persistence/sql/sqlite.sql - SQLite 建表脚本
- api/persistence/sql/test_data.sql - 测试数据

### 文档(2个文件)
- README.md - 更新主文档,新增 Persistence 使用指南
- api/persistence/README.md - 完整的 Persistence 文档
- api/persistence/sql/README.md - SQL 脚本说明

## 技术亮点

1. **充分利用 Cursor 游标表**
   - 作为任务发现队列,非简单的位置记录
   - Key-Value 模式,支持多游标并发扫描
   - 时间戳天然有序,增量扫描高效

2. **双层保障机制**
   - Cursor:正常流程,快速处理
   - Retry:异常流程,可靠重试
   - 职责分离,监控清晰

3. **可空 IP 字段支持**
   - ClientIP 和 ServerIP 使用 *string 类型
   - 支持 NULL 值,符合数据库最佳实践
   - 使用 sql.NullString 正确处理

4. **完整的监控支持**
   - 未存证记录数监控
   - Cursor 延迟监控
   - 重试队列长度监控
   - 死信队列监控

## 测试结果

-  单元测试:49/49 通过 (100%)
-  代码覆盖率:28.5%
-  编译状态:无错误
-  支持数据库:PostgreSQL, MySQL, SQLite

## Breaking Changes

无破坏性变更。Persistence 模块作为可选功能,不影响现有代码。

## 版本信息

- 版本:v2.1.0
- Go 版本要求:1.21+
- 更新日期:2025-12-23
2025-12-23 18:59:43 +08:00
..

Trustlog 数据库建表脚本

本目录包含 go-trustlog 数据库持久化模块的建表 SQL 脚本。


📁 文件列表

文件 数据库 说明
postgresql.sql PostgreSQL 12+ PostgreSQL 数据库建表脚本
mysql.sql MySQL 8.0+ / MariaDB 10+ MySQL 数据库建表脚本
sqlite.sql SQLite 3+ SQLite 数据库建表脚本
test_data.sql 通用 测试数据插入脚本

📊 表结构说明

1. operation 表

操作记录表,用于存储所有的操作记录。

关键字段:

  • op_id - 操作ID主键
  • client_ip - 客户端IP可空仅落库不存证
  • server_ip - 服务端IP可空仅落库不存证
  • trustlog_status - 存证状态NOT_TRUSTLOGGED / TRUSTLOGGED
  • timestamp - 操作时间戳

索引:

  • idx_operation_timestamp - 时间戳索引
  • idx_operation_status - 存证状态索引
  • idx_operation_doid - DOID 索引

2. trustlog_cursor 表

游标表,用于跟踪处理进度,支持断点续传。

关键字段:

  • id - 游标ID固定为1
  • last_processed_id - 最后处理的操作ID
  • last_processed_at - 最后处理时间

特性:

  • 自动初始化一条记录id=1
  • 用于实现最终一致性

3. trustlog_retry 表

重试表,用于管理失败的存证操作。

关键字段:

  • op_id - 操作ID主键
  • retry_count - 重试次数
  • retry_status - 重试状态PENDING / RETRYING / DEAD_LETTER
  • next_retry_at - 下次重试时间(指数退避)
  • error_message - 错误信息

索引:

  • idx_retry_status - 重试状态索引
  • idx_retry_next_retry_at - 下次重试时间索引

🚀 使用方法

PostgreSQL

# 方式1: 使用 psql 命令
psql -U username -d database_name -f postgresql.sql

# 方式2: 使用管道
psql -U username -d database_name < postgresql.sql

# 方式3: 在 psql 中执行
psql -U username -d database_name
\i postgresql.sql

MySQL

# 方式1: 使用 mysql 命令
mysql -u username -p database_name < mysql.sql

# 方式2: 在 mysql 中执行
mysql -u username -p
USE database_name;
SOURCE mysql.sql;

SQLite

# 方式1: 使用 sqlite3 命令
sqlite3 trustlog.db < sqlite.sql

# 方式2: 在 sqlite3 中执行
sqlite3 trustlog.db
.read sqlite.sql

🔍 验证安装

每个 SQL 脚本末尾都包含验证查询,执行后可以检查:

PostgreSQL

-- 查询所有表
SELECT tablename FROM pg_tables WHERE schemaname = 'public' 
    AND tablename IN ('operation', 'trustlog_cursor', 'trustlog_retry');

MySQL

-- 查询所有表
SHOW TABLES LIKE 'operation%';
SHOW TABLES LIKE 'trustlog_%';

SQLite

-- 查询所有表
SELECT name FROM sqlite_master 
WHERE type='table' 
    AND name IN ('operation', 'trustlog_cursor', 'trustlog_retry');

📝 字段说明

operation 表新增字段

client_ip 和 server_ip

特性:

  • 类型: VARCHAR(32) / TEXT (根据数据库而定)
  • 可空: YES
  • 默认值: NULL

用途:

  • 记录客户端和服务端的 IP 地址
  • 仅用于数据库持久化
  • 不参与存证哈希计算
  • 不会被序列化到 CBOR 格式

示例:

-- 插入 NULL 值(默认)
INSERT INTO operation (..., client_ip, server_ip, ...) 
VALUES (..., NULL, NULL, ...);

-- 插入 IP 值
INSERT INTO operation (..., client_ip, server_ip, ...) 
VALUES (..., '192.168.1.100', '10.0.0.50', ...);

trustlog_status

特性:

  • 类型: VARCHAR(32) / TEXT
  • 可空: YES
  • 可选值:
    • NOT_TRUSTLOGGED - 未存证
    • TRUSTLOGGED - 已存证

用途:

  • 标记操作记录的存证状态
  • 用于查询未存证的记录
  • 支持最终一致性机制

🔄 常用查询

1. 查询未存证的操作

SELECT * FROM operation 
WHERE trustlog_status = 'NOT_TRUSTLOGGED'
ORDER BY timestamp ASC
LIMIT 100;

2. 查询待重试的操作

SELECT * FROM trustlog_retry 
WHERE retry_status IN ('PENDING', 'RETRYING')
    AND next_retry_at <= NOW()
ORDER BY next_retry_at ASC
LIMIT 100;

3. 查询死信记录

SELECT 
    o.op_id,
    o.doid,
    r.retry_count,
    r.error_message,
    r.created_at
FROM operation o
JOIN trustlog_retry r ON o.op_id = r.op_id
WHERE r.retry_status = 'DEAD_LETTER'
ORDER BY r.created_at DESC;

4. 按 IP 查询操作

-- 查询特定客户端IP的操作
SELECT * FROM operation 
WHERE client_ip = '192.168.1.100'
ORDER BY timestamp DESC;

-- 查询未设置IP的操作
SELECT * FROM operation 
WHERE client_ip IS NULL
ORDER BY timestamp DESC;

5. 统计存证状态

SELECT 
    trustlog_status,
    COUNT(*) as count
FROM operation
GROUP BY trustlog_status;

🗑️ 清理脚本

删除所有表

-- PostgreSQL / MySQL
DROP TABLE IF EXISTS trustlog_retry;
DROP TABLE IF EXISTS trustlog_cursor;
DROP TABLE IF EXISTS operation;

-- SQLite
DROP TABLE IF EXISTS trustlog_retry;
DROP TABLE IF EXISTS trustlog_cursor;
DROP TABLE IF EXISTS operation;

清空数据(保留结构)

-- 清空重试表
DELETE FROM trustlog_retry;

-- 清空操作表
DELETE FROM operation;

-- 重置游标表
UPDATE trustlog_cursor SET 
    last_processed_id = NULL,
    last_processed_at = NULL,
    updated_at = CURRENT_TIMESTAMP
WHERE id = 1;

⚠️ 注意事项

1. 字符集和排序规则MySQL

  • 使用 utf8mb4 字符集
  • 使用 utf8mb4_unicode_ci 排序规则
  • 支持完整的 Unicode 字符

2. 索引长度MySQL

  • doid 字段使用前缀索引 doid(255)
  • 避免索引长度超过限制

3. 自增主键

  • PostgreSQL: SERIAL
  • MySQL: AUTO_INCREMENT
  • SQLite: AUTOINCREMENT

4. 时间类型

  • PostgreSQL: TIMESTAMP
  • MySQL: DATETIME
  • SQLite: DATETIME (存储为文本)

5. IP 字段长度

  • 当前长度: 32 字符
  • IPv4: 最长 15 字符 (255.255.255.255)
  • IPv4 with port: 最长 21 字符 (255.255.255.255:65535)
  • IPv6: 最长 39 字符 - 如需支持完整 IPv6建议扩展到 64 字符

🔧 扩展建议

1. 如果需要支持完整 IPv6

-- 修改 client_ip 和 server_ip 字段长度
ALTER TABLE operation MODIFY COLUMN client_ip VARCHAR(64);
ALTER TABLE operation MODIFY COLUMN server_ip VARCHAR(64);

2. 如果需要分区表PostgreSQL

-- 按时间分区
CREATE TABLE operation_partitioned (
    -- ... 字段定义 ...
) PARTITION BY RANGE (timestamp);

CREATE TABLE operation_2024_01 PARTITION OF operation_partitioned
    FOR VALUES FROM ('2024-01-01') TO ('2024-02-01');

3. 如果需要添加审计字段

-- 添加创建人和更新人
ALTER TABLE operation ADD COLUMN created_by VARCHAR(64);
ALTER TABLE operation ADD COLUMN updated_by VARCHAR(64);
ALTER TABLE operation ADD COLUMN updated_at TIMESTAMP;

📚 相关文档


检查清单

安装完成后,请检查:

  • 所有3个表都已创建
  • 所有索引都已创建
  • trustlog_cursor 表有初始记录id=1
  • operation 表可以插入 NULL 的 IP 值
  • operation 表可以插入非 NULL 的 IP 值
  • 查询验证脚本能正常执行

最后更新: 2025-12-23
版本: v1.0.0