fix(worker): 防止事务泄漏并恢复滞留队列

为 PostgreSQL 连接增加可配置的事务空闲与锁等待超时,并在请求取消或 Worker 退出后使用独立有界上下文回滚事务。\n\n恢复过期任务时按批次使用 SKIP LOCKED,周期重建缺失或终态 River Job;锁等待超时只在确认尚未提交上游时安全重排,避免重复执行和重复结算。\n\n验证:完整 Go 测试、go vet、生产 Kustomize 渲染、gofmt 与 git diff --check 均通过。
This commit is contained in:
2026-07-30 17:27:11 +08:00
parent bc44af751e
commit 6bab0f0749
32 changed files with 545 additions and 106 deletions
@@ -55,6 +55,29 @@ func TestPostgresPoolConfigWarmsBoundedIdleConnections(t *testing.T) {
}
}
func TestPostgresPoolConfigSetsBoundedTransactionTimeouts(t *testing.T) {
config, err := postgresPoolConfigWithOptions(
"postgresql://gateway:password@localhost:5432/gateway?sslmode=disable",
PostgresPoolOptions{
MaxConns: 32,
IdleInTransactionTimeout: 60 * time.Second,
LockTimeout: 30 * time.Second,
},
)
if err != nil {
t.Fatalf("parse PostgreSQL pool config: %v", err)
}
if got := config.ConnConfig.RuntimeParams["idle_in_transaction_session_timeout"]; got != "60000ms" {
t.Fatalf("idle transaction timeout = %q, want 60000ms", got)
}
if got := config.ConnConfig.RuntimeParams["lock_timeout"]; got != "30000ms" {
t.Fatalf("lock timeout = %q, want 30000ms", got)
}
if config.MaxConns != 32 || config.MinIdleConns != 32 {
t.Fatalf("pool bounds max=%d minIdle=%d, want 32/32", config.MaxConns, config.MinIdleConns)
}
}
func TestIsPostgresUnavailableClassifiesConnectivityFailures(t *testing.T) {
for _, testCase := range []struct {
name string
@@ -75,3 +98,12 @@ func TestIsPostgresUnavailableClassifiesConnectivityFailures(t *testing.T) {
t.Fatal("SQL syntax error was incorrectly classified as PostgreSQL unavailable")
}
}
func TestIsPostgresLockTimeout(t *testing.T) {
if !IsPostgresLockTimeout(&pgconn.PgError{Code: "55P03"}) {
t.Fatal("lock timeout was not classified")
}
if IsPostgresLockTimeout(&pgconn.PgError{Code: "57014"}) {
t.Fatal("query cancellation was classified as lock timeout")
}
}