Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion auth/strategy_database_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
"github.com/dgraph-io/ristretto/v2"
"github.com/erpc/erpc/common"
"github.com/erpc/erpc/data"
"github.com/jackc/pgconn"
"github.com/jackc/pgx/v5/pgconn"
"github.com/rs/zerolog"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand Down
41 changes: 30 additions & 11 deletions data/postgresql.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ import (
"github.com/aws/aws-sdk-go/aws/session"
"github.com/erpc/erpc/common"
"github.com/erpc/erpc/util"
"github.com/jackc/pgconn"
"github.com/jackc/pgx/v4"
"github.com/jackc/pgx/v4/pgxpool"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgconn"
"github.com/jackc/pgx/v5/pgxpool"
"github.com/rs/zerolog"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
Expand Down Expand Up @@ -146,7 +146,7 @@ func NewPostgreSQLConnector(
// the util.Initializer auto-retry loop whenever handleConnectionFailure
// marks the task as failed.
//
// All the slow work below — pgxpool.ConnectConfig (TCP dial, TLS, auth,
// All the slow work below — connectPostgreSQLPool (TCP dial, TLS, auth,
// opening MinConns connections) and the one-time schema setup — runs WITHOUT
// the connMu write lock. The previous design held connMu.Lock() for the
// entire ~5s body of this function, which serialized every Get/Set/Lock
Expand Down Expand Up @@ -185,7 +185,7 @@ func (p *PostgreSQLConnector) connectTask(ctx context.Context, cfg *common.Postg
connectCtx, cancel := context.WithTimeout(ctx, p.initTimeout)
defer cancel()

newConn, err := pgxpool.ConnectConfig(connectCtx, config)
newConn, err := connectPostgreSQLPool(connectCtx, config)
if err != nil {
return err
}
Expand All @@ -204,7 +204,7 @@ func (p *PostgreSQLConnector) connectTask(ctx context.Context, cfg *common.Postg
return err
}

newReadonlyConns := p.connectReadonlyPools(connectCtx, cfg, iamSession, pgxpool.ConnectConfig)
newReadonlyConns := p.connectReadonlyPools(connectCtx, cfg, iamSession, connectPostgreSQLPool)

// Publish the new pool. This is the only critical section: readers see
// a consistent snapshot of (conn, listenerPool) and the swap is
Expand Down Expand Up @@ -258,6 +258,25 @@ func (p *PostgreSQLConnector) connectTask(ctx context.Context, cfg *common.Postg

type postgreSQLPoolConnectFunc func(context.Context, *pgxpool.Config) (*pgxpool.Pool, error)

// connectPostgreSQLPool creates a pool and eagerly verifies connectivity.
// pgx v5 pools always connect lazily — pgxpool.NewWithConfig never dials, so
// without the Ping a connectTask against an unreachable server would
// "succeed" and publish a dead pool as ready. The initializer's retry loop
// (and the not-ready sentinel surfaced to callers) is built around the v4
// behavior where pool construction dialed eagerly and failed fast; the Ping
// preserves that contract.
func connectPostgreSQLPool(ctx context.Context, config *pgxpool.Config) (*pgxpool.Pool, error) {
pool, err := pgxpool.NewWithConfig(ctx, config)
if err != nil {
return nil, err
}
if err := pool.Ping(ctx); err != nil {
pool.Close()
return nil, err
}
return pool, nil
}

func (p *PostgreSQLConnector) connectReadonlyPools(
ctx context.Context,
cfg *common.PostgreSQLConnectorConfig,
Expand Down Expand Up @@ -654,7 +673,7 @@ func (p *PostgreSQLConnector) Get(ctx context.Context, index, partitionKey, rang
common.SetTraceSpanError(span, err)
}

if err == pgx.ErrNoRows {
if errors.Is(err, pgx.ErrNoRows) {
err := common.NewErrRecordNotFound(partitionKey, rangeKey, PostgreSQLDriverName)
common.SetTraceSpanError(span, err)
return nil, err
Expand Down Expand Up @@ -1050,9 +1069,9 @@ func (p *PostgreSQLConnector) connectListener(ctx context.Context, channel strin
p.logger.Trace().Str("channel", channel).Msg("attempting to connect to postgres channel")

// Snapshot the current pool state under RLock so we don't hold the
// WRITE lock across the slow pgxpool.ConnectConfig dial below. The
// WRITE lock across the slow connectPostgreSQLPool dial below. The
// previous design held connMu.Lock() for the entire body of this
// function — including ConnectConfig and Acquire — which
// function — including the pool dial and Acquire — which
// serialized every Get/Set/Lock behind any listener that needed to
// build a new pool. This is the same anti-pattern that connectTask
// used to have (see the long comment there for incident context).
Expand All @@ -1073,7 +1092,7 @@ func (p *PostgreSQLConnector) connectListener(ctx context.Context, channel strin
lcfg.MaxConns = p.maxConns
// Build the new listener pool OUTSIDE any lock — this is the
// slow operation (TCP dial + TLS + auth + MinConns conns).
newPool, err := pgxpool.ConnectConfig(ctx, lcfg)
newPool, err := connectPostgreSQLPool(ctx, lcfg)
if err != nil {
time.Sleep(5 * time.Second)
continue
Expand Down Expand Up @@ -1193,7 +1212,7 @@ func (p *PostgreSQLConnector) getWithWildcard(ctx context.Context, pool *pgxpool
var value []byte
err := pool.QueryRow(ctx, query, args...).Scan(&value)

if err == pgx.ErrNoRows {
if errors.Is(err, pgx.ErrNoRows) {
err := common.NewErrRecordNotFound(partitionKey, rangeKey, PostgreSQLDriverName)
common.SetTraceSpanError(span, err)
return nil, err
Expand Down
2 changes: 1 addition & 1 deletion data/postgresql_iam_auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/rds/rdsutils"
"github.com/erpc/erpc/common"
"github.com/jackc/pgx/v4"
"github.com/jackc/pgx/v5"
)

// newRDSBeforeConnect returns a pgxpool BeforeConnect hook that injects a fresh
Expand Down
6 changes: 3 additions & 3 deletions data/postgresql_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ import (
"time"

"github.com/erpc/erpc/common"
"github.com/jackc/pgconn"
"github.com/jackc/pgx/v4"
"github.com/jackc/pgx/v4/pgxpool"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgconn"
"github.com/jackc/pgx/v5/pgxpool"
"github.com/rs/zerolog"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand Down
11 changes: 10 additions & 1 deletion docs/plans/2026-05-06-pgx-v5-migration.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# pgx v5 Migration Audit

**Date:** 2026-05-06
**Status:** Draft
**Status:** Implemented (PLA-1392, 2026-07-08)

Scope: `github.com/jackc/pgx/v4` in `data/postgresql.go`.

Expand Down Expand Up @@ -29,3 +29,12 @@ Suggested follow-up:

- Dedicated pgx v5 PR that updates the imports, adapts pool creation, and adds/updates connector tests for lazy pool startup.
- Validation: `go test ./data -run 'TestPostgre' -count=1`, `go test ./data -run 'TestPostgreSQLDistributedLocking' -count=1`, and full `make test-fast` if runtime allows.

Implementation outcome (PLA-1392):

- `go.mod`: `github.com/jackc/pgx/v4` and direct `github.com/jackc/pgconn` v1 replaced by `github.com/jackc/pgx/v5` (v5.10.0); the v1-era `pgtype`, `pgio`, and `pgproto3/v2` indirects dropped, `puddle/v2` added.
- One correction to the audit: `data/postgresql.go` (PgError SQLSTATE matching in `isPostgresConnectionError`) and two test files did import `pgconn` v1 directly; all moved to `github.com/jackc/pgx/v5/pgconn`.
- Pool creation goes through a new `connectPostgreSQLPool` helper (`pgxpool.NewWithConfig` + eager `Ping`) so `connectTask`, readonly replicas, and the listener pool keep v4's fail-fast dial semantics — without the Ping, a reconnect after schema setup would publish a dead pool as ready.
- `err == pgx.ErrNoRows` comparisons switched to `errors.Is`, since v5 may wrap the sentinel.
- `newRDSBeforeConnect` needed only the import bump: v5 keeps the `func(context.Context, *pgx.ConnConfig) error` hook shape and `User`/`Password` are promoted from the embedded `pgconn.Config`.
- Validated with the real-Postgres container tests (`TestPostgreConnectorInitialization` both readiness cases, `TestPostgreSQLDistributedLocking` all five locking scenarios) plus the `data`/`auth` unit suites and `make test-fast`.
10 changes: 2 additions & 8 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ require (
github.com/golang-jwt/jwt/v4 v4.5.2
github.com/grafana/sobek v0.0.0-20241024150027-d91f02b05e9b
github.com/h2non/gock v1.2.0
github.com/jackc/pgconn v1.14.3
github.com/jackc/pgx/v4 v4.18.3
github.com/jackc/pgx/v5 v5.10.0
github.com/joho/godotenv v1.5.1
github.com/klauspost/compress v1.18.6
github.com/lyft/gostats v0.4.14
Expand Down Expand Up @@ -106,13 +105,9 @@ require (
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/holiman/uint256 v1.3.2 // indirect
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
github.com/jackc/pgio v1.0.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgproto3/v2 v2.3.3 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/pgtype v1.14.4 // indirect
github.com/jackc/puddle v1.3.0 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/jpillora/backoff v1.0.0 // indirect
github.com/kelseyhightower/envconfig v1.4.0 // indirect
Expand Down Expand Up @@ -142,7 +137,6 @@ require (
github.com/prometheus/procfs v0.16.1 // indirect
github.com/relvacode/iso8601 v1.5.0 // indirect
github.com/shirou/gopsutil/v4 v4.26.3 // indirect
github.com/shopspring/decimal v1.3.1 // indirect
github.com/sirupsen/logrus v1.9.4 // indirect
github.com/stretchr/objx v0.5.3 // indirect
github.com/supranational/blst v0.3.16 // indirect
Expand Down
Loading
Loading