From 9cd456bae05d34d3395c37cf595f6772e0d293e7 Mon Sep 17 00:00:00 2001 From: TenSt Date: Thu, 18 Jun 2026 13:59:32 +0200 Subject: [PATCH] RHINENG-27056: fix waitForSessionClosed query --- database_admin/update.go | 31 +++++++++++++---- database_admin/update_test.go | 65 +++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 7 deletions(-) create mode 100644 database_admin/update_test.go diff --git a/database_admin/update.go b/database_admin/update.go index 28f53ad8b..cf20609d4 100644 --- a/database_admin/update.go +++ b/database_admin/update.go @@ -3,17 +3,24 @@ package database_admin import ( "app/base/utils" "database/sql" + "errors" "fmt" "os" "strings" "time" "github.com/golang-migrate/migrate/v4/database" + "github.com/lib/pq" log "github.com/sirupsen/logrus" ) var lockUsers = []string{"listener", "evaluator", "manager", "vmaas_sync"} +const activeAppSessionsQuery = `SELECT usename || ' ' || substring(query for 50) +FROM pg_stat_activity +WHERE usename = ANY($1) +LIMIT 1` + func execOrPanic(db *sql.DB, query string, args ...interface{}) { if _, err := db.Exec(query, args...); err != nil { panic(err) @@ -38,18 +45,28 @@ func releaseAdvisoryLock(db *sql.DB) { execOrPanic(db, "SELECT pg_advisory_unlock(123)") } +// findActiveAppSession returns the first open session for lockUsers, if any. +func findActiveAppSession(db *sql.DB) (session string, found bool, err error) { + err = db.QueryRow(activeAppSessionsQuery, pq.Array(lockUsers)).Scan(&session) + if errors.Is(err, sql.ErrNoRows) { + return "", false, nil + } + if err != nil { + return "", false, err + } + return session, true, nil +} + // Wait for closing of all lockUsers database sessions. func waitForSessionClosed(db *sql.DB) { for { - session := "" - err := db.QueryRow( - "SELECT usename || ' ' || substring(query for 50) FROM pg_stat_activity WHERE "+ - "usename IN (?) LIMIT 30;", lockUsers, - ).Scan(&session) + session, found, err := findActiveAppSession(db) if err != nil { - log.Info(err) + utils.LogError("err", err.Error(), "failed to check app database sessions") + time.Sleep(time.Second) + continue } - if session == "" { + if !found { log.Info("No ", strings.Join(lockUsers, ", "), " sessions found") return } diff --git a/database_admin/update_test.go b/database_admin/update_test.go new file mode 100644 index 000000000..40c77fa15 --- /dev/null +++ b/database_admin/update_test.go @@ -0,0 +1,65 @@ +package database_admin + +import ( + "app/base/database" + "app/base/utils" + "database/sql" + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func openAppDB(t *testing.T, user, password string) *sql.DB { + t.Helper() + url := fmt.Sprintf("postgres://%s:%s@%s:%d/%s?sslmode=%s", + user, password, + utils.CoreCfg.DBHost, utils.CoreCfg.DBPort, + utils.CoreCfg.DBName, utils.CoreCfg.DBSslMode, + ) + db, err := sql.Open("postgres", url) + require.NoError(t, err) + require.NoError(t, db.Ping()) + t.Cleanup(func() { _ = db.Close() }) + return db +} + +// Query errors must not be treated as "no sessions". Use an unreachable host/port so +// QueryRow fails on connect; sql.Open itself does not connect. +func TestFindActiveAppSessionInvalidDB(t *testing.T) { + db, err := sql.Open("postgres", "postgres://127.0.0.1:1/nope?sslmode=disable&connect_timeout=1") + require.NoError(t, err) + t.Cleanup(func() { _ = db.Close() }) + + _, found, err := findActiveAppSession(db) + assert.Error(t, err) + assert.False(t, found) +} + +func TestFindActiveAppSessionNoRows(t *testing.T) { + utils.SkipWithoutDB(t) + database.Configure() + + _, db := dbConn() + t.Cleanup(func() { _ = db.Close() }) + + _, found, err := findActiveAppSession(db) + assert.NoError(t, err) + assert.False(t, found) +} + +func TestFindActiveAppSessionFound(t *testing.T) { + utils.SkipWithoutDB(t) + database.Configure() + + _ = openAppDB(t, "manager", utils.Getenv("MANAGER_PASSWORD", "manager")) + + _, db := dbConn() + t.Cleanup(func() { _ = db.Close() }) + + session, found, err := findActiveAppSession(db) + require.NoError(t, err) + assert.True(t, found) + assert.Contains(t, session, "manager") +}