Files
go-trustlog/api/persistence/sql/README.md
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

362 lines
7.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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
```bash
# 方式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
```bash
# 方式1: 使用 mysql 命令
mysql -u username -p database_name < mysql.sql
# 方式2: 在 mysql 中执行
mysql -u username -p
USE database_name;
SOURCE mysql.sql;
```
### SQLite
```bash
# 方式1: 使用 sqlite3 命令
sqlite3 trustlog.db < sqlite.sql
# 方式2: 在 sqlite3 中执行
sqlite3 trustlog.db
.read sqlite.sql
```
---
## 🔍 验证安装
每个 SQL 脚本末尾都包含验证查询,执行后可以检查:
### PostgreSQL
```sql
-- 查询所有表
SELECT tablename FROM pg_tables WHERE schemaname = 'public'
AND tablename IN ('operation', 'trustlog_cursor', 'trustlog_retry');
```
### MySQL
```sql
-- 查询所有表
SHOW TABLES LIKE 'operation%';
SHOW TABLES LIKE 'trustlog_%';
```
### SQLite
```sql
-- 查询所有表
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 格式**
**示例**:
```sql
-- 插入 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. 查询未存证的操作
```sql
SELECT * FROM operation
WHERE trustlog_status = 'NOT_TRUSTLOGGED'
ORDER BY timestamp ASC
LIMIT 100;
```
### 2. 查询待重试的操作
```sql
SELECT * FROM trustlog_retry
WHERE retry_status IN ('PENDING', 'RETRYING')
AND next_retry_at <= NOW()
ORDER BY next_retry_at ASC
LIMIT 100;
```
### 3. 查询死信记录
```sql
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 查询操作
```sql
-- 查询特定客户端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. 统计存证状态
```sql
SELECT
trustlog_status,
COUNT(*) as count
FROM operation
GROUP BY trustlog_status;
```
---
## 🗑️ 清理脚本
### 删除所有表
```sql
-- 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;
```
### 清空数据(保留结构)
```sql
-- 清空重试表
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
```sql
-- 修改 client_ip 和 server_ip 字段长度
ALTER TABLE operation MODIFY COLUMN client_ip VARCHAR(64);
ALTER TABLE operation MODIFY COLUMN server_ip VARCHAR(64);
```
### 2. 如果需要分区表PostgreSQL
```sql
-- 按时间分区
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. 如果需要添加审计字段
```sql
-- 添加创建人和更新人
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;
```
---
## 📚 相关文档
- [PERSISTENCE_QUICKSTART.md](../../PERSISTENCE_QUICKSTART.md) - 快速入门
- [README.md](../README.md) - 详细技术文档
- [IP_FIELDS_USAGE.md](../IP_FIELDS_USAGE.md) - IP 字段使用说明
---
## ✅ 检查清单
安装完成后,请检查:
- [ ] 所有3个表都已创建
- [ ] 所有索引都已创建
- [ ] trustlog_cursor 表有初始记录id=1
- [ ] operation 表可以插入 NULL 的 IP 值
- [ ] operation 表可以插入非 NULL 的 IP 值
- [ ] 查询验证脚本能正常执行
---
**最后更新**: 2025-12-23
**版本**: v1.0.0