From 2fe1b0961daa6d2106dce9a45d453c0a3433e10a Mon Sep 17 00:00:00 2001 From: sayaoailun Date: Sun, 3 Apr 2022 20:30:46 +0800 Subject: [PATCH] add parameters for PostgreSQL Signed-off-by: sayaoailun --- make/harbor.yml.tmpl | 6 +++ make/photon/prepare/templates/core/env.jinja | 2 + .../prepare/templates/exporter/env.jinja | 4 +- make/photon/prepare/utils/configs.py | 4 ++ src/cmd/exporter/main.go | 27 ++++++++---- src/common/const.go | 2 + src/common/dao/base.go | 2 + src/common/dao/pgsql.go | 42 +++++++++++-------- src/common/models/database.go | 20 +++++---- src/lib/config/metadata/metadatalist.go | 2 + src/lib/config/metadata/type.go | 14 +++++++ src/lib/config/metadata/value.go | 17 ++++++++ src/lib/config/systemconfig.go | 18 ++++---- src/pkg/config/manager.go | 18 ++++---- 14 files changed, 128 insertions(+), 50 deletions(-) diff --git a/make/harbor.yml.tmpl b/make/harbor.yml.tmpl index 2ad41ace7d46..7c953b9c61a9 100644 --- a/make/harbor.yml.tmpl +++ b/make/harbor.yml.tmpl @@ -42,6 +42,12 @@ database: # The maximum number of open connections to the database. If it <= 0, then there is no limit on the number of open connections. # Note: the default number of connections is 1024 for postgres of harbor. max_open_conns: 900 + # The maximum amount of time a connection may be reused. Expired connections may be closed lazily before reuse. If it <= 0, connections are not closed due to a connection's age. + # The value is a duration string. A duration string is a possibly signed sequence of decimal numbers, each with optional fraction and a unit suffix, such as "300ms", "-1.5h" or "2h45m". Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". + conn_max_lifetime: 5m + # The maximum amount of time a connection may be idle. Expired connections may be closed lazily before reuse. If it <= 0, connections are not closed due to a connection's idle time. + # The value is a duration string. A duration string is a possibly signed sequence of decimal numbers, each with optional fraction and a unit suffix, such as "300ms", "-1.5h" or "2h45m". Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". + conn_max_idle_time: 0 # The default data volume data_volume: /data diff --git a/make/photon/prepare/templates/core/env.jinja b/make/photon/prepare/templates/core/env.jinja index 954860942ee6..5ea5d3734bf9 100644 --- a/make/photon/prepare/templates/core/env.jinja +++ b/make/photon/prepare/templates/core/env.jinja @@ -16,6 +16,8 @@ POSTGRESQL_DATABASE={{harbor_db_name}} POSTGRESQL_SSLMODE={{harbor_db_sslmode}} POSTGRESQL_MAX_IDLE_CONNS={{harbor_db_max_idle_conns}} POSTGRESQL_MAX_OPEN_CONNS={{harbor_db_max_open_conns}} +POSTGRESQL_CONN_MAX_LIFETIME={{harbor_db_conn_max_lifetime}} +POSTGRESQL_CONN_MAX_IDLE_TIME={{harbor_db_conn_max_idle_time}} REGISTRY_URL={{registry_url}} PORTAL_URL={{portal_url}} TOKEN_SERVICE_URL={{token_service_url}} diff --git a/make/photon/prepare/templates/exporter/env.jinja b/make/photon/prepare/templates/exporter/env.jinja index d9e557c3ad8f..1a05abbf3db8 100644 --- a/make/photon/prepare/templates/exporter/env.jinja +++ b/make/photon/prepare/templates/exporter/env.jinja @@ -25,4 +25,6 @@ HARBOR_DATABASE_PASSWORD={{harbor_db_password}} HARBOR_DATABASE_DBNAME={{harbor_db_name}} HARBOR_DATABASE_SSLMODE={{harbor_db_sslmode}} HARBOR_DATABASE_MAX_IDLE_CONNS={{harbor_db_max_idle_conns}} -HARBOR_DATABASE_MAX_OPEN_CONNS={{harbor_db_max_open_conns}} \ No newline at end of file +HARBOR_DATABASE_MAX_OPEN_CONNS={{harbor_db_max_open_conns}} +HARBOR_DATABASE_CONN_MAX_LIFETIME={{harbor_db_conn_max_lifetime}} +HARBOR_DATABASE_CONN_MAX_IDLE_TIME={{harbor_db_conn_max_idle_time}} diff --git a/make/photon/prepare/utils/configs.py b/make/photon/prepare/utils/configs.py index 64498db18f02..a951b95f8cc2 100644 --- a/make/photon/prepare/utils/configs.py +++ b/make/photon/prepare/utils/configs.py @@ -158,6 +158,8 @@ def parse_yaml_config(config_file_path, with_notary, with_trivy, with_chartmuseu config_dict['harbor_db_sslmode'] = 'disable' config_dict['harbor_db_max_idle_conns'] = db_configs.get("max_idle_conns") or default_db_max_idle_conns config_dict['harbor_db_max_open_conns'] = db_configs.get("max_open_conns") or default_db_max_open_conns + config_dict['harbor_db_conn_max_lifetime'] = db_configs.get("conn_max_lifetime") or '5m' + config_dict['harbor_db_conn_max_idle_time'] = db_configs.get("conn_max_idle_time") or '0' if with_notary: # notary signer @@ -288,6 +290,8 @@ def parse_yaml_config(config_file_path, with_notary, with_trivy, with_chartmuseu config_dict['harbor_db_sslmode'] = external_db_configs['harbor']['ssl_mode'] config_dict['harbor_db_max_idle_conns'] = external_db_configs['harbor'].get("max_idle_conns") or default_db_max_idle_conns config_dict['harbor_db_max_open_conns'] = external_db_configs['harbor'].get("max_open_conns") or default_db_max_open_conns + config_dict['harbor_db_conn_max_lifetime'] = external_db_configs['harbor'].get("conn_max_lifetime") or '5m' + config_dict['harbor_db_conn_max_idle_time'] = external_db_configs['harbor'].get("conn_max_idle_time") or '0' if with_notary: # notary signer diff --git a/src/cmd/exporter/main.go b/src/cmd/exporter/main.go index 536636c52191..14249c1f8822 100644 --- a/src/cmd/exporter/main.go +++ b/src/cmd/exporter/main.go @@ -4,6 +4,7 @@ import ( "net/http" "os" "strings" + "time" _ "github.com/jackc/pgx/v4/stdlib" // registry pgx driver "github.com/prometheus/client_golang/prometheus" @@ -21,17 +22,27 @@ func main() { viper.SetEnvPrefix("harbor") viper.AutomaticEnv() viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) + connMaxLifetime, err := time.ParseDuration(viper.GetString("database.conn_max_lifetime")) + if err != nil { + connMaxLifetime = 0 + } + connMaxIdleTime, err := time.ParseDuration(viper.GetString("database.conn_max_idle_time")) + if err != nil { + connMaxIdleTime = 0 + } dbCfg := &models.Database{ Type: "postgresql", PostGreSQL: &models.PostGreSQL{ - Host: viper.GetString("database.host"), - Port: viper.GetInt("database.port"), - Username: viper.GetString("database.username"), - Password: viper.GetString("database.password"), - Database: viper.GetString("database.dbname"), - SSLMode: viper.GetString("database.sslmode"), - MaxIdleConns: viper.GetInt("database.max_idle_conns"), - MaxOpenConns: viper.GetInt("database.max_open_conns"), + Host: viper.GetString("database.host"), + Port: viper.GetInt("database.port"), + Username: viper.GetString("database.username"), + Password: viper.GetString("database.password"), + Database: viper.GetString("database.dbname"), + SSLMode: viper.GetString("database.sslmode"), + MaxIdleConns: viper.GetInt("database.max_idle_conns"), + MaxOpenConns: viper.GetInt("database.max_open_conns"), + ConnMaxLifetime: connMaxLifetime, + ConnMaxIdleTime: connMaxIdleTime, }, } if err := dao.InitDatabase(dbCfg); err != nil { diff --git a/src/common/const.go b/src/common/const.go index dd0ce0889122..07c127cb3ee1 100755 --- a/src/common/const.go +++ b/src/common/const.go @@ -59,6 +59,8 @@ const ( PostGreSQLSSLMode = "postgresql_sslmode" PostGreSQLMaxIdleConns = "postgresql_max_idle_conns" PostGreSQLMaxOpenConns = "postgresql_max_open_conns" + PostGreSQLConnMaxLifetime = "postgresql_conn_max_lifetime" + PostGreSQLConnMaxIdleTime = "postgresql_conn_max_idle_time" SelfRegistration = "self_registration" CoreURL = "core_url" CoreLocalURL = "core_local_url" diff --git a/src/common/dao/base.go b/src/common/dao/base.go index cf9bba28272f..96c854ff1616 100644 --- a/src/common/dao/base.go +++ b/src/common/dao/base.go @@ -85,6 +85,8 @@ func getDatabase(database *models.Database) (db Database, err error) { database.PostGreSQL.SSLMode, database.PostGreSQL.MaxIdleConns, database.PostGreSQL.MaxOpenConns, + database.PostGreSQL.ConnMaxLifetime, + database.PostGreSQL.ConnMaxIdleTime, ) default: err = fmt.Errorf("invalid database: %s", database.Type) diff --git a/src/common/dao/pgsql.go b/src/common/dao/pgsql.go index 665e8c2e9e31..270d8913f246 100644 --- a/src/common/dao/pgsql.go +++ b/src/common/dao/pgsql.go @@ -36,14 +36,16 @@ import ( const defaultMigrationPath = "migrations/postgresql/" type pgsql struct { - host string - port string - usr string - pwd string - database string - sslmode string - maxIdleConns int - maxOpenConns int + host string + port string + usr string + pwd string + database string + sslmode string + maxIdleConns int + maxOpenConns int + connMaxLifetime time.Duration + connMaxIdleTime time.Duration } // Name returns the name of PostgreSQL @@ -58,19 +60,21 @@ func (p *pgsql) String() string { } // NewPGSQL returns an instance of postgres -func NewPGSQL(host string, port string, usr string, pwd string, database string, sslmode string, maxIdleConns int, maxOpenConns int) Database { +func NewPGSQL(host string, port string, usr string, pwd string, database string, sslmode string, maxIdleConns int, maxOpenConns int, connMaxLifetime time.Duration, connMaxIdleTime time.Duration) Database { if len(sslmode) == 0 { sslmode = "disable" } return &pgsql{ - host: host, - port: port, - usr: usr, - pwd: pwd, - database: database, - sslmode: sslmode, - maxIdleConns: maxIdleConns, - maxOpenConns: maxOpenConns, + host: host, + port: port, + usr: usr, + pwd: pwd, + database: database, + sslmode: sslmode, + maxIdleConns: maxIdleConns, + maxOpenConns: maxOpenConns, + connMaxLifetime: connMaxLifetime, + connMaxIdleTime: connMaxIdleTime, } } @@ -96,6 +100,10 @@ func (p *pgsql) Register(alias ...string) error { return err } + db, _ := orm.GetDB(an) + db.SetConnMaxLifetime(p.connMaxLifetime) + db.SetConnMaxIdleTime(p.connMaxIdleTime) + return nil } diff --git a/src/common/models/database.go b/src/common/models/database.go index 3fb031240919..3331cd9de7e7 100644 --- a/src/common/models/database.go +++ b/src/common/models/database.go @@ -14,6 +14,8 @@ package models +import "time" + // Database ... type Database struct { Type string `json:"type"` @@ -36,12 +38,14 @@ type SQLite struct { // PostGreSQL ... type PostGreSQL struct { - Host string `json:"host"` - Port int `json:"port"` - Username string `json:"username"` - Password string `json:"password,omitempty"` - Database string `json:"database"` - SSLMode string `json:"sslmode"` - MaxIdleConns int `json:"max_idle_conns"` - MaxOpenConns int `json:"max_open_conns"` + Host string `json:"host"` + Port int `json:"port"` + Username string `json:"username"` + Password string `json:"password,omitempty"` + Database string `json:"database"` + SSLMode string `json:"sslmode"` + MaxIdleConns int `json:"max_idle_conns"` + MaxOpenConns int `json:"max_open_conns"` + ConnMaxLifetime time.Duration `json:"conn_max_lifetime"` + ConnMaxIdleTime time.Duration `json:"conn_max_idle_time"` } diff --git a/src/lib/config/metadata/metadatalist.go b/src/lib/config/metadata/metadatalist.go index 601abd8abe3d..1762045b5053 100644 --- a/src/lib/config/metadata/metadatalist.go +++ b/src/lib/config/metadata/metadatalist.go @@ -104,6 +104,8 @@ var ( {Name: common.PostGreSQLUsername, Scope: SystemScope, Group: DatabaseGroup, EnvKey: "POSTGRESQL_USERNAME", DefaultValue: "postgres", ItemType: &StringType{}, Editable: false}, {Name: common.PostGreSQLMaxIdleConns, Scope: SystemScope, Group: DatabaseGroup, EnvKey: "POSTGRESQL_MAX_IDLE_CONNS", DefaultValue: "2", ItemType: &IntType{}, Editable: false}, {Name: common.PostGreSQLMaxOpenConns, Scope: SystemScope, Group: DatabaseGroup, EnvKey: "POSTGRESQL_MAX_OPEN_CONNS", DefaultValue: "0", ItemType: &IntType{}, Editable: false}, + {Name: common.PostGreSQLConnMaxLifetime, Scope: SystemScope, Group: DatabaseGroup, EnvKey: "POSTGRESQL_CONN_MAX_LIFETIME", DefaultValue: "0", ItemType: &DurationType{}, Editable: false}, + {Name: common.PostGreSQLConnMaxIdleTime, Scope: SystemScope, Group: DatabaseGroup, EnvKey: "POSTGRESQL_CONN_MAX_IDLE_TIME", DefaultValue: "0", ItemType: &DurationType{}, Editable: false}, {Name: common.ProjectCreationRestriction, Scope: UserScope, Group: BasicGroup, EnvKey: "PROJECT_CREATION_RESTRICTION", DefaultValue: common.ProCrtRestrEveryone, ItemType: &ProjectCreationRestrictionType{}, Editable: false, Description: `Indicate who can create projects, it could be ''adminonly'' or ''everyone''.`}, {Name: common.ReadOnly, Scope: UserScope, Group: BasicGroup, EnvKey: "READ_ONLY", DefaultValue: "false", ItemType: &BoolType{}, Editable: false, Description: `The flag to indicate whether Harbor is in readonly mode.`}, diff --git a/src/lib/config/metadata/type.go b/src/lib/config/metadata/type.go index b5a7dac641f6..264d60fb6cc8 100644 --- a/src/lib/config/metadata/type.go +++ b/src/lib/config/metadata/type.go @@ -21,6 +21,7 @@ import ( "math" "strconv" "strings" + "time" "github.com/goharbor/harbor/src/common" ) @@ -234,6 +235,19 @@ func (t *QuotaType) validate(str string) error { return nil } +// DurationType ... +type DurationType struct { +} + +func (t *DurationType) validate(str string) error { + _, err := time.ParseDuration(str) + return err +} + +func (t *DurationType) get(str string) (interface{}, error) { + return time.ParseDuration(str) +} + // parseInt64 returns int64 from string which support scientific notation func parseInt64(str string) (int64, error) { val, err := strconv.ParseInt(str, 10, 64) diff --git a/src/lib/config/metadata/value.go b/src/lib/config/metadata/value.go index ad9d738aa9fe..6f3b17c12dd2 100644 --- a/src/lib/config/metadata/value.go +++ b/src/lib/config/metadata/value.go @@ -16,6 +16,7 @@ package metadata import ( "errors" + "time" "github.com/goharbor/harbor/src/lib/log" ) @@ -144,6 +145,22 @@ func (c *ConfigureValue) GetStringToStringMap() map[string]string { return result } +// GetDuration - return the time.Duration value of current value +func (c *ConfigureValue) GetDuration() time.Duration { + if item, ok := Instance().GetByName(c.Name); ok { + val, err := item.ItemType.get(c.Value) + if err != nil { + log.Errorf("GetDuration failed, error: %+v", err) + return 0 + } + if durationValue, suc := val.(time.Duration); suc { + return durationValue + } + } + log.Errorf("GetDuration failed, the current value's metadata is not defined, %+v", c) + return 0 +} + // GetAnyType get the interface{} of current value func (c *ConfigureValue) GetAnyType() (interface{}, error) { if item, ok := Instance().GetByName(c.Name); ok { diff --git a/src/lib/config/systemconfig.go b/src/lib/config/systemconfig.go index b8d8eb7206a2..b6dda1826780 100644 --- a/src/lib/config/systemconfig.go +++ b/src/lib/config/systemconfig.go @@ -274,14 +274,16 @@ func Database() (*models.Database, error) { database := &models.Database{} database.Type = DefaultMgr().Get(backgroundCtx, common.DatabaseType).GetString() postgresql := &models.PostGreSQL{ - Host: DefaultMgr().Get(backgroundCtx, common.PostGreSQLHOST).GetString(), - Port: DefaultMgr().Get(backgroundCtx, common.PostGreSQLPort).GetInt(), - Username: DefaultMgr().Get(backgroundCtx, common.PostGreSQLUsername).GetString(), - Password: DefaultMgr().Get(backgroundCtx, common.PostGreSQLPassword).GetPassword(), - Database: DefaultMgr().Get(backgroundCtx, common.PostGreSQLDatabase).GetString(), - SSLMode: DefaultMgr().Get(backgroundCtx, common.PostGreSQLSSLMode).GetString(), - MaxIdleConns: DefaultMgr().Get(backgroundCtx, common.PostGreSQLMaxIdleConns).GetInt(), - MaxOpenConns: DefaultMgr().Get(backgroundCtx, common.PostGreSQLMaxOpenConns).GetInt(), + Host: DefaultMgr().Get(backgroundCtx, common.PostGreSQLHOST).GetString(), + Port: DefaultMgr().Get(backgroundCtx, common.PostGreSQLPort).GetInt(), + Username: DefaultMgr().Get(backgroundCtx, common.PostGreSQLUsername).GetString(), + Password: DefaultMgr().Get(backgroundCtx, common.PostGreSQLPassword).GetPassword(), + Database: DefaultMgr().Get(backgroundCtx, common.PostGreSQLDatabase).GetString(), + SSLMode: DefaultMgr().Get(backgroundCtx, common.PostGreSQLSSLMode).GetString(), + MaxIdleConns: DefaultMgr().Get(backgroundCtx, common.PostGreSQLMaxIdleConns).GetInt(), + MaxOpenConns: DefaultMgr().Get(backgroundCtx, common.PostGreSQLMaxOpenConns).GetInt(), + ConnMaxLifetime: DefaultMgr().Get(backgroundCtx, common.PostGreSQLConnMaxLifetime).GetDuration(), + ConnMaxIdleTime: DefaultMgr().Get(backgroundCtx, common.PostGreSQLConnMaxIdleTime).GetDuration(), } database.PostGreSQL = postgresql diff --git a/src/pkg/config/manager.go b/src/pkg/config/manager.go index 4ecb253f24a8..c853dd5b3ea4 100644 --- a/src/pkg/config/manager.go +++ b/src/pkg/config/manager.go @@ -159,14 +159,16 @@ func (c *CfgManager) GetDatabaseCfg() *models.Database { return &models.Database{ Type: c.Get(ctx, common.DatabaseType).GetString(), PostGreSQL: &models.PostGreSQL{ - Host: c.Get(ctx, common.PostGreSQLHOST).GetString(), - Port: c.Get(ctx, common.PostGreSQLPort).GetInt(), - Username: c.Get(ctx, common.PostGreSQLUsername).GetString(), - Password: c.Get(ctx, common.PostGreSQLPassword).GetString(), - Database: c.Get(ctx, common.PostGreSQLDatabase).GetString(), - SSLMode: c.Get(ctx, common.PostGreSQLSSLMode).GetString(), - MaxIdleConns: c.Get(ctx, common.PostGreSQLMaxIdleConns).GetInt(), - MaxOpenConns: c.Get(ctx, common.PostGreSQLMaxOpenConns).GetInt(), + Host: c.Get(ctx, common.PostGreSQLHOST).GetString(), + Port: c.Get(ctx, common.PostGreSQLPort).GetInt(), + Username: c.Get(ctx, common.PostGreSQLUsername).GetString(), + Password: c.Get(ctx, common.PostGreSQLPassword).GetString(), + Database: c.Get(ctx, common.PostGreSQLDatabase).GetString(), + SSLMode: c.Get(ctx, common.PostGreSQLSSLMode).GetString(), + MaxIdleConns: c.Get(ctx, common.PostGreSQLMaxIdleConns).GetInt(), + MaxOpenConns: c.Get(ctx, common.PostGreSQLMaxOpenConns).GetInt(), + ConnMaxLifetime: c.Get(ctx, common.PostGreSQLConnMaxLifetime).GetDuration(), + ConnMaxIdleTime: c.Get(ctx, common.PostGreSQLConnMaxIdleTime).GetDuration(), }, } }