From eff033621f5ae4873cca4e6c60d67dc6f205f00c Mon Sep 17 00:00:00 2001 From: Stuart Douglas Date: Mon, 23 Sep 2024 12:57:28 +1000 Subject: [PATCH] feat: DB connection pool sizing (#2779) --- backend/controller/controller.go | 13 +++++++++++++ backend/controller/sql/databasetesting/devel.go | 2 ++ cmd/ftl-controller/main.go | 2 +- frontend/cli/cmd_box_run.go | 3 +-- frontend/cli/cmd_serve.go | 2 +- 5 files changed, 18 insertions(+), 4 deletions(-) diff --git a/backend/controller/controller.go b/backend/controller/controller.go index 04d2386f1d..d2218e80a9 100644 --- a/backend/controller/controller.go +++ b/backend/controller/controller.go @@ -61,6 +61,7 @@ import ( ftlmaps "github.com/TBD54566975/ftl/internal/maps" "github.com/TBD54566975/ftl/internal/model" "github.com/TBD54566975/ftl/internal/modulecontext" + internalobservability "github.com/TBD54566975/ftl/internal/observability" ftlreflect "github.com/TBD54566975/ftl/internal/reflect" "github.com/TBD54566975/ftl/internal/rpc" "github.com/TBD54566975/ftl/internal/rpc/headers" @@ -100,6 +101,8 @@ type Config struct { EventLogRetention *time.Duration `help:"Delete call logs after this time period. 0 to disable" env:"FTL_EVENT_LOG_RETENTION" default:"24h"` ArtefactChunkSize int `help:"Size of each chunk streamed to the client." default:"1048576"` KMSURI *string `help:"URI for KMS key e.g. with fake-kms:// or aws-kms://arn:aws:kms:ap-southeast-2:12345:key/0000-1111" env:"FTL_KMS_URI"` + MaxOpenDBConnections int `help:"Maximum number of database connections." default:"20" env:"FTL_MAX_OPEN_DB_CONNECTIONS"` + MaxIdleDBConnections int `help:"Maximum number of idle database connections." default:"20" env:"FTL_MAX_IDLE_DB_CONNECTIONS"` CommonConfig } @@ -112,6 +115,16 @@ func (c *Config) SetDefaults() { } } +func (c *Config) OpenDBAndInstrument() (*sql.DB, error) { + conn, err := internalobservability.OpenDBAndInstrument(c.DSN) + if err != nil { + return nil, fmt.Errorf("failed to open DB connection: %w", err) + } + conn.SetMaxIdleConns(c.MaxIdleDBConnections) + conn.SetMaxOpenConns(c.MaxOpenDBConnections) + return conn, nil +} + // Start the Controller. Blocks until the context is cancelled. func Start(ctx context.Context, config Config, runnerScaling scaling.RunnerScaling, conn *sql.DB, devel bool) error { config.SetDefaults() diff --git a/backend/controller/sql/databasetesting/devel.go b/backend/controller/sql/databasetesting/devel.go index 3d34a5dec8..3918d2e886 100644 --- a/backend/controller/sql/databasetesting/devel.go +++ b/backend/controller/sql/databasetesting/devel.go @@ -77,6 +77,8 @@ func CreateForDevel(ctx context.Context, dsn string, recreate bool) (*stdsql.DB, if err != nil { return nil, fmt.Errorf("failed to open database: %w", err) } + realConn.SetMaxIdleConns(20) + realConn.SetMaxOpenConns(20) // Reset transient state in the database to a clean state for development purposes. // This includes things like resetting the state of async calls, leases, // controller/runner registration, etc. but not anything more. diff --git a/cmd/ftl-controller/main.go b/cmd/ftl-controller/main.go index e1ae5d498a..82a0e57ac8 100644 --- a/cmd/ftl-controller/main.go +++ b/cmd/ftl-controller/main.go @@ -50,7 +50,7 @@ func main() { kctx.FatalIfErrorf(err, "failed to initialize observability") // The FTL controller currently only supports DB as a cf provider/resolver. - conn, err := observability.OpenDBAndInstrument(cli.ControllerConfig.DSN) + conn, err := cli.ControllerConfig.OpenDBAndInstrument() kctx.FatalIfErrorf(err) dal := dbleaser.NewDatabaseLeaser(conn) diff --git a/frontend/cli/cmd_box_run.go b/frontend/cli/cmd_box_run.go index edbf6c84f5..9c628f6c5a 100644 --- a/frontend/cli/cmd_box_run.go +++ b/frontend/cli/cmd_box_run.go @@ -19,7 +19,6 @@ import ( "github.com/TBD54566975/ftl/internal/buildengine" "github.com/TBD54566975/ftl/internal/log" "github.com/TBD54566975/ftl/internal/model" - "github.com/TBD54566975/ftl/internal/observability" "github.com/TBD54566975/ftl/internal/projectconfig" "github.com/TBD54566975/ftl/internal/rpc" ) @@ -58,7 +57,7 @@ func (b *boxRunCmd) Run(ctx context.Context, projConfig projectconfig.Config) er } // Bring up the DB connection and DAL. - conn, err := observability.OpenDBAndInstrument(config.DSN) + conn, err := config.OpenDBAndInstrument() if err != nil { return fmt.Errorf("failed to bring up DB connection: %w", err) } diff --git a/frontend/cli/cmd_serve.go b/frontend/cli/cmd_serve.go index fea7559c7e..6a5965d933 100644 --- a/frontend/cli/cmd_serve.go +++ b/frontend/cli/cmd_serve.go @@ -148,7 +148,7 @@ func (s *serveCmd) run(ctx context.Context, projConfig projectconfig.Config, ini controllerCtx = manager.ContextWithSecrets(controllerCtx, sm) // Bring up the DB connection and DAL. - conn, err := observability.OpenDBAndInstrument(config.DSN) + conn, err := config.OpenDBAndInstrument() if err != nil { return fmt.Errorf("failed to bring up DB connection: %w", err) }