diff --git a/integration/nwo/fsc/node/core_template.go b/integration/nwo/fsc/node/core_template.go index 958f1ece3..ddc10a426 100755 --- a/integration/nwo/fsc/node/core_template.go +++ b/integration/nwo/fsc/node/core_template.go @@ -96,6 +96,8 @@ fsc: createSchema: {{ NodeKVSPersistence.SQL.CreateSchema }} tablePrefix: {{ NodeKVSPersistence.SQL.TablePrefix }} maxOpenConns: {{ NodeKVSPersistence.SQL.MaxOpenConns }} + maxIdleConns: 3 + maxIdleTime: 45s {{- else }} path: {{ NodeKVSPath }} SyncWrites: false diff --git a/platform/view/services/db/driver/memory/driver.go b/platform/view/services/db/driver/memory/driver.go index 0599b289e..fe745cd03 100644 --- a/platform/view/services/db/driver/memory/driver.go +++ b/platform/view/services/db/driver/memory/driver.go @@ -27,6 +27,8 @@ var ( SkipCreateTable: false, SkipPragmas: false, MaxOpenConns: 10, + MaxIdleConns: common.CopyPtr(common.DefaultMaxIdleConns), + MaxIdleTime: common.CopyPtr(common.DefaultMaxIdleTime), } ) diff --git a/platform/view/services/db/driver/sql/common/base.go b/platform/view/services/db/driver/sql/common/base.go index f2bfc695a..c7a21ae56 100644 --- a/platform/view/services/db/driver/sql/common/base.go +++ b/platform/view/services/db/driver/sql/common/base.go @@ -337,8 +337,8 @@ type Opts struct { SkipCreateTable bool SkipPragmas bool MaxOpenConns int - MaxIdleConns int - MaxIdleTime time.Duration + MaxIdleConns *int + MaxIdleTime *time.Duration } func generateParamSet(offset, count int) string { diff --git a/platform/view/services/db/driver/sql/common/utils.go b/platform/view/services/db/driver/sql/common/utils.go index f1260043c..23b39e847 100644 --- a/platform/view/services/db/driver/sql/common/utils.go +++ b/platform/view/services/db/driver/sql/common/utils.go @@ -19,6 +19,11 @@ import ( "github.com/pkg/errors" ) +const ( + DefaultMaxIdleConns = 2 + DefaultMaxIdleTime = time.Minute +) + type TableNameCreator struct { prefix string r *regexp.Regexp @@ -92,16 +97,21 @@ func GetOpts(config driver.Config, optsKey string) (*Opts, error) { if opts.DataSource == "" { return nil, notSetError(optsKey + ".dataSource") } - if !config.IsSet(optsKey + ".maxIdleConns") { - opts.MaxIdleConns = 2 // go default + if opts.MaxIdleConns == nil { + opts.MaxIdleConns = CopyPtr(DefaultMaxIdleConns) // go default } - if !config.IsSet(optsKey + ".maxIdleTime") { - opts.MaxIdleTime = time.Minute + if opts.MaxIdleTime == nil { + opts.MaxIdleTime = CopyPtr(DefaultMaxIdleTime) } return &opts, nil } +func CopyPtr[T any](t T) *T { + v := t + return &v +} + func notSetError(key string) error { return fmt.Errorf( "either %s in core.yaml or the %s environment variable must be specified", key, diff --git a/platform/view/services/db/driver/sql/driver.go b/platform/view/services/db/driver/sql/driver.go index add17b821..0443b0c92 100644 --- a/platform/view/services/db/driver/sql/driver.go +++ b/platform/view/services/db/driver/sql/driver.go @@ -132,5 +132,11 @@ func getOps(config driver.Config) (common.Opts, error) { if opts.TablePrefix == "" { opts.TablePrefix = "fsc" } + if opts.MaxIdleTime == nil { + opts.MaxIdleTime = common.CopyPtr(common.DefaultMaxIdleTime) + } + if opts.MaxIdleConns == nil { + opts.MaxIdleConns = common.CopyPtr(common.DefaultMaxIdleConns) + } return *opts, nil } diff --git a/platform/view/services/db/driver/sql/postgres/persistence.go b/platform/view/services/db/driver/sql/postgres/persistence.go index d4fe148a6..c82fe4d61 100644 --- a/platform/view/services/db/driver/sql/postgres/persistence.go +++ b/platform/view/services/db/driver/sql/postgres/persistence.go @@ -12,6 +12,7 @@ import ( "time" "github.com/hyperledger-labs/fabric-smart-client/platform/common/services/logging" + "github.com/hyperledger-labs/fabric-smart-client/platform/view/services/db/driver/sql/common" _ "github.com/jackc/pgx/v5/stdlib" ) @@ -19,21 +20,28 @@ var logger = logging.MustGetLogger("view-sdk.db.postgres") const driverName = "pgx" -func OpenDB(dataSourceName string, maxOpenConns, maxIdleConns int, maxIdleTime time.Duration) (*sql.DB, error) { +func OpenDB(dataSourceName string, maxOpenConns int, maxIdleConns *int, maxIdleTime *time.Duration) (*sql.DB, error) { db, err := sql.Open(driverName, dataSourceName) if err != nil { logger.Error(err) return nil, fmt.Errorf("can't open %s database: %w", driverName, err) } + if maxIdleConns == nil { + maxIdleConns = common.CopyPtr(common.DefaultMaxIdleConns) + } + if maxIdleTime == nil { + maxIdleTime = common.CopyPtr(common.DefaultMaxIdleTime) + } + db.SetMaxOpenConns(maxOpenConns) - db.SetMaxIdleConns(maxIdleConns) - db.SetConnMaxIdleTime(maxIdleTime) + db.SetMaxIdleConns(*maxIdleConns) + db.SetConnMaxIdleTime(*maxIdleTime) if err = db.Ping(); err != nil { return nil, err } - logger.Infof("connected to [%s] for reads, max open connections: %d", driverName, maxOpenConns) + logger.Infof("connected to [%s] for reads, max open connections: %d, max idle connections: %d, max idle time: %v", driverName, maxOpenConns, maxIdleConns, maxIdleTime) logger.Info("using same db for writes") diff --git a/platform/view/services/db/driver/sql/postgres/test_utils.go b/platform/view/services/db/driver/sql/postgres/test_utils.go index 9633cb66e..39f4d149f 100644 --- a/platform/view/services/db/driver/sql/postgres/test_utils.go +++ b/platform/view/services/db/driver/sql/postgres/test_utils.go @@ -257,7 +257,7 @@ type dbObject interface { type persistenceConstructor[V dbObject] func(common2.Opts, string) (V, error) func initPersistence[V dbObject](constructor persistenceConstructor[V], pgConnStr, name string, maxOpenConns, maxIdleConns int, maxIdleTime time.Duration) (V, error) { - p, err := constructor(common2.Opts{DataSource: pgConnStr, MaxOpenConns: maxOpenConns, MaxIdleConns: maxIdleConns, MaxIdleTime: maxIdleTime}, name) + p, err := constructor(common2.Opts{DataSource: pgConnStr, MaxOpenConns: maxOpenConns, MaxIdleConns: &maxIdleConns, MaxIdleTime: &maxIdleTime}, name) if err != nil { return utils.Zero[V](), err } diff --git a/platform/view/services/db/driver/sql/sqlite/persistence.go b/platform/view/services/db/driver/sql/sqlite/persistence.go index 0457a4a69..0b9cf6735 100644 --- a/platform/view/services/db/driver/sql/sqlite/persistence.go +++ b/platform/view/services/db/driver/sql/sqlite/persistence.go @@ -16,6 +16,12 @@ import ( "time" "github.com/hyperledger-labs/fabric-smart-client/platform/common/services/logging" + "github.com/hyperledger-labs/fabric-smart-client/platform/view/services/db/driver/sql/common" +) + +const ( + maxIdleConnsWrite = 1 + maxIdleTimeWrite time.Duration = 0 ) const sqlitePragmas = ` @@ -30,34 +36,41 @@ const driverName = "sqlite" var logger = logging.MustGetLogger("view-sdk.db.sqlite") -func openDB(dataSourceName string, maxOpenConns, maxIdleConns int, maxIdleTime time.Duration, skipPragmas bool) (*sql.DB, *sql.DB, error) { +func openDB(dataSourceName string, maxOpenConns int, maxIdleConns *int, maxIdleTime *time.Duration, skipPragmas bool) (*sql.DB, *sql.DB, error) { logger.Infof("Opening read db [%v]", dataSourceName) readDB, err := OpenDB(dataSourceName, maxOpenConns, maxIdleConns, maxIdleTime, skipPragmas) if err != nil { return nil, nil, fmt.Errorf("can't open read %s database: %w", driverName, err) } logger.Infof("Opening write db [%v]", dataSourceName) - writeDB, err := OpenDB(dataSourceName, 1, 1, 0, skipPragmas) + writeDB, err := OpenDB(dataSourceName, 1, common.CopyPtr(maxIdleConnsWrite), common.CopyPtr(maxIdleTimeWrite), skipPragmas) if err != nil { return nil, nil, fmt.Errorf("can't open write %s database: %w", driverName, err) } return readDB, writeDB, nil } -func OpenDB(dataSourceName string, maxOpenConns, maxIdleConns int, maxIdleTime time.Duration, skipPragmas bool) (*sql.DB, error) { +func OpenDB(dataSourceName string, maxOpenConns int, maxIdleConns *int, maxIdleTime *time.Duration, skipPragmas bool) (*sql.DB, error) { // Create directories if they do not exist to avoid error "out of memory (14)", see below path := getDir(dataSourceName) if err := os.MkdirAll(path, 0777); err != nil { logger.Warnf("failed creating dir [%s]: %s", path, err) } + if maxIdleConns == nil { + maxIdleConns = common.CopyPtr(common.DefaultMaxIdleConns) + } + if maxIdleTime == nil { + maxIdleTime = common.CopyPtr(common.DefaultMaxIdleTime) + } + db, err := sql.Open(driverName, dataSourceName) if err != nil { return nil, fmt.Errorf("can't open %s database: %w", driverName, err) } db.SetMaxOpenConns(maxOpenConns) - db.SetMaxIdleConns(maxIdleConns) - db.SetConnMaxIdleTime(maxIdleTime) + db.SetMaxIdleConns(*maxIdleConns) + db.SetConnMaxIdleTime(*maxIdleTime) if err = db.Ping(); err != nil && strings.Contains(err.Error(), "out of memory (14)") { return nil, fmt.Errorf("can't open %s database, does the folder exist?", driverName) diff --git a/platform/view/services/db/driver/sql/sqlite/test_utils.go b/platform/view/services/db/driver/sql/sqlite/test_utils.go index 466df0e80..5e0724427 100644 --- a/platform/view/services/db/driver/sql/sqlite/test_utils.go +++ b/platform/view/services/db/driver/sql/sqlite/test_utils.go @@ -9,6 +9,7 @@ package sqlite import ( "fmt" "path" + "time" "github.com/hyperledger-labs/fabric-smart-client/platform/view/services/db/driver" common2 "github.com/hyperledger-labs/fabric-smart-client/platform/view/services/db/driver/sql/common" @@ -65,9 +66,11 @@ func (t *TestDriver) NewTransactionalUnversioned(dataSourceName string, config d } func unversionedOpts(name string, tempDir string) common2.Opts { - return common2.Opts{DataSource: fmt.Sprintf("file:%s.sqlite?_pragma=busy_timeout(1000)", path.Join(tempDir, name))} + maxIdleConns, maxIdleTime := 2, 1*time.Minute + return common2.Opts{DataSource: fmt.Sprintf("file:%s.sqlite?_pragma=busy_timeout(1000)", path.Join(tempDir, name)), MaxIdleConns: &maxIdleConns, MaxIdleTime: &maxIdleTime} } func versionedOpts(name string, tempDir string) common2.Opts { - return common2.Opts{DataSource: fmt.Sprintf("%s.sqlite", path.Join(tempDir, name))} + maxIdleConns, maxIdleTime := 2, 1*time.Minute + return common2.Opts{DataSource: fmt.Sprintf("%s.sqlite", path.Join(tempDir, name)), MaxIdleConns: &maxIdleConns, MaxIdleTime: &maxIdleTime} }