fix(db): 修复事务中SQL错误时的回滚机制

- 添加txFailed标志跟踪事务内的SQL错误状态
- 在事务模式下遇到SQL错误时立即标记事务失败并阻止重试
- 修改Action方法确保SQL出错时强制回滚事务
- 为事务失败场景添加完整的单元测试覆盖
- 防止在事务已回滚的情况下继续执行后续操作导致数据不一致
This commit is contained in:
2026-04-11 21:53:28 +08:00
parent 1546967918
commit f787421f82
4 changed files with 197 additions and 17 deletions
+29 -6
View File
@@ -62,15 +62,21 @@ func (that *HoTimeDB) queryWithRetry(query string, retried bool, args ...interfa
processedArgs := that.processArgs(args)
if that.testTx != nil {
if that.testMu != nil { that.testMu.Lock() }
if that.testMu != nil {
that.testMu.Lock()
}
resl, err = that.testTx.Query(query, processedArgs...)
that.LastErr.SetError(err)
if err != nil {
if that.testMu != nil { that.testMu.Unlock() }
if that.testMu != nil {
that.testMu.Unlock()
}
return nil
}
result := that.Row(resl)
if that.testMu != nil { that.testMu.Unlock() }
if that.testMu != nil {
that.testMu.Unlock()
}
return result
} else if that.Tx != nil {
resl, err = that.Tx.Query(query, processedArgs...)
@@ -80,7 +86,12 @@ func (that *HoTimeDB) queryWithRetry(query string, retried bool, args ...interfa
that.LastErr.SetError(err)
if err != nil {
if !retried {
if that.Tx != nil {
if that.txFailed != nil {
*that.txFailed = true
}
}
if !retried && that.Tx == nil {
if pingErr := db.Ping(); pingErr == nil {
return that.queryWithRetry(query, true, args...)
}
@@ -128,9 +139,13 @@ func (that *HoTimeDB) execWithRetry(query string, retried bool, args ...interfac
processedArgs := that.processArgs(args)
if that.testTx != nil {
if that.testMu != nil { that.testMu.Lock() }
if that.testMu != nil {
that.testMu.Lock()
}
resl, e = that.testTx.Exec(query, processedArgs...)
if that.testMu != nil { that.testMu.Unlock() }
if that.testMu != nil {
that.testMu.Unlock()
}
} else if that.Tx != nil {
resl, e = that.Tx.Exec(query, processedArgs...)
} else {
@@ -143,6 +158,14 @@ func (that *HoTimeDB) execWithRetry(query string, retried bool, args ...interfac
if that.testTx != nil {
return resl, that.LastErr
}
// 事务内不做连接级重试:死锁等错误会导致 MySQL 自动回滚事务,
// 在已回滚的 Tx 上重试会以 auto-commit 模式执行,造成数据不一致
if that.Tx != nil {
if that.txFailed != nil {
*that.txFailed = true
}
return resl, that.LastErr
}
if !retried {
if pingErr := that.DB.Ping(); pingErr == nil {
return that.execWithRetry(query, true, args...)