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
3 changes: 2 additions & 1 deletion opentdf-core-mode.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ logger:
# port: 5432
# user: postgres
# password: changeme
# statement_timeout_seconds: 30
server:
auth:
enabled: false
Expand Down Expand Up @@ -57,4 +58,4 @@ server:
maxage: 3600
grpc:
reflectionEnabled: true # Default is false
port: 8383
port: 8383
1 change: 1 addition & 0 deletions opentdf-dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ logger:
# password: changeme
# sslmode: prefer
# connect_timeout_seconds: 15
# statement_timeout_seconds: 30
# pool:
# max_connection_count: 4
# min_connection_count: 0
Expand Down
1 change: 1 addition & 0 deletions opentdf-example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ db:
# password: changeme
# sslmode: prefer
# connect_timeout_seconds: 15
# statement_timeout_seconds: 30
# pool:
# max_connection_count: 4
# min_connection_count: 0
Expand Down
24 changes: 15 additions & 9 deletions service/pkg/db/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,15 +89,16 @@ type PoolConfig struct {
}

type Config struct {
Host string `mapstructure:"host" json:"host" default:"localhost"`
Port int `mapstructure:"port" json:"port" default:"5432"`
Database string `mapstructure:"database" json:"database" default:"opentdf"`
User string `mapstructure:"user" json:"user" default:"postgres"`
Password string `mapstructure:"password" json:"password" default:"changeme"`
SSLMode string `mapstructure:"sslmode" json:"sslmode" default:"prefer"`
Schema string `mapstructure:"schema" json:"schema" default:"opentdf"`
ConnectTimeout int `mapstructure:"connect_timeout_seconds" json:"connect_timeout_seconds" default:"15"`
Pool PoolConfig `mapstructure:"pool" json:"pool"`
Host string `mapstructure:"host" json:"host" default:"localhost"`
Port int `mapstructure:"port" json:"port" default:"5432"`
Database string `mapstructure:"database" json:"database" default:"opentdf"`
User string `mapstructure:"user" json:"user" default:"postgres"`
Password string `mapstructure:"password" json:"password" default:"changeme"`
SSLMode string `mapstructure:"sslmode" json:"sslmode" default:"prefer"`
Schema string `mapstructure:"schema" json:"schema" default:"opentdf"`
ConnectTimeout int `mapstructure:"connect_timeout_seconds" json:"connect_timeout_seconds" default:"15"`
StatementTimeoutSeconds int `mapstructure:"statement_timeout_seconds" json:"statement_timeout_seconds"`
Pool PoolConfig `mapstructure:"pool" json:"pool"`

RunMigrations bool `mapstructure:"runMigrations" json:"runMigrations" default:"true"`
MigrationsFS *embed.FS `mapstructure:"-" json:"-"`
Expand All @@ -114,6 +115,7 @@ func (c Config) LogValue() slog.Value {
slog.String("sslmode", c.SSLMode),
slog.String("schema", c.Schema),
slog.Int("connect_timeout_seconds", c.ConnectTimeout),
slog.Int("statement_timeout_seconds", c.StatementTimeoutSeconds),
slog.Group("pool",
slog.Int("max_connection_count", int(c.Pool.MaxConns)),
slog.Int("min_connection_count", int(c.Pool.MinConns)),
Expand Down Expand Up @@ -250,6 +252,10 @@ func (c Config) buildConfig() (*pgxpool.Config, error) {
parsed.MaxConnIdleTime = time.Duration(c.Pool.MaxConnIdleTime) * time.Second
parsed.HealthCheckPeriod = time.Duration(c.Pool.HealthCheckPeriod) * time.Second

if c.StatementTimeoutSeconds > 0 {
parsed.ConnConfig.RuntimeParams["statement_timeout"] = fmt.Sprintf("%ds", c.StatementTimeoutSeconds)
}
Comment on lines +255 to +257
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot May 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# How are migrations run? Do they reuse the pool/SQLDB built from buildConfig?
rg -nP --type=go -C4 '\b(RunMigrations|runMigrations|Migrate|migrat)' service/pkg/db
# Find the migration runner entrypoint and what conn/pool it uses
ast-grep --pattern 'func ($_ $_) RunMigrations($$$) { $$$ }'

Repository: opentdf/platform

Length of output: 10075


statement_timeout is pool-wide and will also affect migrations/admin work

The statement_timeout startup runtime param applies to every statement executed via this client’s shared pgxpool.Pool:

  • migrationInit casts c.Pgx to *pgxpool.Pool and creates the migrations DB handle with stdlib.OpenDBFromPool(pool), so Goose runs migrations on connections from the same pool that carries RuntimeParams["statement_timeout"].
  • This means a pool-wide timeout could fail slow startup migrations/backfills, not just the intended List RPC queries.
  • "%ds" formatting is correct for PostgreSQL statement_timeout (unit-suffixed seconds like "30s").
if c.StatementTimeoutSeconds > 0 {
	parsed.ConnConfig.RuntimeParams["statement_timeout"] = fmt.Sprintf("%ds", c.StatementTimeoutSeconds)
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@service/pkg/db/db.go` around lines 255 - 257, The code currently sets
parsed.ConnConfig.RuntimeParams["statement_timeout"] using
c.StatementTimeoutSeconds which applies a pool-wide timeout and will also affect
migrations/admin work (see migrationInit which casts c.Pgx to *pgxpool.Pool and
uses stdlib.OpenDBFromPool). Remove the RuntimeParams assignment and instead
apply the timeout only where needed: use context.WithTimeout around RPC
handlers/queries that require a per-request timeout (or run "SET LOCAL
statement_timeout = 'Ns'" at the start of the specific transaction) in the
List-query code paths; keep references to c.StatementTimeoutSeconds,
parsed.ConnConfig.RuntimeParams, migrationInit, c.Pgx and stdlib.OpenDBFromPool
to locate the related code.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good point. Should we override this client setting while running migrations? @c-r33d

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I actually was rethinking how I was going to do this because of the coderabbit comment. The problem is that we run migrations during startup and that uses the pool that where we have already specified. I will check if we can override this with a command-level SET.


// Configure the search_path schema immediately on connection opening
parsed.AfterConnect = func(ctx context.Context, conn *pgx.Conn) error {
_, err := conn.Exec(ctx, "SET search_path TO "+pgx.Identifier{c.Schema}.Sanitize())
Expand Down
43 changes: 41 additions & 2 deletions service/pkg/db/db_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ import (

func Test_BuildConfig_ConnString(t *testing.T) {
tests := []struct {
config *Config
want string
config *Config
want string
wantRuntimeParams map[string]string
}{
{
config: &Config{
Expand Down Expand Up @@ -63,6 +64,36 @@ func Test_BuildConfig_ConnString(t *testing.T) {
},
want: "postgres://myuser:mypassword@myhost:1234/mydb?sslmode=require",
},
// Statement timeout should not be added as a runtime parameter unless configured.
{
config: &Config{
Host: "localhost",
Port: 5432,
Database: "opentdf",
User: "postgres",
Password: "changeme",
SSLMode: "prefer",
},
want: "postgres://postgres:changeme@localhost:5432/opentdf?sslmode=prefer",
wantRuntimeParams: map[string]string{
"statement_timeout": "",
},
},
{
config: &Config{
Host: "localhost",
Port: 5432,
Database: "opentdf",
User: "postgres",
Password: "changeme",
SSLMode: "prefer",
StatementTimeoutSeconds: 30,
},
want: "postgres://postgres:changeme@localhost:5432/opentdf?sslmode=prefer",
wantRuntimeParams: map[string]string{
"statement_timeout": "30s",
},
},
}

for _, test := range tests {
Expand All @@ -71,5 +102,13 @@ func Test_BuildConfig_ConnString(t *testing.T) {
assert.Equal(t, test.want, cfg.ConnString())
// AfterConnect hook was defined when building
assert.NotNil(t, cfg.AfterConnect)

for key, value := range test.wantRuntimeParams {
if value == "" {
assert.NotContains(t, cfg.ConnConfig.RuntimeParams, key)
continue
}
assert.Equal(t, value, cfg.ConnConfig.RuntimeParams[key])
}
}
}
Loading