diff options
author | 2023-01-26 15:12:48 +0100 | |
---|---|---|
committer | 2023-01-26 15:12:48 +0100 | |
commit | 782169da765baf1d651c1b71e5f974f762c92d8e (patch) | |
tree | 073d78698a911af2ac37a2a64bf5ae57406fc38c /internal/db | |
parent | [feature] Public list of suspended domains (#1362) (diff) | |
download | gotosocial-782169da765baf1d651c1b71e5f974f762c92d8e.tar.xz |
[chore] set max open / idle conns + conn max lifetime for both postgres and sqlite (#1369)
* [chore] set max open / idle conns + conn max lifetime for both postgres and sqlite
* reduce cache size default to 8MiB, reduce connections to 2 * cpu
* introduce max open conns multiplier, tune sqlite and pg separately
* go fmt
Diffstat (limited to 'internal/db')
-rw-r--r-- | internal/db/bundb/bundb.go | 167 |
1 files changed, 89 insertions, 78 deletions
diff --git a/internal/db/bundb/bundb.go b/internal/db/bundb/bundb.go index b6a07bdc6..385b0ca1f 100644 --- a/internal/db/bundb/bundb.go +++ b/internal/db/bundb/bundb.go @@ -222,6 +222,32 @@ func NewBunDBService(ctx context.Context, state *state.State) (db.DB, error) { return ps, nil } +func pgConn(ctx context.Context) (*DBConn, error) { + opts, err := deriveBunDBPGOptions() //nolint:contextcheck + if err != nil { + return nil, fmt.Errorf("could not create bundb postgres options: %s", err) + } + + sqldb := stdlib.OpenDB(*opts) + + // Tune db connections for postgres, see: + // - https://bun.uptrace.dev/guide/running-bun-in-production.html#database-sql + // - https://www.alexedwards.net/blog/configuring-sqldb + sqldb.SetMaxOpenConns(maxOpenConns()) // x number of conns per CPU + sqldb.SetMaxIdleConns(2) // assume default 2; if max idle is less than max open, it will be automatically adjusted + sqldb.SetConnMaxLifetime(5 * time.Minute) // fine to kill old connections + + conn := WrapDBConn(bun.NewDB(sqldb, pgdialect.New())) + + // ping to check the db is there and listening + if err := conn.PingContext(ctx); err != nil { + return nil, fmt.Errorf("postgres ping: %s", err) + } + + log.Info("connected to POSTGRES database") + return conn, nil +} + func sqliteConn(ctx context.Context) (*DBConn, error) { // validate db address has actually been set address := config.GetDbAddress() @@ -236,13 +262,10 @@ func sqliteConn(ctx context.Context) (*DBConn, error) { // Append our own SQLite preferences address = "file:" + address - var inMem bool - if address == "file::memory:" { address = fmt.Sprintf("file:%s?mode=memory&cache=shared", uuid.NewString()) log.Infof("using in-memory database address " + address) log.Warn("sqlite in-memory database should only be used for debugging") - inMem = true } // Open new DB instance @@ -254,12 +277,12 @@ func sqliteConn(ctx context.Context) (*DBConn, error) { return nil, fmt.Errorf("could not open sqlite db: %s", err) } - if inMem { - // don't close connections on disconnect -- otherwise - // the SQLite database will be deleted when there - // are no active connections - sqldb.SetConnMaxLifetime(0) - } + // Tune db connections for sqlite, see: + // - https://bun.uptrace.dev/guide/running-bun-in-production.html#database-sql + // - https://www.alexedwards.net/blog/configuring-sqldb + sqldb.SetMaxOpenConns(maxOpenConns()) // x number of conns per cpu + sqldb.SetMaxIdleConns(1) // only keep max 1 idle connection around + sqldb.SetConnMaxLifetime(0) // don't kill connections due to age // Wrap Bun database conn in our own wrapper conn := WrapDBConn(bun.NewDB(sqldb, sqlitedialect.New())) @@ -276,79 +299,20 @@ func sqliteConn(ctx context.Context) (*DBConn, error) { return conn, nil } -func sqlitePragmas(ctx context.Context, conn *DBConn) error { - var pragmas [][]string - if mode := config.GetDbSqliteJournalMode(); mode != "" { - // Set the user provided SQLite journal mode - pragmas = append(pragmas, []string{"journal_mode", mode}) - } - - if mode := config.GetDbSqliteSynchronous(); mode != "" { - // Set the user provided SQLite synchronous mode - pragmas = append(pragmas, []string{"synchronous", mode}) - } - - if size := config.GetDbSqliteCacheSize(); size > 0 { - // Set the user provided SQLite cache size (in kibibytes) - // Prepend a '-' character to this to indicate to sqlite - // that we're giving kibibytes rather than num pages. - // https://www.sqlite.org/pragma.html#pragma_cache_size - s := "-" + strconv.FormatUint(uint64(size/bytesize.KiB), 10) - pragmas = append(pragmas, []string{"cache_size", s}) - } - - if timeout := config.GetDbSqliteBusyTimeout(); timeout > 0 { - t := strconv.FormatInt(timeout.Milliseconds(), 10) - pragmas = append(pragmas, []string{"busy_timeout", t}) - } - - for _, p := range pragmas { - pk := p[0] - pv := p[1] - - if _, err := conn.DB.ExecContext(ctx, "PRAGMA ?=?", bun.Ident(pk), bun.Safe(pv)); err != nil { - return fmt.Errorf("error executing sqlite pragma %s: %w", pk, err) - } - - var res string - if err := conn.DB.NewRaw("PRAGMA ?", bun.Ident(pk)).Scan(ctx, &res); err != nil { - return fmt.Errorf("error scanning sqlite pragma %s: %w", pv, err) - } - - log.Infof("sqlite pragma %s set to %s", pk, res) - } - - return nil -} - -func pgConn(ctx context.Context) (*DBConn, error) { - opts, err := deriveBunDBPGOptions() //nolint:contextcheck - if err != nil { - return nil, fmt.Errorf("could not create bundb postgres options: %s", err) - } - - sqldb := stdlib.OpenDB(*opts) - - // https://bun.uptrace.dev/postgres/running-bun-in-production.html#database-sql - maxOpenConns := 4 * runtime.GOMAXPROCS(0) - sqldb.SetMaxOpenConns(maxOpenConns) - sqldb.SetMaxIdleConns(maxOpenConns) - - conn := WrapDBConn(bun.NewDB(sqldb, pgdialect.New())) - - // ping to check the db is there and listening - if err := conn.PingContext(ctx); err != nil { - return nil, fmt.Errorf("postgres ping: %s", err) - } - - log.Info("connected to POSTGRES database") - return conn, nil -} - /* HANDY STUFF */ +// maxOpenConns returns multiplier * GOMAXPROCS, +// clamping multiplier to 1 if it was below 1. +func maxOpenConns() int { + multiplier := config.GetDbMaxOpenConnsMultiplier() + if multiplier < 1 { + multiplier = 1 + } + return multiplier * runtime.GOMAXPROCS(0) +} + // deriveBunDBPGOptions takes an application config and returns either a ready-to-use set of options // with sensible defaults, or an error if it's not satisfied by the provided config. func deriveBunDBPGOptions() (*pgx.ConnConfig, error) { @@ -434,6 +398,53 @@ func deriveBunDBPGOptions() (*pgx.ConnConfig, error) { return cfg, nil } +// sqlitePragmas sets desired sqlite pragmas based on configured values, and +// logs the results of the pragma queries. Errors if something goes wrong. +func sqlitePragmas(ctx context.Context, conn *DBConn) error { + var pragmas [][]string + if mode := config.GetDbSqliteJournalMode(); mode != "" { + // Set the user provided SQLite journal mode + pragmas = append(pragmas, []string{"journal_mode", mode}) + } + + if mode := config.GetDbSqliteSynchronous(); mode != "" { + // Set the user provided SQLite synchronous mode + pragmas = append(pragmas, []string{"synchronous", mode}) + } + + if size := config.GetDbSqliteCacheSize(); size > 0 { + // Set the user provided SQLite cache size (in kibibytes) + // Prepend a '-' character to this to indicate to sqlite + // that we're giving kibibytes rather than num pages. + // https://www.sqlite.org/pragma.html#pragma_cache_size + s := "-" + strconv.FormatUint(uint64(size/bytesize.KiB), 10) + pragmas = append(pragmas, []string{"cache_size", s}) + } + + if timeout := config.GetDbSqliteBusyTimeout(); timeout > 0 { + t := strconv.FormatInt(timeout.Milliseconds(), 10) + pragmas = append(pragmas, []string{"busy_timeout", t}) + } + + for _, p := range pragmas { + pk := p[0] + pv := p[1] + + if _, err := conn.DB.ExecContext(ctx, "PRAGMA ?=?", bun.Ident(pk), bun.Safe(pv)); err != nil { + return fmt.Errorf("error executing sqlite pragma %s: %w", pk, err) + } + + var res string + if err := conn.DB.NewRaw("PRAGMA ?", bun.Ident(pk)).Scan(ctx, &res); err != nil { + return fmt.Errorf("error scanning sqlite pragma %s: %w", pv, err) + } + + log.Infof("sqlite pragma %s set to %s", pk, res) + } + + return nil +} + /* CONVERSION FUNCTIONS */ |