Skip to content

Commit

Permalink
Merge pull request #300 from pieceofsoul/add-variadic-options-to-post…
Browse files Browse the repository at this point in the history
…gres

Added variadic ConfigOptions to postgres ConnectionPool function
  • Loading branch information
Ferlonas committed Mar 9, 2022
2 parents e640e1e + d2711c7 commit 74a4ee5
Show file tree
Hide file tree
Showing 3 changed files with 354 additions and 1 deletion.
170 changes: 170 additions & 0 deletions backend/postgres/options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
// Copyright © 2022 by PACE Telematics GmbH. All rights reserved.
// Created at 2022/03/09 by Sascha Voth

package postgres

import (
"time"
)

type ConfigOption func(cfg *Config)

// WithPort - customize the db port
func WithPort(port int) ConfigOption {
return func(cfg *Config) {
cfg.Port = port
}
}

// WithHost - customise the db host
func WithHost(host string) ConfigOption {
return func(cfg *Config) {
cfg.Host = host
}
}

// WithPassword - customise the db password
func WithPassword(password string) ConfigOption {
return func(cfg *Config) {
cfg.Password = password
}
}

// WithUser - customise the db user
func WithUser(user string) ConfigOption {
return func(cfg *Config) {
cfg.User = user
}
}

// WithDatabase - customise the db name
func WithDatabase(database string) ConfigOption {
return func(cfg *Config) {
cfg.Database = database
}
}

// WithApplicationName -ApplicationName is the application name. Used in logs on Pg side.
// Only available from pg-9.0.
func WithApplicationName(applicationName string) ConfigOption {
return func(cfg *Config) {
cfg.ApplicationName = applicationName
}
}

// WithMaxRetries - Maximum number of retries before giving up.
func WithMaxRetries(maxRetries int) ConfigOption {
return func(cfg *Config) {
cfg.MaxRetries = maxRetries
}
}

// WithRetryStatementTimeout - Whether to retry queries cancelled because of statement_timeout.
func WithRetryStatementTimeout(retryStatementTimeout bool) ConfigOption {
return func(cfg *Config) {
cfg.RetryStatementTimeout = retryStatementTimeout
}
}

// WithMinRetryBackoff - Minimum backoff between each retry.
// -1 disables backoff.
func WithMinRetryBackoff(minRetryBackoff time.Duration) ConfigOption {
return func(cfg *Config) {
cfg.MinRetryBackoff = minRetryBackoff
}
}

// WithMaxRetryBackoff - Maximum backoff between each retry.
// -1 disables backoff.
func WithMaxRetryBackoff(maxRetryBackoff time.Duration) ConfigOption {
return func(cfg *Config) {
cfg.MaxRetryBackoff = maxRetryBackoff
}
}

// WithDialTimeout - Dial timeout for establishing new connections.
func WithDialTimeout(dialTimeout time.Duration) ConfigOption {
return func(cfg *Config) {
cfg.DialTimeout = dialTimeout
}
}

// WithReadTimeout - Timeout for socket reads. If reached, commands will fail
// with a timeout instead of blocking.
func WithReadTimeout(readTimeout time.Duration) ConfigOption {
return func(cfg *Config) {
cfg.ReadTimeout = readTimeout
}
}

// WithWriteTimeout - Timeout for socket writes. If reached, commands will fail
// with a timeout instead of blocking.
func WithWriteTimeout(writeTimeout time.Duration) ConfigOption {
return func(cfg *Config) {
cfg.WriteTimeout = writeTimeout
}
}

// WithPoolSize - Maximum number of socket connections.
func WithPoolSize(poolSize int) ConfigOption {
return func(cfg *Config) {
cfg.PoolSize = poolSize
}
}

// WithMinIdleConns - Minimum number of idle connections which is useful when establishing
// new connection is slow.
func WithMinIdleConns(minIdleConns int) ConfigOption {
return func(cfg *Config) {
cfg.MinIdleConns = minIdleConns
}
}

// WithMaxConnAge - Connection age at which client retires (closes) the connection.
// It is useful with proxies like PgBouncer and HAProxy.
func WithMaxConnAge(maxConnAge time.Duration) ConfigOption {
return func(cfg *Config) {
cfg.MaxConnAge = maxConnAge
}
}

// WithPoolTimeout - Time for which client waits for free connection if all
// connections are busy before returning an error.
func WithPoolTimeout(poolTimeout time.Duration) ConfigOption {
return func(cfg *Config) {
cfg.PoolTimeout = poolTimeout
}
}

// WithIdleTimeout - Amount of time after which client closes idle connections.
// Should be less than server's timeout.
// -1 disables idle timeout check.
func WithIdleTimeout(idleTimeout time.Duration) ConfigOption {
return func(cfg *Config) {
cfg.IdleTimeout = idleTimeout
}
}

// WithIdleCheckFrequency - Frequency of idle checks made by idle connection's reaper.
// -1 disables idle connection's reaper,
// but idle connections are still discarded by the client
// if IdleTimeout is set.
func WithIdleCheckFrequency(idleCheckFrequency time.Duration) ConfigOption {
return func(cfg *Config) {
cfg.IdleCheckFrequency = idleCheckFrequency
}
}

// WithHealthCheckTableName - Name of the Table that is created to try if database is writeable
func WithHealthCheckTableName(healthCheckTableName string) ConfigOption {
return func(cfg *Config) {
cfg.HealthCheckTableName = healthCheckTableName
}
}

