feat: expose transaction failure stages and conservative outcomes
This commit is contained in:
@@ -9,7 +9,7 @@
|
||||
- `Config`:驱动、DSN 与连接池参数;嵌入项目配置,通过文件显式引用变量。
|
||||
- `Open` / `OpenConnector`:通过 DSN 或标准 Connector 创建连接池、设置连接上限并在返回前执行 `PingContext`。
|
||||
- `Health`:用于就绪检查,等待连接和 Ping 均受 `PingTimeout` 限制。
|
||||
- `InTx`:统一事务提交和回滚;工作函数返回错误或 panic 时回滚。
|
||||
- `InTx`:统一事务提交和回滚;工作函数返回错误或 panic 时尝试回滚。返回 `TxError` 区分阶段与结果,提交错误不代表已回滚。
|
||||
- `Close`:由嵌入的 `*sql.DB` 提供,应用退出时显式调用。
|
||||
|
||||
框架不导入 MySQL、PostgreSQL 等具体驱动。应用负责注册驱动:
|
||||
@@ -63,6 +63,55 @@ python3 scripts/verifyDatabase.py --version v0.2.0
|
||||
|
||||
配置结构作为项目 Config 的字段,Default() 初始化连接池,文件按需覆盖。DSN 可使用 `dsn: "${DB_DSN}"`;database.Config 的 JSON 序列化不输出 DSN 原值。不要打印原始配置对象。当前 API 脚手架使用结构化 MySQL 适配器,由 svc 统一初始化与关闭。
|
||||
|
||||
## 事务阶段与结果
|
||||
|
||||
v0.25 起,`db.InTx(ctx, options, work)` 签名不变,所有普通错误返回都是 `*database.TxError`。返回 nil 表示 Commit 返回成功;框架不重试回调、提交或整个事务。业务通过标准库 errors.As/Is 判断,不解析错误文本:
|
||||
|
||||
```go
|
||||
err := db.InTx(ctx, nil, func(tx *sql.Tx) error {
|
||||
_, err := tx.ExecContext(ctx, "INSERT INTO objects (id, storage_key) VALUES (?, ?)", id, key)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
var txErr *database.TxError
|
||||
if errors.As(err, &txErr) {
|
||||
switch txErr.Outcome {
|
||||
case database.TxOutcomeNotStarted, database.TxOutcomeRolledBack:
|
||||
// 当前受管理事务未执行回调或已确认回滚。
|
||||
// 业务结合文件归属、并发操作和其他副作用,决定是否清理。
|
||||
default:
|
||||
// 包括 TxOutcomeUnknown:保留文件,交给业务后续核查。
|
||||
// 不因为收到提交错误就删除文件或直接重试整个操作。
|
||||
}
|
||||
// txErr.Stage 是 begin / work / commit。
|
||||
// txErr.Err 是主要原因;txErr.RollbackErr 是回滚调用返回的原因。
|
||||
}
|
||||
if errors.Is(err, context.DeadlineExceeded) {
|
||||
// 说明错误链包含超时,不意味着数据库没有提交。
|
||||
}
|
||||
return err
|
||||
}
|
||||
```
|
||||
|
||||
`errors` 为标准库包,业务自己的数据库驱动和 SQL 参数规则不变。完整可运行示例见 [examples/transactions](../examples/transactions/README.md)。
|
||||
|
||||
| 情况 | Stage | Outcome | RollbackErr |
|
||||
| --- | --- | --- | --- |
|
||||
| 未初始化数据库、nil 回调、BeginTx 报错 | TxStageBegin | TxOutcomeNotStarted | nil;框架没有拿到事务,不执行回调 |
|
||||
| 回调报错,显式 Rollback 成功 | TxStageWork | TxOutcomeRolledBack | nil |
|
||||
| 回调报错,Rollback 失败或返回 ErrTxDone | TxStageWork | TxOutcomeUnknown | 原始回滚错误 |
|
||||
| Commit 报错,包括取消/断网/驱动错误 | TxStageCommit | TxOutcomeUnknown | 后续清理的 Rollback 返回值,可能为 ErrTxDone 或 nil |
|
||||
|
||||
`Stage` 描述主要失败发生的位置;`Outcome` 只描述框架掌握的结果,二者独立。提交调用返回错误后一律保守标为 unknown,即使某个驱动的某种错误意味着没有提交,通用层也不根据错误文本猜测。标准库在 Commit 后通常已经结束本地事务对象,后续 Rollback 返回 `sql.ErrTxDone`;它不能证明数据库已回滚,框架保留该原因。
|
||||
|
||||
`TxError.Err` 与 `RollbackErr` 分开保存;`Unwrap() []error` 按主要原因、回滚原因顺序暴露,所以 errors.Is/As 可查两条链。需要区分来源时对 `txErr.Err` 或 `txErr.RollbackErr` 单独判断。回调返回另一个事务的 TxError 时外层不把它压平:外层结果只属于外层事务,内部事务的结果仍在 Err 中。
|
||||
|
||||
**结果保证的范围:** 回调使用给定 `*sql.Tx` 完成事务操作,不自行 Commit/Rollback,不执行会隐式提交的 SQL,不将事务交给后台 goroutine。非事务表、其他连接、嵌套的独立事务、文件和第三方调用的副作用不在这份保证内。框架没有删除文件、入队补偿或分布式事务规则。
|
||||
|
||||
**取消与异常边界:** context 取消时 database/sql 可能自行异步回滚,且不公开那次驱动 Rollback 的原始错误。框架若只观察到 ErrTxDone,就返回 unknown,不能补造成功证据或不存在的底层原因。正常返回路径上,框架实际观察到的主要/回滚原因全部保留。panic/runtime.Goexit 保持原有传播行为并尝试回滚,不转换为普通 error;这类展开没有错误返回值,因此不能把回滚原因作为返回值交给调用方。nil context 使用 Background,nil 回调在开启事务前报错。
|
||||
|
||||
**兼容说明:** 已有 errors.Is/As 继续生效;以前直接用 `err == sentinel`、`err.(*SomeError)` 或匹配文本的调用要改成 errors.Is/As。业务 APIError、Go 错误目录和参数提示仍可穿过包装解析,驱动错误不会自动进入公开提示。TxError.Error 包含内部原因,仅供内部诊断。GORM 自己的 Transaction 不经过本方法,也不会自动变成 TxError;可选 gormx 的连接池接入不变。
|
||||
|
||||
## Connector 接入(v0.9.1-preview)
|
||||
|
||||
项目已有 `database/sql/driver.Connector` 时使用 `OpenConnector`。这是标准驱动接口,核心包不增加具体驱动依赖。项目可以在同一进程创建多个使用不同证书、日志、拨号策略的连接池。
|
||||
|
||||
Reference in New Issue
Block a user