为 PostgreSQL 连接增加可配置的事务空闲与锁等待超时,并在请求取消或 Worker 退出后使用独立有界上下文回滚事务。\n\n恢复过期任务时按批次使用 SKIP LOCKED,周期重建缺失或终态 River Job;锁等待超时只在确认尚未提交上游时安全重排,避免重复执行和重复结算。\n\n验证:完整 Go 测试、go vet、生产 Kustomize 渲染、gofmt 与 git diff --check 均通过。
54 lines
1.3 KiB
Go
54 lines
1.3 KiB
Go
package store
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"time"
|
|
|
|
"github.com/jackc/pgx/v5"
|
|
"github.com/jackc/pgx/v5/pgconn"
|
|
)
|
|
|
|
const transactionRollbackTimeout = 5 * time.Second
|
|
|
|
type Tx interface {
|
|
Exec(ctx context.Context, sql string, arguments ...any) (pgconn.CommandTag, error)
|
|
QueryRow(ctx context.Context, sql string, args ...any) pgx.Row
|
|
}
|
|
|
|
func (s *Store) InTx(ctx context.Context, fn func(Tx) error) error {
|
|
return s.beginTransaction(ctx, func(tx pgx.Tx) error {
|
|
return fn(tx)
|
|
})
|
|
}
|
|
|
|
func (s *Store) beginTransaction(ctx context.Context, fn func(pgx.Tx) error) (err error) {
|
|
tx, err := s.pool.Begin(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// A Worker shutdown cancels ctx before in-flight callbacks return. Use an
|
|
// independent bounded context so rollback still reaches PostgreSQL.
|
|
defer func() {
|
|
rollbackCtx, cancel := context.WithTimeout(context.Background(), transactionRollbackTimeout)
|
|
defer cancel()
|
|
rollbackErr := tx.Rollback(rollbackCtx)
|
|
if err == nil && rollbackErr != nil && !errors.Is(rollbackErr, pgx.ErrTxClosed) {
|
|
err = rollbackErr
|
|
}
|
|
}()
|
|
if err = fn(tx); err != nil {
|
|
return err
|
|
}
|
|
return tx.Commit(ctx)
|
|
}
|
|
|
|
func rollbackTransaction(tx pgx.Tx) {
|
|
if tx == nil {
|
|
return
|
|
}
|
|
ctx, cancel := context.WithTimeout(context.Background(), transactionRollbackTimeout)
|
|
defer cancel()
|
|
_ = tx.Rollback(ctx)
|
|
}
|