Skip to content

Commit 55ffb2e

Browse files
authored
add configurable maxIdleConns and maxIdleTime (#713)
Signed-off-by: Arne Rutjes <arne123@gmail.com>
1 parent 135f646 commit 55ffb2e

File tree

19 files changed

+162
-33
lines changed

19 files changed

+162
-33
lines changed

docs/core-fabric.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -480,8 +480,10 @@ persistence:
480480
dataSource: file:/some/path/fsc.sqlite&_txlock=immediate
481481
tablePrefix: fsc # optional
482482
skipCreateTable: true # tells FSC _not_ to create a table when starting up (because it already exists).
483-
skipPragmas: true # if this is false, the pragmas we set in the datasource will be overridden with the defaults.
484-
maxOpenConns: 50 # optional: max open read connections to the database. Defaults to unlimited.
483+
skipPragmas: true # if this is false, the pragmas we set in the datasource will be overridden with the defaults (sqlite specific).
484+
maxOpenConns: 20 # optional: max open read connections to the database. Defaults to unlimited. See https://go.dev/doc/database/manage-connections.
485+
maxIdleConns: 20 # optional: max idle read connections to the database. Defaults to 2.
486+
maxIdleTime: 30s # optional: max duration a connection can be idle before it is closed. Defaults to 1 minute.
485487
```
486488

487489
By default we set the following pragmas (unless you do `skipPragmas: true`. Make sure you always have `_pragma=journal_mode(WAL`):
@@ -510,4 +512,11 @@ persistence:
510512
opts:
511513
driver: postgres
512514
dataSource: host=localhost port=5432 user=postgres password=example dbname=tokendb sslmode=disable
515+
maxOpenConns: 25 # optional: max open read connections to the database. Defaults to unlimited.
516+
maxIdleConns: 25 # optional: max idle read connections to the database. Defaults to 2.
517+
maxIdleTime: 30s # optional: max duration a connection can be idle before it is closed. Defaults to 1 minute.
513518
```
519+
520+
For more info about managing connections, see https://go.dev/doc/database/manage-connections. Keep in mind that Fabric Smart Client
521+
maintains two independent database instances: one for KVS and one for the Vault. The combined maxOpenConns should not exceed the
522+
configured max_connections in the postgres server (100 by default).

platform/common/core/generic/vault/db/helpers.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ func openBadger[V any](tempDir, dir string, open opener[V]) (V, error) {
5252

5353
type dbConfig common.Opts
5454

55-
func (c *dbConfig) IsSet(string) bool { panic("not supported") }
55+
func (c *dbConfig) IsSet(string) bool { return false }
5656
func (c *dbConfig) UnmarshalKey(key string, rawVal interface{}) error {
5757
if len(key) > 0 {
5858
return errors.New("invalid key")

platform/view/services/db/dbtest/bench.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package dbtest
88

99
import (
1010
"fmt"
11+
"math"
1112
"testing"
1213

1314
"github.com/hyperledger-labs/fabric-smart-client/platform/view/services/db/driver"
@@ -69,6 +70,8 @@ func WriteOne(b *testing.B, db driver.TransactionalVersionedPersistence) {
6970
func WriteMany(b *testing.B, db driver.TransactionalVersionedPersistence) {
7071
var err error
7172
var k string
73+
b.Logf("before: %+v", db.Stats())
74+
mid := math.Round(float64(b.N) / 2)
7275
b.ResetTimer()
7376
for i := 0; i < b.N; i++ {
7477
k = fmt.Sprintf("key_%d", i)
@@ -78,9 +81,50 @@ func WriteMany(b *testing.B, db driver.TransactionalVersionedPersistence) {
7881
err = db.SetState(namespace, k, driver.VersionedValue{Raw: payload})
7982
_ = err
8083
err = db.Commit()
84+
85+
if i == int(mid) {
86+
b.Logf("mid: %+v", db.Stats())
87+
}
8188
}
8289
b.StopTimer()
8390
returnErr = err
8491
assert.NoError(b, returnErr)
8592
b.Logf("%.0f writes per second to different keys", float64(b.N)/b.Elapsed().Seconds())
93+
b.Logf("after: %+v", db.Stats())
94+
}
95+
96+
func WriteParallel(b *testing.B, db driver.TransactionalUnversionedPersistence) {
97+
var err error
98+
var k string
99+
var i int
100+
b.Logf(" before: %+v", db.Stats())
101+
mid := math.Round(float64(b.N) / 2)
102+
b.ResetTimer()
103+
104+
b.RunParallel(func(pb *testing.PB) {
105+
for pb.Next() {
106+
i++ // i is not unique because of paralellism but that's ok
107+
k = fmt.Sprintf("key_%d", i) // this could be optimized by moving the key creation out of the benchmark
108+
if i == int(mid) {
109+
b.Logf(" mid (%d): %+v", int(mid), db.Stats())
110+
}
111+
112+
tx, err := db.NewWriteTransaction()
113+
if err != nil {
114+
b.Error(err)
115+
}
116+
if err := tx.SetState(namespace, k, payload); err != nil {
117+
b.Error(err)
118+
}
119+
if err := tx.Commit(); err != nil {
120+
b.Error(err)
121+
}
122+
}
123+
})
124+
125+
b.StopTimer()
126+
returnErr = err
127+
assert.NoError(b, returnErr)
128+
b.Logf(" after: %+v", db.Stats())
129+
b.Logf("%.0f writes per second to different keys", float64(b.N)/b.Elapsed().Seconds())
86130
}

platform/view/services/db/driver/badger/badger.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,10 @@ func (db *DB) NewWriteTransaction() (driver.WriteTransaction, error) {
248248
}, nil
249249
}
250250

251+
func (db *DB) Stats() any {
252+
return db.db.BlockCacheMetrics()
253+
}
254+
251255
type txMgr interface {
252256
Commit() error
253257
Discard() error

platform/view/services/db/driver/driver.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ type BasePersistence[V any, R any] interface {
7373
Commit() error
7474
// Discard discards the changes since BeginUpdate
7575
Discard() error
76+
// Stats returns driver specific statistics of the datastore
77+
Stats() any
7678
}
7779

7880
// UnversionedPersistence models a key-value storage place

platform/view/services/db/driver/notifier/persistence.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,10 @@ func (db *UnversionedPersistenceNotifier[P]) Close() error { return db.Persisten
108108

109109
func (db *UnversionedPersistenceNotifier[P]) BeginUpdate() error { return db.Persistence.BeginUpdate() }
110110

111+
func (db *UnversionedPersistenceNotifier[P]) Stats() any {
112+
return db.Persistence.Stats()
113+
}
114+
111115
func (db *UnversionedPersistenceNotifier[P]) Subscribe(callback driver.TriggerCallback) error {
112116
return db.Notifier.Subscribe(callback)
113117
}
@@ -210,6 +214,10 @@ func (db *VersionedPersistenceNotifier[P]) GetStateSetIterator(ns driver2.Namesp
210214
return db.Persistence.GetStateSetIterator(ns, keys...)
211215
}
212216

217+
func (db *VersionedPersistenceNotifier[P]) Stats() any {
218+
return db.Persistence.Stats()
219+
}
220+
213221
func (db *VersionedPersistenceNotifier[P]) Close() error { return db.Persistence.Close() }
214222

215223
func (db *VersionedPersistenceNotifier[P]) BeginUpdate() error { return db.Persistence.BeginUpdate() }

platform/view/services/db/driver/sql/common/base.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"database/sql"
1111
"fmt"
1212
"strings"
13+
"time"
1314

1415
errors2 "github.com/hyperledger-labs/fabric-smart-client/pkg/utils/errors"
1516
driver2 "github.com/hyperledger-labs/fabric-smart-client/platform/common/driver"
@@ -54,7 +55,7 @@ type BasePersistence[V any, R any] struct {
5455

5556
func NewBasePersistence[V any, R any](writeDB *sql.DB, readDB *sql.DB, table string, readScanner readScanner[R], valueScanner ValueScanner[V], errorWrapper driver.SQLErrorWrapper, ci Interpreter, newTransaction func() (*sql.Tx, error)) *BasePersistence[V, R] {
5657
return &BasePersistence[V, R]{
57-
BaseDB: common.NewBaseDB[*sql.Tx](func() (*sql.Tx, error) { return newTransaction() }),
58+
BaseDB: common.NewBaseDB(func() (*sql.Tx, error) { return newTransaction() }),
5859
readDB: readDB,
5960
writeDB: writeDB,
6061
table: table,
@@ -301,6 +302,13 @@ func (db *BasePersistence[V, R]) TableName() string {
301302
return db.table
302303
}
303304

305+
func (db *BasePersistence[V, R]) Stats() any {
306+
if db.readDB != nil {
307+
return db.readDB.Stats()
308+
}
309+
return nil
310+
}
311+
304312
type readIterator[V any] struct {
305313
txs *sql.Rows
306314
scanner readScanner[V]
@@ -329,6 +337,8 @@ type Opts struct {
329337
SkipCreateTable bool
330338
SkipPragmas bool
331339
MaxOpenConns int
340+
MaxIdleConns int
341+
MaxIdleTime time.Duration
332342
}
333343

334344
func generateParamSet(offset, count int) string {

platform/view/services/db/driver/sql/common/utils.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"regexp"
1313
"runtime/debug"
1414
"strings"
15+
"time"
1516

1617
errors2 "github.com/hyperledger-labs/fabric-smart-client/pkg/utils/errors"
1718
"github.com/hyperledger-labs/fabric-smart-client/platform/view/services/db/driver"
@@ -91,6 +92,13 @@ func GetOpts(config driver.Config, optsKey string) (*Opts, error) {
9192
if opts.DataSource == "" {
9293
return nil, notSetError(optsKey + ".dataSource")
9394
}
95+
if !config.IsSet(optsKey + ".maxIdleConns") {
96+
opts.MaxIdleConns = 2 // go default
97+
}
98+
if !config.IsSet(optsKey + ".maxIdleTime") {
99+
opts.MaxIdleTime = time.Minute
100+
}
101+
94102
return &opts, nil
95103
}
96104

platform/view/services/db/driver/sql/common/versioned.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ func NewVersionedPersistence(base basePersistence[driver.VersionedValue, driver.
100100
}
101101

102102
func NewVersioned(readDB *sql.DB, writeDB *sql.DB, table string, errorWrapper driver.SQLErrorWrapper, ci Interpreter) *VersionedPersistence {
103-
base := NewBasePersistence[driver.VersionedValue, driver.VersionedRead](writeDB, readDB, table, &versionedReadScanner{}, &versionedValueScanner{}, errorWrapper, ci, writeDB.Begin)
103+
base := NewBasePersistence(writeDB, readDB, table, &versionedReadScanner{}, &versionedValueScanner{}, errorWrapper, ci, writeDB.Begin)
104104
return NewVersionedPersistence(base, base.table, base.errorWrapper, base.readDB, base.writeDB)
105105
}
106106

@@ -176,6 +176,10 @@ func (db *VersionedPersistence) NewWriteTransaction() (driver.WriteTransaction,
176176
return NewWriteTransaction(txn, db), nil
177177
}
178178

179+
func (db *VersionedPersistence) Stats() any {
180+
return db.basePersistence.Stats()
181+
}
182+
179183
type versionedPersistence interface {
180184
SetStateWithTx(tx *sql.Tx, ns driver2.Namespace, pkey string, value driver.VersionedValue) error
181185
DeleteStateWithTx(tx *sql.Tx, ns driver2.Namespace, key driver2.PKey) error

platform/view/services/db/driver/sql/postgres/bench_test.go

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@ package postgres
88

99
import (
1010
"testing"
11+
"time"
1112

1213
"github.com/hyperledger-labs/fabric-smart-client/platform/view/services/db/dbtest"
14+
"github.com/hyperledger-labs/fabric-smart-client/platform/view/services/db/driver/unversioned"
1315
)
1416

1517
func BenchmarkReadExistingPostgres(b *testing.B) {
@@ -18,7 +20,7 @@ func BenchmarkReadExistingPostgres(b *testing.B) {
1820
b.Fatal(err)
1921
}
2022
defer terminate()
21-
db, err := initPersistence(NewVersioned, pgConnStr, "benchmark", 50)
23+
db, err := initPersistence(NewVersioned, pgConnStr, "benchmark", 50, 2, time.Minute)
2224
if err != nil {
2325
b.Fatal(err)
2426
}
@@ -33,7 +35,7 @@ func BenchmarkReadNonExistingPostgres(b *testing.B) {
3335
b.Fatal(err)
3436
}
3537
defer terminate()
36-
db, err := initPersistence(NewVersioned, pgConnStr, "benchmark", 50)
38+
db, err := initPersistence(NewVersioned, pgConnStr, "benchmark", 50, 2, time.Minute)
3739
if err != nil {
3840
b.Fatal(err)
3941
}
@@ -48,7 +50,7 @@ func BenchmarkWriteOnePostgres(b *testing.B) {
4850
b.Fatal(err)
4951
}
5052
defer terminate()
51-
db, err := initPersistence(NewVersioned, pgConnStr, "benchmark", 50)
53+
db, err := initPersistence(NewVersioned, pgConnStr, "benchmark", 50, 2, time.Minute)
5254
if err != nil {
5355
b.Fatal(err)
5456
}
@@ -63,11 +65,27 @@ func BenchmarkWriteManyPostgres(b *testing.B) {
6365
b.Fatal(err)
6466
}
6567
defer terminate()
66-
db, err := initPersistence(NewVersioned, pgConnStr, "benchmark", 50)
68+
db, err := initPersistence(NewVersioned, pgConnStr, "benchmark", 50, 2, time.Minute)
6769
if err != nil {
6870
b.Fatal(err)
6971
}
7072
defer db.Close()
7173

7274
dbtest.WriteMany(b, db)
7375
}
76+
77+
func BenchmarkWriteManyPostgresWithIdle(b *testing.B) {
78+
terminate, pgConnStr, err := StartPostgres(b, false)
79+
if err != nil {
80+
b.Fatal(err)
81+
}
82+
defer terminate()
83+
p, err := initPersistence(NewVersioned, pgConnStr, "benchmark", 50, 50, time.Minute)
84+
if err != nil {
85+
b.Fatal(err)
86+
}
87+
db := &unversioned.Transactional{TransactionalVersioned: p}
88+
defer db.Close()
89+
90+
dbtest.WriteParallel(b, db)
91+
}

0 commit comments

Comments
 (0)