diff --git a/contrib/helm/charts/probo/templates/deployment.yaml b/contrib/helm/charts/probo/templates/deployment.yaml index e01b0d820..4cf8b19f6 100644 --- a/contrib/helm/charts/probo/templates/deployment.yaml +++ b/contrib/helm/charts/probo/templates/deployment.yaml @@ -93,6 +93,10 @@ spec: value: {{ .Values.postgresql.maxConnIdleTimeSeconds | default "1800" | quote }} - name: PG_MAX_CONN_LIFETIME_SECONDS value: {{ .Values.postgresql.maxConnLifetimeSeconds | default "3600" | quote }} + - name: PG_MAX_CONN_LIFETIME_JITTER_SECONDS + value: {{ .Values.postgresql.maxConnLifetimeJitterSeconds | default "300" | quote }} + - name: PG_HEALTH_CHECK_PERIOD_SECONDS + value: {{ .Values.postgresql.healthCheckPeriodSeconds | default "60" | quote }} {{- if .Values.postgresql.caBundle }} - name: PG_CA_BUNDLE valueFrom: diff --git a/contrib/helm/charts/probo/values.yaml b/contrib/helm/charts/probo/values.yaml index c76f65a26..ad5a5982a 100644 --- a/contrib/helm/charts/probo/values.yaml +++ b/contrib/helm/charts/probo/values.yaml @@ -311,6 +311,12 @@ postgresql: # Maximum lifetime of a connection before being recycled, in # seconds. Default 3600 (1 hour) matches pgx defaults. maxConnLifetimeSeconds: 3600 + # Jitter added to maxConnLifetime to spread reconnect storms, in + # seconds. Default 300 (5 minutes). + maxConnLifetimeJitterSeconds: 300 + # How often the pool checks idle connections, in seconds. Default 60 + # (1 minute). + healthCheckPeriodSeconds: 60 # PostgreSQL TLS/SSL configuration # caBundle: | # -----BEGIN CERTIFICATE----- diff --git a/pkg/bootstrap/builder.go b/pkg/bootstrap/builder.go index 55db3dceb..b2cc64c99 100644 --- a/pkg/bootstrap/builder.go +++ b/pkg/bootstrap/builder.go @@ -81,16 +81,18 @@ func (b *Builder) Build() (*probodconfig.FullConfig, error) { ExtraHeaderFields: make(map[string]string), }, Pg: probodconfig.PgConfig{ - Addr: b.getEnvOrDefault("PG_ADDR", "localhost:5432"), - Username: b.getEnvOrDefault("PG_USERNAME", "probod"), - Password: b.getEnvOrDefault("PG_PASSWORD", "probod"), - Database: b.getEnvOrDefault("PG_DATABASE", "probod"), - PoolSize: int32(b.getEnvIntOrDefault("PG_POOL_SIZE", 100)), - MinPoolSize: int32(b.getEnvIntOrDefault("PG_MIN_POOL_SIZE", 10)), - MaxConnIdleTimeSeconds: b.getEnvIntOrDefault("PG_MAX_CONN_IDLE_TIME_SECONDS", 1800), - MaxConnLifetimeSeconds: b.getEnvIntOrDefault("PG_MAX_CONN_LIFETIME_SECONDS", 3600), - CACertBundle: pgCACertBundle, - Debug: b.getEnvBoolOrDefault("PG_DEBUG", false), + Addr: b.getEnvOrDefault("PG_ADDR", "localhost:5432"), + Username: b.getEnvOrDefault("PG_USERNAME", "probod"), + Password: b.getEnvOrDefault("PG_PASSWORD", "probod"), + Database: b.getEnvOrDefault("PG_DATABASE", "probod"), + PoolSize: int32(b.getEnvIntOrDefault("PG_POOL_SIZE", 100)), + MinPoolSize: int32(b.getEnvIntOrDefault("PG_MIN_POOL_SIZE", 10)), + MaxConnIdleTimeSeconds: b.getEnvIntOrDefault("PG_MAX_CONN_IDLE_TIME_SECONDS", 1800), + MaxConnLifetimeSeconds: b.getEnvIntOrDefault("PG_MAX_CONN_LIFETIME_SECONDS", 3600), + MaxConnLifetimeJitterSeconds: b.getEnvIntOrDefault("PG_MAX_CONN_LIFETIME_JITTER_SECONDS", 300), + HealthCheckPeriodSeconds: b.getEnvIntOrDefault("PG_HEALTH_CHECK_PERIOD_SECONDS", 60), + CACertBundle: pgCACertBundle, + Debug: b.getEnvBoolOrDefault("PG_DEBUG", false), }, Auth: probodconfig.AuthConfig{ DisableSignup: b.getEnvBoolOrDefault("AUTH_DISABLE_SIGNUP", false), diff --git a/pkg/bootstrap/builder_test.go b/pkg/bootstrap/builder_test.go index ef46587f9..2d15356c1 100644 --- a/pkg/bootstrap/builder_test.go +++ b/pkg/bootstrap/builder_test.go @@ -154,6 +154,8 @@ func TestBuilder_Build_Defaults(t *testing.T) { assert.Equal(t, int32(10), cfg.Probod.Pg.MinPoolSize) assert.Equal(t, 1800, cfg.Probod.Pg.MaxConnIdleTimeSeconds) assert.Equal(t, 3600, cfg.Probod.Pg.MaxConnLifetimeSeconds) + assert.Equal(t, 300, cfg.Probod.Pg.MaxConnLifetimeJitterSeconds) + assert.Equal(t, 60, cfg.Probod.Pg.HealthCheckPeriodSeconds) assert.False(t, cfg.Probod.Pg.Debug) // Auth config @@ -254,6 +256,8 @@ func TestBuilder_Build_CustomValues(t *testing.T) { env["PG_MIN_POOL_SIZE"] = "25" env["PG_MAX_CONN_IDLE_TIME_SECONDS"] = "900" env["PG_MAX_CONN_LIFETIME_SECONDS"] = "7200" + env["PG_MAX_CONN_LIFETIME_JITTER_SECONDS"] = "600" + env["PG_HEALTH_CHECK_PERIOD_SECONDS"] = "30" env["PG_DEBUG"] = "true" // Auth env["AUTH_DISABLE_SIGNUP"] = "true" @@ -331,6 +335,8 @@ func TestBuilder_Build_CustomValues(t *testing.T) { assert.Equal(t, int32(25), cfg.Probod.Pg.MinPoolSize) assert.Equal(t, 900, cfg.Probod.Pg.MaxConnIdleTimeSeconds) assert.Equal(t, 7200, cfg.Probod.Pg.MaxConnLifetimeSeconds) + assert.Equal(t, 600, cfg.Probod.Pg.MaxConnLifetimeJitterSeconds) + assert.Equal(t, 30, cfg.Probod.Pg.HealthCheckPeriodSeconds) assert.True(t, cfg.Probod.Pg.Debug) // Auth assert.True(t, cfg.Probod.Auth.DisableSignup) diff --git a/pkg/probod/probod.go b/pkg/probod/probod.go index d8bcf63b2..63074a0fe 100644 --- a/pkg/probod/probod.go +++ b/pkg/probod/probod.go @@ -91,14 +91,16 @@ func New() *Implm { Addr: "localhost:8080", }, Pg: PgConfig{ - Addr: "localhost:5432", - Username: "probod", - Password: "probod", - Database: "probod", - PoolSize: 100, - MinPoolSize: 10, - MaxConnIdleTimeSeconds: 1800, - MaxConnLifetimeSeconds: 3600, + Addr: "localhost:5432", + Username: "probod", + Password: "probod", + Database: "probod", + PoolSize: 100, + MinPoolSize: 10, + MaxConnIdleTimeSeconds: 1800, + MaxConnLifetimeSeconds: 3600, + MaxConnLifetimeJitterSeconds: 300, + HealthCheckPeriodSeconds: 60, }, ChromeDPAddr: "localhost:9222", Auth: AuthConfig{ @@ -209,6 +211,7 @@ func (impl *Implm) Run( pgClient, err := pg.NewClient( impl.cfg.Pg.Options( + pg.WithApplicationName("probod"), pg.WithLogger(l), pg.WithRegisterer(r), pg.WithTracerProvider(tp), diff --git a/pkg/probodconfig/pg_config.go b/pkg/probodconfig/pg_config.go index 0a65f30ff..03719dea7 100644 --- a/pkg/probodconfig/pg_config.go +++ b/pkg/probodconfig/pg_config.go @@ -23,16 +23,18 @@ import ( ) type PgConfig struct { - Addr string `json:"addr"` - Username string `json:"username"` - Password string `json:"password"` - Database string `json:"database"` - PoolSize int32 `json:"pool-size"` - MinPoolSize int32 `json:"min-pool-size"` - MaxConnIdleTimeSeconds int `json:"max-conn-idle-time-seconds"` - MaxConnLifetimeSeconds int `json:"max-conn-lifetime-seconds"` - CACertBundle string `json:"ca-cert-bundle"` - Debug bool `json:"debug"` + Addr string `json:"addr"` + Username string `json:"username"` + Password string `json:"password"` + Database string `json:"database"` + PoolSize int32 `json:"pool-size"` + MinPoolSize int32 `json:"min-pool-size"` + MaxConnIdleTimeSeconds int `json:"max-conn-idle-time-seconds"` + MaxConnLifetimeSeconds int `json:"max-conn-lifetime-seconds"` + MaxConnLifetimeJitterSeconds int `json:"max-conn-lifetime-jitter-seconds"` + HealthCheckPeriodSeconds int `json:"health-check-period-seconds"` + CACertBundle string `json:"ca-cert-bundle"` + Debug bool `json:"debug"` } func (cfg PgConfig) Options(options ...pg.Option) []pg.Option { @@ -66,6 +68,24 @@ func (cfg PgConfig) Options(options ...pg.Option) []pg.Option { ) } + if cfg.MaxConnLifetimeJitterSeconds > 0 { + opts = append( + opts, + pg.WithMaxConnLifetimeJitter( + time.Duration(cfg.MaxConnLifetimeJitterSeconds)*time.Second, + ), + ) + } + + if cfg.HealthCheckPeriodSeconds > 0 { + opts = append( + opts, + pg.WithHealthCheckPeriod( + time.Duration(cfg.HealthCheckPeriodSeconds)*time.Second, + ), + ) + } + if cfg.Debug { opts = append(opts, pg.WithDebug()) }