// WithHealthCheckResultTTL - Amount of time to cache the last health check result
func WithHealthCheckResultTTL(healthCheckResultTTL time.Duration) ConfigOption {
return func(cfg *Config) {
cfg.HealthCheckResultTTL = healthCheckResultTTL
}
}
175 changes: 175 additions & 0 deletions backend/postgres/options_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
package postgres

import (
"github.com/stretchr/testify/require"
"testing"
"time"
)

func TestWithApplicationName(t *testing.T) {
param := "ApplicationName"
var conf Config
f := WithApplicationName(param)
f(&conf)
require.Equal(t, conf.ApplicationName, param)
}

func TestWithDatabase(t *testing.T) {
param := "Database"
var conf Config
f := WithDatabase(param)
f(&conf)
require.Equal(t, conf.Database, param)
}

func TestWithDialTimeout(t *testing.T) {
param := 5 * time.Second
var conf Config
f := WithDialTimeout(param)
f(&conf)
require.Equal(t, conf.DialTimeout, param)
}

func TestWithHealthCheckResultTTL(t *testing.T) {
param := 5 * time.Second
var conf Config
f := WithHealthCheckResultTTL(param)
f(&conf)
require.Equal(t, conf.HealthCheckResultTTL, param)
}

func TestWithHealthCheckTableName(t *testing.T) {
param := "HealthCheckTableName"
var conf Config
f := WithHealthCheckTableName(param)
f(&conf)
require.Equal(t, conf.HealthCheckTableName, param)
}

func TestWithHost(t *testing.T) {
param := "Host"
var conf Config
f := WithHost(param)
f(&conf)
require.Equal(t, conf.Host, param)
}

func TestWithIdleCheckFrequency(t *testing.T) {
param := 5 * time.Second
var conf Config
f := WithIdleCheckFrequency(param)
f(&conf)
require.Equal(t, conf.IdleCheckFrequency, param)
}

func TestWithIdleTimeout(t *testing.T) {
param := 5 * time.Second
var conf Config
f := WithIdleTimeout(param)
f(&conf)
require.Equal(t, conf.IdleTimeout, param)
}

func TestWithMaxConnAge(t *testing.T) {
param := 5 * time.Second
var conf Config
f := WithMaxConnAge(param)
f(&conf)
require.Equal(t, conf.MaxConnAge, param)
}

func TestWithMaxRetries(t *testing.T) {
param := 42
var conf Config
f := WithMaxRetries(param)
f(&conf)
require.Equal(t, conf.MaxRetries, param)
}

func TestWithMaxRetryBackoff(t *testing.T) {
param := 5 * time.Second
var conf Config
f := WithMaxRetryBackoff(param)
f(&conf)
require.Equal(t, conf.MaxRetryBackoff, param)
}

func TestWithMinIdleConns(t *testing.T) {
param := 42
var conf Config
f := WithMinIdleConns(param)
f(&conf)
require.Equal(t, conf.MinIdleConns, param)
}

func TestWithMinRetryBackoff(t *testing.T) {
param := 5 * time.Second
var conf Config
f := WithMinRetryBackoff(param)
f(&conf)
require.Equal(t, conf.MinRetryBackoff, param)
}

func TestWithPassword(t *testing.T) {
param := "Password"
var conf Config
f := WithPassword(param)
f(&conf)
require.Equal(t, conf.Password, param)
}

func TestWithPoolSize(t *testing.T) {
param := 42
var conf Config
f := WithPoolSize(param)
f(&conf)
require.Equal(t, conf.PoolSize, param)
}

func TestWithPoolTimeout(t *testing.T) {
param := 5 * time.Second
var conf Config
f := WithPoolTimeout(param)
f(&conf)
require.Equal(t, conf.PoolTimeout, param)
}

func TestWithPort(t *testing.T) {
param := 42
var conf Config
f := WithPort(param)
f(&conf)
require.Equal(t, conf.Port, param)
}

func TestWithReadTimeout(t *testing.T) {
param := 5 * time.Second
var conf Config
f := WithReadTimeout(param)
f(&conf)
require.Equal(t, conf.ReadTimeout, param)
}

func TestWithRetryStatementTimeout(t *testing.T) {
param := true
var conf Config
f := WithRetryStatementTimeout(param)
f(&conf)
require.Equal(t, conf.RetryStatementTimeout, param)
}

func TestWithUser(t *testing.T) {
param := "User"
var conf Config
f := WithUser(param)
f(&conf)
require.Equal(t, conf.User, param)
}

func TestWithWriteTimeout(t *testing.T) {
param := 5 * time.Second
var conf Config
f := WithWriteTimeout(param)
f(&conf)
require.Equal(t, conf.WriteTimeout, param)
}
10 changes: 9 additions & 1 deletion backend/postgres/postgres.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,15 @@ func DefaultConnectionPool() *pg.DB {
// ConnectionPool returns a new database connection pool
// that is already configured with the correct credentials and
// instrumented with tracing and logging
func ConnectionPool() *pg.DB {
// Used Config is taken from the env and it's default values. These
// values can be overwritten by the use of ConfigOption.
func ConnectionPool(opts ...ConfigOption) *pg.DB {

// apply functional options if given to overwrite the default config / env config
for _, f := range opts {
f(&cfg)
}

return CustomConnectionPool(&pg.Options{
Addr: fmt.Sprintf("%s:%d", cfg.Host, cfg.Port),
User: cfg.User,
Expand Down

0 comments on commit 74a4ee5

Please sign in to comment.