feat: expose mysql transaction retry metrics
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
driver "github.com/go-sql-driver/mysql"
|
||||
@@ -53,6 +54,24 @@ func Open(ctx context.Context, dsn string, maxOpen, maxIdle int) (*sql.DB, error
|
||||
|
||||
const transactionRetryLimit = 2
|
||||
|
||||
var transactionRetries atomic.Uint64
|
||||
var transactionRetryExhausted atomic.Uint64
|
||||
|
||||
// TransactionStats 是本进程提交前事务重试的累计快照。
|
||||
// 计数器只记录明确的 MySQL 锁等待超时或死锁回调失败,不记录提交回执不明。
|
||||
type TransactionStats struct {
|
||||
Retries uint64 // 已实际执行的重试次数。
|
||||
Exhausted uint64 // 达到重试上限的事务次数。
|
||||
}
|
||||
|
||||
// ReadTransactionStats 返回本进程事务重试计数,不访问数据库。
|
||||
func ReadTransactionStats() TransactionStats {
|
||||
return TransactionStats{
|
||||
Retries: transactionRetries.Load(),
|
||||
Exhausted: transactionRetryExhausted.Load(),
|
||||
}
|
||||
}
|
||||
|
||||
// WithTx retries only a callback failure caused by a MySQL deadlock or lock wait
|
||||
// timeout before COMMIT. It never retries Begin/Commit errors because a commit
|
||||
// response may be lost after the database has already applied the transaction;
|
||||
@@ -61,11 +80,15 @@ func WithTx(ctx context.Context, db *sql.DB, fn func(*sql.Tx) error) error {
|
||||
for attempt := 0; attempt <= transactionRetryLimit; attempt++ {
|
||||
err, retryable := withTxAttempt(ctx, db, fn)
|
||||
if err == nil || !retryable || attempt == transactionRetryLimit {
|
||||
if retryable && attempt == transactionRetryLimit {
|
||||
transactionRetryExhausted.Add(1)
|
||||
}
|
||||
return err
|
||||
}
|
||||
if err := waitTransactionRetry(ctx, attempt); err != nil {
|
||||
return err
|
||||
}
|
||||
transactionRetries.Add(1)
|
||||
}
|
||||
return errors.New("transaction retry loop exhausted")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user