From 83d9202b6f29d6415a436c34e344783c6d40b313 Mon Sep 17 00:00:00 2001 From: Chris Reed Date: Thu, 28 May 2026 15:31:29 -0500 Subject: [PATCH 1/2] feat(core): Add statement timeout parameter. --- opentdf-core-mode.yaml | 3 ++- opentdf-dev.yaml | 1 + opentdf-example.yaml | 1 + service/pkg/db/db.go | 24 ++++++++++++++-------- service/pkg/db/db_test.go | 43 +++++++++++++++++++++++++++++++++++++-- 5 files changed, 60 insertions(+), 12 deletions(-) diff --git a/opentdf-core-mode.yaml b/opentdf-core-mode.yaml index 98401d19ee..c4b8868d12 100644 --- a/opentdf-core-mode.yaml +++ b/opentdf-core-mode.yaml @@ -17,6 +17,7 @@ logger: # port: 5432 # user: postgres # password: changeme +# statement_timeout: 30s server: auth: enabled: false @@ -57,4 +58,4 @@ server: maxage: 3600 grpc: reflectionEnabled: true # Default is false - port: 8383 \ No newline at end of file + port: 8383 diff --git a/opentdf-dev.yaml b/opentdf-dev.yaml index 5a90cfa605..d73f6023ca 100644 --- a/opentdf-dev.yaml +++ b/opentdf-dev.yaml @@ -10,6 +10,7 @@ logger: # password: changeme # sslmode: prefer # connect_timeout_seconds: 15 +# statement_timeout: 30s # pool: # max_connection_count: 4 # min_connection_count: 0 diff --git a/opentdf-example.yaml b/opentdf-example.yaml index 9bc9f90ef2..3be7a573f5 100644 --- a/opentdf-example.yaml +++ b/opentdf-example.yaml @@ -10,6 +10,7 @@ db: # password: changeme # sslmode: prefer # connect_timeout_seconds: 15 +# statement_timeout: 30s # pool: # max_connection_count: 4 # min_connection_count: 0 diff --git a/service/pkg/db/db.go b/service/pkg/db/db.go index 366093a3d5..a40de3e3d4 100644 --- a/service/pkg/db/db.go +++ b/service/pkg/db/db.go @@ -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"` + StatementTimeout string `mapstructure:"statement_timeout" json:"statement_timeout"` + Pool PoolConfig `mapstructure:"pool" json:"pool"` RunMigrations bool `mapstructure:"runMigrations" json:"runMigrations" default:"true"` MigrationsFS *embed.FS `mapstructure:"-" json:"-"` @@ -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.String("statement_timeout", c.StatementTimeout), slog.Group("pool", slog.Int("max_connection_count", int(c.Pool.MaxConns)), slog.Int("min_connection_count", int(c.Pool.MinConns)), @@ -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.StatementTimeout != "" { + parsed.ConnConfig.RuntimeParams["statement_timeout"] = c.StatementTimeout + } + // 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()) diff --git a/service/pkg/db/db_test.go b/service/pkg/db/db_test.go index 37d0dc4815..eb0f5cee71 100644 --- a/service/pkg/db/db_test.go +++ b/service/pkg/db/db_test.go @@ -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{ @@ -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", + StatementTimeout: "30s", + }, + want: "postgres://postgres:changeme@localhost:5432/opentdf?sslmode=prefer", + wantRuntimeParams: map[string]string{ + "statement_timeout": "30s", + }, + }, } for _, test := range tests { @@ -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]) + } } } From 81ff8de0003eaffe70841c18cd13d70e0f91aee5 Mon Sep 17 00:00:00 2001 From: Chris Reed Date: Fri, 29 May 2026 07:19:54 -0500 Subject: [PATCH 2/2] change to statement_timeout_seconds. --- opentdf-core-mode.yaml | 2 +- opentdf-dev.yaml | 2 +- opentdf-example.yaml | 2 +- service/pkg/db/db.go | 26 +++++++++++++------------- service/pkg/db/db_test.go | 14 +++++++------- 5 files changed, 23 insertions(+), 23 deletions(-) diff --git a/opentdf-core-mode.yaml b/opentdf-core-mode.yaml index c4b8868d12..508b73ea2d 100644 --- a/opentdf-core-mode.yaml +++ b/opentdf-core-mode.yaml @@ -17,7 +17,7 @@ logger: # port: 5432 # user: postgres # password: changeme -# statement_timeout: 30s +# statement_timeout_seconds: 30 server: auth: enabled: false diff --git a/opentdf-dev.yaml b/opentdf-dev.yaml index d73f6023ca..e021126959 100644 --- a/opentdf-dev.yaml +++ b/opentdf-dev.yaml @@ -10,7 +10,7 @@ logger: # password: changeme # sslmode: prefer # connect_timeout_seconds: 15 -# statement_timeout: 30s +# statement_timeout_seconds: 30 # pool: # max_connection_count: 4 # min_connection_count: 0 diff --git a/opentdf-example.yaml b/opentdf-example.yaml index 3be7a573f5..2c82108bea 100644 --- a/opentdf-example.yaml +++ b/opentdf-example.yaml @@ -10,7 +10,7 @@ db: # password: changeme # sslmode: prefer # connect_timeout_seconds: 15 -# statement_timeout: 30s +# statement_timeout_seconds: 30 # pool: # max_connection_count: 4 # min_connection_count: 0 diff --git a/service/pkg/db/db.go b/service/pkg/db/db.go index a40de3e3d4..d3ea7cd04a 100644 --- a/service/pkg/db/db.go +++ b/service/pkg/db/db.go @@ -89,16 +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"` - StatementTimeout string `mapstructure:"statement_timeout" json:"statement_timeout"` - 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:"-"` @@ -115,7 +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.String("statement_timeout", c.StatementTimeout), + 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)), @@ -252,8 +252,8 @@ 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.StatementTimeout != "" { - parsed.ConnConfig.RuntimeParams["statement_timeout"] = c.StatementTimeout + if c.StatementTimeoutSeconds > 0 { + parsed.ConnConfig.RuntimeParams["statement_timeout"] = fmt.Sprintf("%ds", c.StatementTimeoutSeconds) } // Configure the search_path schema immediately on connection opening diff --git a/service/pkg/db/db_test.go b/service/pkg/db/db_test.go index eb0f5cee71..1697c8ccf1 100644 --- a/service/pkg/db/db_test.go +++ b/service/pkg/db/db_test.go @@ -81,13 +81,13 @@ func Test_BuildConfig_ConnString(t *testing.T) { }, { config: &Config{ - Host: "localhost", - Port: 5432, - Database: "opentdf", - User: "postgres", - Password: "changeme", - SSLMode: "prefer", - StatementTimeout: "30s", + 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{