From 72dbc3dbb4efaa48fdc7f900142e76456e2a73ad Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Wed, 21 Dec 2022 18:18:34 +0200 Subject: [PATCH 1/8] kvdb: bump go version to 1.18 Bump the go version to go1.18 so that we can get rid of the '// +build' comments in the upcoming commits where some of the '//go:build' comments will become too complex to write as '// +build' comments. --- kvdb/go.mod | 86 +++++++++++++++++++++++++++++++++++++++++++++++------ kvdb/go.sum | 4 --- 2 files changed, 77 insertions(+), 13 deletions(-) diff --git a/kvdb/go.mod b/kvdb/go.mod index a75cdac9555..a5c91d973a4 100644 --- a/kvdb/go.mod +++ b/kvdb/go.mod @@ -1,22 +1,14 @@ module github.com/lightningnetwork/lnd/kvdb require ( - github.com/andybalholm/brotli v1.0.3 // indirect github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f github.com/btcsuite/btcwallet/walletdb v1.3.6-0.20210803004036-eebed51155ec github.com/davecgh/go-spew v1.1.1 github.com/fergusstrange/embedded-postgres v1.10.0 - github.com/golang/snappy v0.0.4 // indirect github.com/google/btree v1.0.1 github.com/jackc/pgx/v4 v4.13.0 - github.com/klauspost/compress v1.13.6 // indirect - github.com/klauspost/pgzip v1.2.5 // indirect - github.com/lib/pq v1.10.3 // indirect github.com/lightningnetwork/lnd/healthcheck v1.0.0 - github.com/nwaples/rardecode v1.1.2 // indirect - github.com/pierrec/lz4/v4 v4.1.8 // indirect github.com/stretchr/testify v1.7.0 - github.com/ulikunitz/xz v0.5.10 // indirect go.etcd.io/bbolt v1.3.6 go.etcd.io/etcd/api/v3 v3.5.0 go.etcd.io/etcd/client/pkg/v3 v3.5.0 @@ -25,6 +17,82 @@ require ( golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 ) +require ( + github.com/andybalholm/brotli v1.0.3 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.1.1 // indirect + github.com/coreos/go-semver v0.3.0 // indirect + github.com/coreos/go-systemd/v22 v22.3.2 // indirect + github.com/dsnet/compress v0.0.1 // indirect + github.com/dustin/go-humanize v1.0.0 // indirect + github.com/form3tech-oss/jwt-go v3.2.3+incompatible // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/gorilla/websocket v1.4.2 // indirect + github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect + github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect + github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect + github.com/jackc/chunkreader/v2 v2.0.1 // indirect + github.com/jackc/pgconn v1.10.0 // indirect + github.com/jackc/pgio v1.0.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgproto3/v2 v2.1.1 // indirect + github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect + github.com/jackc/pgtype v1.8.1 // indirect + github.com/jonboulle/clockwork v0.2.2 // indirect + github.com/json-iterator/go v1.1.11 // indirect + github.com/klauspost/compress v1.13.6 // indirect + github.com/klauspost/pgzip v1.2.5 // indirect + github.com/lib/pq v1.10.3 // indirect + github.com/lightningnetwork/lnd/ticker v1.0.0 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect + github.com/mholt/archiver/v3 v3.5.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.1 // indirect + github.com/nwaples/rardecode v1.1.2 // indirect + github.com/pierrec/lz4/v4 v4.1.8 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/prometheus/client_golang v1.11.0 // indirect + github.com/prometheus/client_model v0.2.0 // indirect + github.com/prometheus/common v0.26.0 // indirect + github.com/prometheus/procfs v0.6.0 // indirect + github.com/sirupsen/logrus v1.7.0 // indirect + github.com/soheilhy/cmux v0.1.5 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 // indirect + github.com/ulikunitz/xz v0.5.10 // indirect + github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect + github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect + go.etcd.io/etcd/client/v2 v2.305.0 // indirect + go.etcd.io/etcd/pkg/v3 v3.5.0 // indirect + go.etcd.io/etcd/raft/v3 v3.5.0 // indirect + go.opentelemetry.io/contrib v0.20.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0 // indirect + go.opentelemetry.io/otel v0.20.0 // indirect + go.opentelemetry.io/otel/exporters/otlp v0.20.0 // indirect + go.opentelemetry.io/otel/metric v0.20.0 // indirect + go.opentelemetry.io/otel/sdk v0.20.0 // indirect + go.opentelemetry.io/otel/sdk/export/metric v0.20.0 // indirect + go.opentelemetry.io/otel/sdk/metric v0.20.0 // indirect + go.opentelemetry.io/otel/trace v0.20.0 // indirect + go.opentelemetry.io/proto/otlp v0.7.0 // indirect + go.uber.org/atomic v1.7.0 // indirect + go.uber.org/multierr v1.6.0 // indirect + go.uber.org/zap v1.17.0 // indirect + golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect + golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 // indirect + golang.org/x/text v0.3.6 // indirect + golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect + google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c // indirect + google.golang.org/grpc v1.38.0 // indirect + google.golang.org/protobuf v1.26.0 // indirect + gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect + sigs.k8s.io/yaml v1.2.0 // indirect +) + // This replace is for https://github.com/advisories/GHSA-w73w-5m7g-f7qc replace github.com/dgrijalva/jwt-go => github.com/golang-jwt/jwt v3.2.1+incompatible @@ -35,4 +103,4 @@ replace github.com/ulikunitz/xz => github.com/ulikunitz/xz v0.5.8 // https://deps.dev/advisory/OSV/GO-2021-0053?from=%2Fgo%2Fgithub.com%252Fgogo%252Fprotobuf%2Fv1.3.1 replace github.com/gogo/protobuf => github.com/gogo/protobuf v1.3.2 -go 1.16 +go 1.18 diff --git a/kvdb/go.sum b/kvdb/go.sum index 1674bbbb17c..aa327cd0314 100644 --- a/kvdb/go.sum +++ b/kvdb/go.sum @@ -46,7 +46,6 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054 h1:uH66TXeswKn5PW5zdZ39xEwfS9an067BirqA+P4QaLI= github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= @@ -68,7 +67,6 @@ github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f h1:JOrtw2xFKzlg+cbHpyrpLDmnN1HqhBfnX7WDiW7eG2c= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= @@ -192,7 +190,6 @@ github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0m github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0= github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= @@ -213,7 +210,6 @@ github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5W github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A= github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= From c6abf585eeabb319621f871fb5a594cdeed30599 Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Thu, 15 Dec 2022 17:38:14 +0200 Subject: [PATCH 2/8] kvdb/postgres: make global dbConns thread safe In this commit, a mutex is added to guard access to the global dbConns variable in the kvdb/postgres package. --- kvdb/postgres/db.go | 20 ++++++++++++++++++-- kvdb/postgres/db_conn_set.go | 11 ++++++----- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/kvdb/postgres/db.go b/kvdb/postgres/db.go index 7badd143398..33dc98035b3 100644 --- a/kvdb/postgres/db.go +++ b/kvdb/postgres/db.go @@ -57,11 +57,21 @@ type db struct { // Enforce db implements the walletdb.DB interface. var _ walletdb.DB = (*db)(nil) -// Global set of database connections. -var dbConns *dbConnSet +var ( + // dbConns is a global set of database connections. + dbConns *dbConnSet + dbConnsMu sync.Mutex +) // Init initializes the global set of database connections. func Init(maxConnections int) { + dbConnsMu.Lock() + defer dbConnsMu.Unlock() + + if dbConns != nil { + return + } + dbConns = newDbConnSet(maxConnections) } @@ -70,6 +80,9 @@ func Init(maxConnections int) { func newPostgresBackend(ctx context.Context, config *Config, prefix string) ( *db, error) { + dbConnsMu.Lock() + defer dbConnsMu.Unlock() + if prefix == "" { return nil, errors.New("empty postgres prefix") } @@ -256,6 +269,9 @@ func (db *db) Copy(w io.Writer) error { // Close cleanly shuts down the database and syncs all data. // This function is part of the walletdb.Db interface implementation. func (db *db) Close() error { + dbConnsMu.Lock() + defer dbConnsMu.Unlock() + log.Infof("Closing database %v", db.prefix) return dbConns.Close(db.cfg.Dsn) diff --git a/kvdb/postgres/db_conn_set.go b/kvdb/postgres/db_conn_set.go index 062a977dc7e..ced065969ac 100644 --- a/kvdb/postgres/db_conn_set.go +++ b/kvdb/postgres/db_conn_set.go @@ -19,7 +19,8 @@ type dbConnSet struct { dbConn map[string]*dbConn maxConnections int - sync.Mutex + // mu is used to guard access to the dbConn map. + mu sync.Mutex } // newDbConnSet initializes a new set of connections. @@ -33,8 +34,8 @@ func newDbConnSet(maxConnections int) *dbConnSet { // Open opens a new database connection. If a connection already exists for the // given dsn, the existing connection is returned. func (d *dbConnSet) Open(dsn string) (*sql.DB, error) { - d.Lock() - defer d.Unlock() + d.mu.Lock() + defer d.mu.Unlock() if dbConn, ok := d.dbConn[dsn]; ok { dbConn.count++ @@ -66,8 +67,8 @@ func (d *dbConnSet) Open(dsn string) (*sql.DB, error) { // Close closes the connection with the given dsn. If there are still other // users of the same connection, this function does nothing. func (d *dbConnSet) Close(dsn string) error { - d.Lock() - defer d.Unlock() + d.mu.Lock() + defer d.mu.Unlock() dbConn, ok := d.dbConn[dsn] if !ok { From 3dfaa88bb48c3cfc0f199bc2b33f2d83782a5d11 Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Tue, 17 Jan 2023 13:16:58 +0200 Subject: [PATCH 3/8] kvdb/postgres: remove old +build directive and cleanup Due to the update of the kvdb package to go1.18, the `// +build` directives are no longer required. They are removed in this commit in order to simplify upcoming commits. Along the way, a few typos are fixed and an unused struct is removed. --- kvdb/postgres/db.go | 11 ++--------- kvdb/postgres/db_test.go | 1 - kvdb/postgres/driver.go | 1 - kvdb/postgres/fixture.go | 1 - kvdb/postgres/no_db.go | 1 - kvdb/postgres/readwrite_bucket.go | 1 - kvdb/postgres/readwrite_cursor.go | 1 - kvdb/postgres/readwrite_tx.go | 1 - 8 files changed, 2 insertions(+), 16 deletions(-) diff --git a/kvdb/postgres/db.go b/kvdb/postgres/db.go index 33dc98035b3..aa517e65248 100644 --- a/kvdb/postgres/db.go +++ b/kvdb/postgres/db.go @@ -1,5 +1,4 @@ //go:build kvdb_postgres -// +build kvdb_postgres package postgres @@ -21,13 +20,7 @@ const ( kvTableName = "kv" ) -// KV stores a key/value pair. -type KV struct { - key string - val string -} - -// db holds a reference to the postgres connection connection. +// db holds a reference to the postgres connection. type db struct { // cfg is the postgres connection config. cfg *Config @@ -76,7 +69,7 @@ func Init(maxConnections int) { } // newPostgresBackend returns a db object initialized with the passed backend -// config. If postgres connection cannot be estabished, then returns error. +// config. If postgres connection cannot be established, then returns error. func newPostgresBackend(ctx context.Context, config *Config, prefix string) ( *db, error) { diff --git a/kvdb/postgres/db_test.go b/kvdb/postgres/db_test.go index 84c2b65038e..d397223d1cd 100644 --- a/kvdb/postgres/db_test.go +++ b/kvdb/postgres/db_test.go @@ -1,5 +1,4 @@ //go:build kvdb_postgres -// +build kvdb_postgres package postgres diff --git a/kvdb/postgres/driver.go b/kvdb/postgres/driver.go index 1792eb6529a..4bc22052fa7 100644 --- a/kvdb/postgres/driver.go +++ b/kvdb/postgres/driver.go @@ -1,5 +1,4 @@ //go:build kvdb_postgres -// +build kvdb_postgres package postgres diff --git a/kvdb/postgres/fixture.go b/kvdb/postgres/fixture.go index 94356c31a81..c7e2b014fc8 100644 --- a/kvdb/postgres/fixture.go +++ b/kvdb/postgres/fixture.go @@ -1,5 +1,4 @@ //go:build kvdb_postgres -// +build kvdb_postgres package postgres diff --git a/kvdb/postgres/no_db.go b/kvdb/postgres/no_db.go index edac449e41d..aa49471ae44 100644 --- a/kvdb/postgres/no_db.go +++ b/kvdb/postgres/no_db.go @@ -1,5 +1,4 @@ //go:build !kvdb_postgres -// +build !kvdb_postgres package postgres diff --git a/kvdb/postgres/readwrite_bucket.go b/kvdb/postgres/readwrite_bucket.go index 933769919fc..f71db50bd00 100644 --- a/kvdb/postgres/readwrite_bucket.go +++ b/kvdb/postgres/readwrite_bucket.go @@ -1,5 +1,4 @@ //go:build kvdb_postgres -// +build kvdb_postgres package postgres diff --git a/kvdb/postgres/readwrite_cursor.go b/kvdb/postgres/readwrite_cursor.go index 80e321e061f..67c1a39aef9 100644 --- a/kvdb/postgres/readwrite_cursor.go +++ b/kvdb/postgres/readwrite_cursor.go @@ -1,5 +1,4 @@ //go:build kvdb_postgres -// +build kvdb_postgres package postgres diff --git a/kvdb/postgres/readwrite_tx.go b/kvdb/postgres/readwrite_tx.go index e0c3e23710e..592128ad691 100644 --- a/kvdb/postgres/readwrite_tx.go +++ b/kvdb/postgres/readwrite_tx.go @@ -1,5 +1,4 @@ //go:build kvdb_postgres -// +build kvdb_postgres package postgres From 30ba8cbae9af18914897297a777d6159861a21ca Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Tue, 17 Jan 2023 13:46:45 +0200 Subject: [PATCH 4/8] kvdb/postgres: separate general sql code from postgres In this commit, changes are made to the `kvdb/postgres` package so that all all the non-postgres-specific code is generalised to be applicable for all sql code. A follow up commit will move all the general sql code into its own package. --- kvdb/postgres/db.go | 145 +++++++++++++++++------------- kvdb/postgres/db_conn_set.go | 4 +- kvdb/postgres/db_test.go | 2 +- kvdb/postgres/readwrite_bucket.go | 9 ++ kvdb/postgres/readwrite_tx.go | 41 +++++++-- kvdb/postgres/schema.go | 74 +++++++++++++++ 6 files changed, 201 insertions(+), 74 deletions(-) create mode 100644 kvdb/postgres/schema.go diff --git a/kvdb/postgres/db.go b/kvdb/postgres/db.go index aa517e65248..2c31d46e0bc 100644 --- a/kvdb/postgres/db.go +++ b/kvdb/postgres/db.go @@ -20,10 +20,44 @@ const ( kvTableName = "kv" ) +// SqlConfig holds a set of configuration options of a sql database connection. +type SqlConfig struct { + // DriverName is the string that defines the registered sql driver that + // is to be used. + DriverName string + + // Dsn is the database connection string that will be used to connect + // to the db. + Dsn string + + // Timeout is the time after which a query to the db will be canceled if + // it has not yet completed. + Timeout time.Duration + + // Schema is the name of the schema under which the sql tables should be + // created. It should be left empty for backends like sqlite that do not + // support having more than one schema. + Schema string + + // TableNamePrefix is the name that should be used as a table name + // prefix when constructing the KV style table. + TableNamePrefix string + + // SQLiteCmdReplacements define a one-to-one string mapping of sql + // keywords to the strings that should replace those keywords in any + // commands. Note that the sqlite keywords to be replaced are + // case-sensitive. + SQLiteCmdReplacements SQLiteCmdReplacements + + // WithTxLevelLock when set will ensure that there is a transaction + // level lock. + WithTxLevelLock bool +} + // db holds a reference to the postgres connection. type db struct { - // cfg is the postgres connection config. - cfg *Config + // cfg is the sql db connection config. + cfg *SqlConfig // prefix is the table name prefix that is used to simulate namespaces. // We don't use schemas because at least sqlite does not support that. @@ -38,7 +72,8 @@ type db struct { // db is the underlying database connection instance. db *sql.DB - // lock is the global write lock that ensures single writer. + // lock is the global write lock that ensures single writer. This is + // only used if cfg.WithTxLevelLock is set. lock sync.RWMutex // table is the name of the table that contains the data for all @@ -68,86 +103,45 @@ func Init(maxConnections int) { dbConns = newDbConnSet(maxConnections) } -// newPostgresBackend returns a db object initialized with the passed backend -// config. If postgres connection cannot be established, then returns error. -func newPostgresBackend(ctx context.Context, config *Config, prefix string) ( - *db, error) { - +// NewSqlBackend returns a db object initialized with the passed backend +// config. If database connection cannot be established, then returns error. +func NewSqlBackend(ctx context.Context, cfg *SqlConfig) (*db, error) { dbConnsMu.Lock() defer dbConnsMu.Unlock() - if prefix == "" { - return nil, errors.New("empty postgres prefix") - } - if dbConns == nil { return nil, errors.New("db connection set not initialized") } - dbConn, err := dbConns.Open(config.Dsn) - if err != nil { - return nil, err + if cfg.TableNamePrefix == "" { + return nil, errors.New("empty table name prefix") } - // Compose system table names. - table := fmt.Sprintf( - "%s_%s", prefix, kvTableName, + table := fmt.Sprintf("%s_%s", cfg.TableNamePrefix, kvTableName) + + query := newKVSchemaCreationCmd( + table, cfg.Schema, cfg.SQLiteCmdReplacements, ) - // Execute the create statements to set up a kv table in postgres. Every - // row points to the bucket that it is one via its parent_id field. A - // NULL parent_id means that the key belongs to the upper-most bucket in - // this table. A constraint on parent_id is enforcing referential - // integrity. - // - // Furthermore there is a _p index on parent_id that is required - // for the foreign key constraint. - // - // Finally there are unique indices on (parent_id, key) to prevent the - // same key being present in a bucket more than once (
_up and - //
_unp). In postgres, a single index wouldn't enforce the unique - // constraint on rows with a NULL parent_id. Therefore two indices are - // defined. - _, err = dbConn.ExecContext(ctx, ` -CREATE SCHEMA IF NOT EXISTS public; -CREATE TABLE IF NOT EXISTS public.`+table+` -( - key bytea NOT NULL, - value bytea, - parent_id bigint, - id bigserial PRIMARY KEY, - sequence bigint, - CONSTRAINT `+table+`_parent FOREIGN KEY (parent_id) - REFERENCES public.`+table+` (id) - ON UPDATE NO ACTION - ON DELETE CASCADE -); - -CREATE INDEX IF NOT EXISTS `+table+`_p - ON public.`+table+` (parent_id); - -CREATE UNIQUE INDEX IF NOT EXISTS `+table+`_up - ON public.`+table+` - (parent_id, key) WHERE parent_id IS NOT NULL; - -CREATE UNIQUE INDEX IF NOT EXISTS `+table+`_unp - ON public.`+table+` (key) WHERE parent_id IS NULL; -`) + dbConn, err := dbConns.Open(cfg.DriverName, cfg.Dsn) + if err != nil { + return nil, err + } + + _, err = dbConn.ExecContext(ctx, query) if err != nil { _ = dbConn.Close() return nil, err } - backend := &db{ - cfg: config, - prefix: prefix, + return &db{ + cfg: cfg, ctx: ctx, db: dbConn, table: table, - } - - return backend, nil + prefix: cfg.TableNamePrefix, + }, nil } // getTimeoutCtx gets a timeout context for database requests. @@ -269,3 +263,28 @@ func (db *db) Close() error { return dbConns.Close(db.cfg.Dsn) } + +// sqliteCmdReplacements defines a mapping from some SQLite keywords and phrases +// to their postgres counterparts. +var sqliteCmdReplacements = SQLiteCmdReplacements{ + "BLOB": "BYTEA", + "INTEGER PRIMARY KEY": "BIGSERIAL PRIMARY KEY", +} + +// newPostgresBackend returns a db object initialized with the passed backend +// config. If postgres connection cannot be established, then returns error. +func newPostgresBackend(ctx context.Context, config *Config, prefix string) ( + walletdb.DB, error) { + + cfg := &SqlConfig{ + DriverName: "pgx", + Dsn: config.Dsn, + Timeout: config.Timeout, + Schema: "public", + TableNamePrefix: prefix, + SQLiteCmdReplacements: sqliteCmdReplacements, + WithTxLevelLock: true, + } + + return NewSqlBackend(ctx, cfg) +} diff --git a/kvdb/postgres/db_conn_set.go b/kvdb/postgres/db_conn_set.go index ced065969ac..736f8516bcf 100644 --- a/kvdb/postgres/db_conn_set.go +++ b/kvdb/postgres/db_conn_set.go @@ -33,7 +33,7 @@ func newDbConnSet(maxConnections int) *dbConnSet { // Open opens a new database connection. If a connection already exists for the // given dsn, the existing connection is returned. -func (d *dbConnSet) Open(dsn string) (*sql.DB, error) { +func (d *dbConnSet) Open(driver, dsn string) (*sql.DB, error) { d.mu.Lock() defer d.mu.Unlock() @@ -43,7 +43,7 @@ func (d *dbConnSet) Open(dsn string) (*sql.DB, error) { return dbConn.db, nil } - db, err := sql.Open("pgx", dsn) + db, err := sql.Open(driver, dsn) if err != nil { return nil, err } diff --git a/kvdb/postgres/db_test.go b/kvdb/postgres/db_test.go index d397223d1cd..0378b4dbb85 100644 --- a/kvdb/postgres/db_test.go +++ b/kvdb/postgres/db_test.go @@ -41,7 +41,7 @@ func TestPanic(t *testing.T) { f, err := NewFixture("") require.NoError(t, err) - err = f.Db.(*db).Update(func(tx walletdb.ReadWriteTx) error { + err = f.Db.Update(func(tx walletdb.ReadWriteTx) error { bucket, err := tx.CreateTopLevelBucket([]byte("test")) require.NoError(t, err) diff --git a/kvdb/postgres/readwrite_bucket.go b/kvdb/postgres/readwrite_bucket.go index f71db50bd00..71856b21397 100644 --- a/kvdb/postgres/readwrite_bucket.go +++ b/kvdb/postgres/readwrite_bucket.go @@ -89,6 +89,15 @@ func (b *readWriteBucket) Get(key []byte) []byte { panic(err) } + // When an empty byte array is stored as the value, Sqlite will decode + // that into nil whereas postgres will decode that as an empty byte + // array. Since returning nil is taken to mean that no value has ever + // been written, we ensure here that we at least return an empty array + // so that nil checks will fail. + if len(*value) == 0 { + return []byte{} + } + return *value } diff --git a/kvdb/postgres/readwrite_tx.go b/kvdb/postgres/readwrite_tx.go index 592128ad691..c31b056031c 100644 --- a/kvdb/postgres/readwrite_tx.go +++ b/kvdb/postgres/readwrite_tx.go @@ -28,14 +28,17 @@ type readWriteTx struct { // newReadWriteTx creates an rw transaction using a connection from the // specified pool. func newReadWriteTx(db *db, readOnly bool) (*readWriteTx, error) { - // Obtain the global lock instance. An alternative here is to obtain a - // database lock from Postgres. Unfortunately there is no database-level - // lock in Postgres, meaning that each table would need to be locked - // individually. Perhaps an advisory lock could perform this function - // too. - var locker sync.Locker = &db.lock - if readOnly { - locker = db.lock.RLocker() + locker := newNoopLocker() + if db.cfg.WithTxLevelLock { + // Obtain the global lock instance. An alternative here is to + // obtain a database lock from Postgres. Unfortunately there is + // no database-level lock in Postgres, meaning that each table + // would need to be locked individually. Perhaps an advisory + // lock could perform this function too. + locker = &db.lock + if readOnly { + locker = db.lock.RLocker() + } } locker.Lock() @@ -198,3 +201,25 @@ func (tx *readWriteTx) Exec(query string, args ...interface{}) (sql.Result, return tx.tx.ExecContext(ctx, query, args...) } + +// noopLocker is an implementation of a no-op sync.Locker. +type noopLocker struct{} + +// newNoopLocker creates a new noopLocker. +func newNoopLocker() sync.Locker { + return &noopLocker{} +} + +// Lock is a noop. +// +// NOTE: this is part of the sync.Locker interface. +func (n *noopLocker) Lock() { +} + +// Unlock is a noop. +// +// NOTE: this is part of the sync.Locker interface. +func (n *noopLocker) Unlock() { +} + +var _ sync.Locker = (*noopLocker)(nil) diff --git a/kvdb/postgres/schema.go b/kvdb/postgres/schema.go new file mode 100644 index 00000000000..9276326e355 --- /dev/null +++ b/kvdb/postgres/schema.go @@ -0,0 +1,74 @@ +//go:build kvdb_postgres + +package postgres + +import ( + "fmt" + "strings" +) + +// SQLiteCmdReplacements is a one to one mapping of sqlite keywords that should +// be replaced by the mapped strings in any command. Note that the sqlite +// keywords to be replaced are case-sensitive. +type SQLiteCmdReplacements map[string]string + +func newKVSchemaCreationCmd(table, schema string, + replacements SQLiteCmdReplacements) string { + + var ( + tableInSchema = table + finalCmd string + ) + if schema != "" { + finalCmd = fmt.Sprintf( + `CREATE SCHEMA IF NOT EXISTS ` + schema + `;`, + ) + + tableInSchema = fmt.Sprintf("%s.%s", schema, table) + } + + // Construct the sql statements to set up a kv table in postgres. Every + // row points to the bucket that it is one via its parent_id field. A + // NULL parent_id means that the key belongs to the uppermost bucket in + // this table. A constraint on parent_id is enforcing referential + // integrity. + // + // Furthermore, there is a
_p index on parent_id that is required + // for the foreign key constraint. + // + // Finally, there are unique indices on (parent_id, key) to prevent the + // same key being present in a bucket more than once (
_up and + //
_unp). In postgres, a single index wouldn't enforce the unique + // constraint on rows with a NULL parent_id. Therefore, two indices are + // defined. + // + // The replacements map can be used to replace any sqlite keywords. + // Callers should note that the sqlite keywords are case-sensitive. + finalCmd += fmt.Sprintf(` +CREATE TABLE IF NOT EXISTS ` + tableInSchema + ` +( + key BLOB NOT NULL, + value BLOB, + parent_id BIGINT, + id INTEGER PRIMARY KEY, + sequence BIGINT, + CONSTRAINT ` + table + `_parent FOREIGN KEY (parent_id) + REFERENCES ` + tableInSchema + ` (id) + ON UPDATE NO ACTION + ON DELETE CASCADE +); +CREATE INDEX IF NOT EXISTS ` + table + `_p + ON ` + tableInSchema + ` (parent_id); +CREATE UNIQUE INDEX IF NOT EXISTS ` + table + `_up + ON ` + tableInSchema + ` + (parent_id, key) WHERE parent_id IS NOT NULL; +CREATE UNIQUE INDEX IF NOT EXISTS ` + table + `_unp + ON ` + tableInSchema + ` (key) WHERE parent_id IS NULL; +`) + + for from, to := range replacements { + finalCmd = strings.Replace(finalCmd, from, to, -1) + } + + return finalCmd +} From 170160f28a6b8e039bf841df0d806b187f7d7a10 Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Tue, 17 Jan 2023 13:54:12 +0200 Subject: [PATCH 5/8] kvdb+refactor: move all general sqlite code to seprate dir In this commit, all the sql, non-postgres-specific, code is moved out of the postgres package and into a new sqlbase package. This will make it more easily reusable for future sql integrations. --- kvdb/log.go | 4 +- kvdb/postgres/db.go | 263 +---------------- kvdb/postgres/fixture.go | 3 +- kvdb/sqlbase/db.go | 267 ++++++++++++++++++ kvdb/{postgres => sqlbase}/db_conn_set.go | 2 +- kvdb/{postgres => sqlbase}/log.go | 2 +- kvdb/{postgres/no_db.go => sqlbase/no_sql.go} | 2 +- .../{postgres => sqlbase}/readwrite_bucket.go | 2 +- .../{postgres => sqlbase}/readwrite_cursor.go | 2 +- kvdb/{postgres => sqlbase}/readwrite_tx.go | 6 +- kvdb/{postgres => sqlbase}/schema.go | 2 +- 11 files changed, 285 insertions(+), 270 deletions(-) create mode 100644 kvdb/sqlbase/db.go rename kvdb/{postgres => sqlbase}/db_conn_set.go (99%) rename kvdb/{postgres => sqlbase}/log.go (95%) rename kvdb/{postgres/no_db.go => sqlbase/no_sql.go} (78%) rename kvdb/{postgres => sqlbase}/readwrite_bucket.go (99%) rename kvdb/{postgres => sqlbase}/readwrite_cursor.go (99%) rename kvdb/{postgres => sqlbase}/readwrite_tx.go (98%) rename kvdb/{postgres => sqlbase}/schema.go (99%) diff --git a/kvdb/log.go b/kvdb/log.go index 6dc3ad714a0..824715432ce 100644 --- a/kvdb/log.go +++ b/kvdb/log.go @@ -2,7 +2,7 @@ package kvdb import ( "github.com/btcsuite/btclog" - "github.com/lightningnetwork/lnd/kvdb/postgres" + "github.com/lightningnetwork/lnd/kvdb/sqlbase" ) // log is a logger that is initialized as disabled. This means the package will @@ -13,5 +13,5 @@ var log = btclog.Disabled func UseLogger(logger btclog.Logger) { log = logger - postgres.UseLogger(log) + sqlbase.UseLogger(log) } diff --git a/kvdb/postgres/db.go b/kvdb/postgres/db.go index 2c31d46e0bc..90ca8324a8d 100644 --- a/kvdb/postgres/db.go +++ b/kvdb/postgres/db.go @@ -4,269 +4,14 @@ package postgres import ( "context" - "database/sql" - "errors" - "fmt" - "io" - "sync" - "time" "github.com/btcsuite/btcwallet/walletdb" + "github.com/lightningnetwork/lnd/kvdb/sqlbase" ) -const ( - // kvTableName is the name of the table that will contain all the kv - // pairs. - kvTableName = "kv" -) - -// SqlConfig holds a set of configuration options of a sql database connection. -type SqlConfig struct { - // DriverName is the string that defines the registered sql driver that - // is to be used. - DriverName string - - // Dsn is the database connection string that will be used to connect - // to the db. - Dsn string - - // Timeout is the time after which a query to the db will be canceled if - // it has not yet completed. - Timeout time.Duration - - // Schema is the name of the schema under which the sql tables should be - // created. It should be left empty for backends like sqlite that do not - // support having more than one schema. - Schema string - - // TableNamePrefix is the name that should be used as a table name - // prefix when constructing the KV style table. - TableNamePrefix string - - // SQLiteCmdReplacements define a one-to-one string mapping of sql - // keywords to the strings that should replace those keywords in any - // commands. Note that the sqlite keywords to be replaced are - // case-sensitive. - SQLiteCmdReplacements SQLiteCmdReplacements - - // WithTxLevelLock when set will ensure that there is a transaction - // level lock. - WithTxLevelLock bool -} - -// db holds a reference to the postgres connection. -type db struct { - // cfg is the sql db connection config. - cfg *SqlConfig - - // prefix is the table name prefix that is used to simulate namespaces. - // We don't use schemas because at least sqlite does not support that. - prefix string - - // ctx is the overall context for the database driver. - // - // TODO: This is an anti-pattern that is in place until the kvdb - // interface supports a context. - ctx context.Context - - // db is the underlying database connection instance. - db *sql.DB - - // lock is the global write lock that ensures single writer. This is - // only used if cfg.WithTxLevelLock is set. - lock sync.RWMutex - - // table is the name of the table that contains the data for all - // top-level buckets that have keys that cannot be mapped to a distinct - // sql table. - table string -} - -// Enforce db implements the walletdb.DB interface. -var _ walletdb.DB = (*db)(nil) - -var ( - // dbConns is a global set of database connections. - dbConns *dbConnSet - dbConnsMu sync.Mutex -) - -// Init initializes the global set of database connections. -func Init(maxConnections int) { - dbConnsMu.Lock() - defer dbConnsMu.Unlock() - - if dbConns != nil { - return - } - - dbConns = newDbConnSet(maxConnections) -} - -// NewSqlBackend returns a db object initialized with the passed backend -// config. If database connection cannot be established, then returns error. -func NewSqlBackend(ctx context.Context, cfg *SqlConfig) (*db, error) { - dbConnsMu.Lock() - defer dbConnsMu.Unlock() - - if dbConns == nil { - return nil, errors.New("db connection set not initialized") - } - - if cfg.TableNamePrefix == "" { - return nil, errors.New("empty table name prefix") - } - - table := fmt.Sprintf("%s_%s", cfg.TableNamePrefix, kvTableName) - - query := newKVSchemaCreationCmd( - table, cfg.Schema, cfg.SQLiteCmdReplacements, - ) - - dbConn, err := dbConns.Open(cfg.DriverName, cfg.Dsn) - if err != nil { - return nil, err - } - - _, err = dbConn.ExecContext(ctx, query) - if err != nil { - _ = dbConn.Close() - - return nil, err - } - - return &db{ - cfg: cfg, - ctx: ctx, - db: dbConn, - table: table, - prefix: cfg.TableNamePrefix, - }, nil -} - -// getTimeoutCtx gets a timeout context for database requests. -func (db *db) getTimeoutCtx() (context.Context, func()) { - if db.cfg.Timeout == time.Duration(0) { - return db.ctx, func() {} - } - - return context.WithTimeout(db.ctx, db.cfg.Timeout) -} - -// getPrefixedTableName returns a table name for this prefix (namespace). -func (db *db) getPrefixedTableName(table string) string { - return fmt.Sprintf("%s_%s", db.prefix, table) -} - -// catchPanic executes the specified function. If a panic occurs, it is returned -// as an error value. -func catchPanic(f func() error) (err error) { - defer func() { - if r := recover(); r != nil { - log.Criticalf("Caught unhandled error: %v", r) - - switch data := r.(type) { - case error: - err = data - - default: - err = errors.New(fmt.Sprintf("%v", data)) - } - } - }() - - err = f() - - return -} - -// View opens a database read transaction and executes the function f with the -// transaction passed as a parameter. After f exits, the transaction is rolled -// back. If f errors, its error is returned, not a rollback error (if any -// occur). The passed reset function is called before the start of the -// transaction and can be used to reset intermediate state. As callers may -// expect retries of the f closure (depending on the database backend used), the -// reset function will be called before each retry respectively. -func (db *db) View(f func(tx walletdb.ReadTx) error, reset func()) error { - return db.executeTransaction( - func(tx walletdb.ReadWriteTx) error { - return f(tx.(walletdb.ReadTx)) - }, - reset, true, - ) -} - -// Update opens a database read/write transaction and executes the function f -// with the transaction passed as a parameter. After f exits, if f did not -// error, the transaction is committed. Otherwise, if f did error, the -// transaction is rolled back. If the rollback fails, the original error -// returned by f is still returned. If the commit fails, the commit error is -// returned. As callers may expect retries of the f closure, the reset function -// will be called before each retry respectively. -func (db *db) Update(f func(tx walletdb.ReadWriteTx) error, reset func()) (err error) { - return db.executeTransaction(f, reset, false) -} - -// executeTransaction creates a new read-only or read-write transaction and -// executes the given function within it. -func (db *db) executeTransaction(f func(tx walletdb.ReadWriteTx) error, - reset func(), readOnly bool) error { - - reset() - - tx, err := newReadWriteTx(db, readOnly) - if err != nil { - return err - } - - err = catchPanic(func() error { return f(tx) }) - if err != nil { - if rollbackErr := tx.Rollback(); rollbackErr != nil { - log.Errorf("Error rolling back tx: %v", rollbackErr) - } - - return err - } - - return tx.Commit() -} - -// PrintStats returns all collected stats pretty printed into a string. -func (db *db) PrintStats() string { - return "stats not supported by Postgres driver" -} - -// BeginReadWriteTx opens a database read+write transaction. -func (db *db) BeginReadWriteTx() (walletdb.ReadWriteTx, error) { - return newReadWriteTx(db, false) -} - -// BeginReadTx opens a database read transaction. -func (db *db) BeginReadTx() (walletdb.ReadTx, error) { - return newReadWriteTx(db, true) -} - -// Copy writes a copy of the database to the provided writer. This call will -// start a read-only transaction to perform all operations. -// This function is part of the walletdb.Db interface implementation. -func (db *db) Copy(w io.Writer) error { - return errors.New("not implemented") -} - -// Close cleanly shuts down the database and syncs all data. -// This function is part of the walletdb.Db interface implementation. -func (db *db) Close() error { - dbConnsMu.Lock() - defer dbConnsMu.Unlock() - - log.Infof("Closing database %v", db.prefix) - - return dbConns.Close(db.cfg.Dsn) -} - // sqliteCmdReplacements defines a mapping from some SQLite keywords and phrases // to their postgres counterparts. -var sqliteCmdReplacements = SQLiteCmdReplacements{ +var sqliteCmdReplacements = sqlbase.SQLiteCmdReplacements{ "BLOB": "BYTEA", "INTEGER PRIMARY KEY": "BIGSERIAL PRIMARY KEY", } @@ -276,7 +21,7 @@ var sqliteCmdReplacements = SQLiteCmdReplacements{ func newPostgresBackend(ctx context.Context, config *Config, prefix string) ( walletdb.DB, error) { - cfg := &SqlConfig{ + cfg := &sqlbase.Config{ DriverName: "pgx", Dsn: config.Dsn, Timeout: config.Timeout, @@ -286,5 +31,5 @@ func newPostgresBackend(ctx context.Context, config *Config, prefix string) ( WithTxLevelLock: true, } - return NewSqlBackend(ctx, cfg) + return sqlbase.NewSqlBackend(ctx, cfg) } diff --git a/kvdb/postgres/fixture.go b/kvdb/postgres/fixture.go index c7e2b014fc8..26d95c33d34 100644 --- a/kvdb/postgres/fixture.go +++ b/kvdb/postgres/fixture.go @@ -13,6 +13,7 @@ import ( "github.com/btcsuite/btcwallet/walletdb" embeddedpostgres "github.com/fergusstrange/embedded-postgres" + "github.com/lightningnetwork/lnd/kvdb/sqlbase" ) const ( @@ -32,7 +33,7 @@ const testMaxConnections = 50 // to be done once, because NewFixture will create random new databases on every // call. It returns a stop closure that stops the database if called. func StartEmbeddedPostgres() (func() error, error) { - Init(testMaxConnections) + sqlbase.Init(testMaxConnections) postgres := embeddedpostgres.NewDatabase( embeddedpostgres.DefaultConfig(). diff --git a/kvdb/sqlbase/db.go b/kvdb/sqlbase/db.go new file mode 100644 index 00000000000..3e5d3c8e1d1 --- /dev/null +++ b/kvdb/sqlbase/db.go @@ -0,0 +1,267 @@ +//go:build kvdb_postgres + +package sqlbase + +import ( + "context" + "database/sql" + "errors" + "fmt" + "io" + "sync" + "time" + + "github.com/btcsuite/btcwallet/walletdb" +) + +const ( + // kvTableName is the name of the table that will contain all the kv + // pairs. + kvTableName = "kv" +) + +// Config holds a set of configuration options of a sql database connection. +type Config struct { + // DriverName is the string that defines the registered sql driver that + // is to be used. + DriverName string + + // Dsn is the database connection string that will be used to connect + // to the db. + Dsn string + + // Timeout is the time after which a query to the db will be canceled if + // it has not yet completed. + Timeout time.Duration + + // Schema is the name of the schema under which the sql tables should be + // created. It should be left empty for backends like sqlite that do not + // support having more than one schema. + Schema string + + // TableNamePrefix is the name that should be used as a table name + // prefix when constructing the KV style table. + TableNamePrefix string + + // SQLiteCmdReplacements define a one-to-one string mapping of sql + // keywords to the strings that should replace those keywords in any + // commands. Note that the sqlite keywords to be replaced are + // case-sensitive. + SQLiteCmdReplacements SQLiteCmdReplacements + + // WithTxLevelLock when set will ensure that there is a transaction + // level lock. + WithTxLevelLock bool +} + +// db holds a reference to the sql db connection. +type db struct { + // cfg is the sql db connection config. + cfg *Config + + // prefix is the table name prefix that is used to simulate namespaces. + // We don't use schemas because at least sqlite does not support that. + prefix string + + // ctx is the overall context for the database driver. + // + // TODO: This is an anti-pattern that is in place until the kvdb + // interface supports a context. + ctx context.Context + + // db is the underlying database connection instance. + db *sql.DB + + // lock is the global write lock that ensures single writer. This is + // only used if cfg.WithTxLevelLock is set. + lock sync.RWMutex + + // table is the name of the table that contains the data for all + // top-level buckets that have keys that cannot be mapped to a distinct + // sql table. + table string +} + +// Enforce db implements the walletdb.DB interface. +var _ walletdb.DB = (*db)(nil) + +var ( + // dbConns is a global set of database connections. + dbConns *dbConnSet + dbConnsMu sync.Mutex +) + +// Init initializes the global set of database connections. +func Init(maxConnections int) { + dbConnsMu.Lock() + defer dbConnsMu.Unlock() + + if dbConns != nil { + return + } + + dbConns = newDbConnSet(maxConnections) +} + +// NewSqlBackend returns a db object initialized with the passed backend +// config. If database connection cannot be established, then returns error. +func NewSqlBackend(ctx context.Context, cfg *Config) (*db, error) { + dbConnsMu.Lock() + defer dbConnsMu.Unlock() + + if dbConns == nil { + return nil, errors.New("db connection set not initialized") + } + + if cfg.TableNamePrefix == "" { + return nil, errors.New("empty table name prefix") + } + + table := fmt.Sprintf("%s_%s", cfg.TableNamePrefix, kvTableName) + + query := newKVSchemaCreationCmd( + table, cfg.Schema, cfg.SQLiteCmdReplacements, + ) + + dbConn, err := dbConns.Open(cfg.DriverName, cfg.Dsn) + if err != nil { + return nil, err + } + + _, err = dbConn.ExecContext(ctx, query) + if err != nil { + _ = dbConn.Close() + + return nil, err + } + + return &db{ + cfg: cfg, + ctx: ctx, + db: dbConn, + table: table, + prefix: cfg.TableNamePrefix, + }, nil +} + +// getTimeoutCtx gets a timeout context for database requests. +func (db *db) getTimeoutCtx() (context.Context, func()) { + if db.cfg.Timeout == time.Duration(0) { + return db.ctx, func() {} + } + + return context.WithTimeout(db.ctx, db.cfg.Timeout) +} + +// getPrefixedTableName returns a table name for this prefix (namespace). +func (db *db) getPrefixedTableName(table string) string { + return fmt.Sprintf("%s_%s", db.prefix, table) +} + +// catchPanic executes the specified function. If a panic occurs, it is returned +// as an error value. +func catchPanic(f func() error) (err error) { + defer func() { + if r := recover(); r != nil { + log.Criticalf("Caught unhandled error: %v", r) + + switch data := r.(type) { + case error: + err = data + + default: + err = errors.New(fmt.Sprintf("%v", data)) + } + } + }() + + err = f() + + return +} + +// View opens a database read transaction and executes the function f with the +// transaction passed as a parameter. After f exits, the transaction is rolled +// back. If f errors, its error is returned, not a rollback error (if any +// occur). The passed reset function is called before the start of the +// transaction and can be used to reset intermediate state. As callers may +// expect retries of the f closure (depending on the database backend used), the +// reset function will be called before each retry respectively. +func (db *db) View(f func(tx walletdb.ReadTx) error, reset func()) error { + return db.executeTransaction( + func(tx walletdb.ReadWriteTx) error { + return f(tx.(walletdb.ReadTx)) + }, + reset, true, + ) +} + +// Update opens a database read/write transaction and executes the function f +// with the transaction passed as a parameter. After f exits, if f did not +// error, the transaction is committed. Otherwise, if f did error, the +// transaction is rolled back. If the rollback fails, the original error +// returned by f is still returned. If the commit fails, the commit error is +// returned. As callers may expect retries of the f closure, the reset function +// will be called before each retry respectively. +func (db *db) Update(f func(tx walletdb.ReadWriteTx) error, + reset func()) error { + + return db.executeTransaction(f, reset, false) +} + +// executeTransaction creates a new read-only or read-write transaction and +// executes the given function within it. +func (db *db) executeTransaction(f func(tx walletdb.ReadWriteTx) error, + reset func(), readOnly bool) error { + + reset() + + tx, err := newReadWriteTx(db, readOnly) + if err != nil { + return err + } + + err = catchPanic(func() error { return f(tx) }) + if err != nil { + if rollbackErr := tx.Rollback(); rollbackErr != nil { + log.Errorf("Error rolling back tx: %v", rollbackErr) + } + + return err + } + + return tx.Commit() +} + +// PrintStats returns all collected stats pretty printed into a string. +func (db *db) PrintStats() string { + return "stats not supported by Postgres driver" +} + +// BeginReadWriteTx opens a database read+write transaction. +func (db *db) BeginReadWriteTx() (walletdb.ReadWriteTx, error) { + return newReadWriteTx(db, false) +} + +// BeginReadTx opens a database read transaction. +func (db *db) BeginReadTx() (walletdb.ReadTx, error) { + return newReadWriteTx(db, true) +} + +// Copy writes a copy of the database to the provided writer. This call will +// start a read-only transaction to perform all operations. +// This function is part of the walletdb.Db interface implementation. +func (db *db) Copy(w io.Writer) error { + return errors.New("not implemented") +} + +// Close cleanly shuts down the database and syncs all data. +// This function is part of the walletdb.Db interface implementation. +func (db *db) Close() error { + dbConnsMu.Lock() + defer dbConnsMu.Unlock() + + log.Infof("Closing database %v", db.prefix) + + return dbConns.Close(db.cfg.Dsn) +} diff --git a/kvdb/postgres/db_conn_set.go b/kvdb/sqlbase/db_conn_set.go similarity index 99% rename from kvdb/postgres/db_conn_set.go rename to kvdb/sqlbase/db_conn_set.go index 736f8516bcf..ee360a97397 100644 --- a/kvdb/postgres/db_conn_set.go +++ b/kvdb/sqlbase/db_conn_set.go @@ -1,4 +1,4 @@ -package postgres +package sqlbase import ( "database/sql" diff --git a/kvdb/postgres/log.go b/kvdb/sqlbase/log.go similarity index 95% rename from kvdb/postgres/log.go rename to kvdb/sqlbase/log.go index 5a27e9eac0f..c6cbe6577be 100644 --- a/kvdb/postgres/log.go +++ b/kvdb/sqlbase/log.go @@ -1,4 +1,4 @@ -package postgres +package sqlbase import "github.com/btcsuite/btclog" diff --git a/kvdb/postgres/no_db.go b/kvdb/sqlbase/no_sql.go similarity index 78% rename from kvdb/postgres/no_db.go rename to kvdb/sqlbase/no_sql.go index aa49471ae44..92c002d9e72 100644 --- a/kvdb/postgres/no_db.go +++ b/kvdb/sqlbase/no_sql.go @@ -1,5 +1,5 @@ //go:build !kvdb_postgres -package postgres +package sqlbase func Init(maxConnections int) {} diff --git a/kvdb/postgres/readwrite_bucket.go b/kvdb/sqlbase/readwrite_bucket.go similarity index 99% rename from kvdb/postgres/readwrite_bucket.go rename to kvdb/sqlbase/readwrite_bucket.go index 71856b21397..c1eff687670 100644 --- a/kvdb/postgres/readwrite_bucket.go +++ b/kvdb/sqlbase/readwrite_bucket.go @@ -1,6 +1,6 @@ //go:build kvdb_postgres -package postgres +package sqlbase import ( "database/sql" diff --git a/kvdb/postgres/readwrite_cursor.go b/kvdb/sqlbase/readwrite_cursor.go similarity index 99% rename from kvdb/postgres/readwrite_cursor.go rename to kvdb/sqlbase/readwrite_cursor.go index 67c1a39aef9..bb5a0251e71 100644 --- a/kvdb/postgres/readwrite_cursor.go +++ b/kvdb/sqlbase/readwrite_cursor.go @@ -1,6 +1,6 @@ //go:build kvdb_postgres -package postgres +package sqlbase import ( "database/sql" diff --git a/kvdb/postgres/readwrite_tx.go b/kvdb/sqlbase/readwrite_tx.go similarity index 98% rename from kvdb/postgres/readwrite_tx.go rename to kvdb/sqlbase/readwrite_tx.go index c31b056031c..7e82a1570d0 100644 --- a/kvdb/postgres/readwrite_tx.go +++ b/kvdb/sqlbase/readwrite_tx.go @@ -1,6 +1,6 @@ //go:build kvdb_postgres -package postgres +package sqlbase import ( "context" @@ -110,7 +110,9 @@ func (tx *readWriteTx) ReadWriteBucket(key []byte) walletdb.ReadWriteBucket { // CreateTopLevelBucket creates the top level bucket for a key if it // does not exist. The newly-created bucket it returned. -func (tx *readWriteTx) CreateTopLevelBucket(key []byte) (walletdb.ReadWriteBucket, error) { +func (tx *readWriteTx) CreateTopLevelBucket(key []byte) ( + walletdb.ReadWriteBucket, error) { + if len(key) == 0 { return nil, walletdb.ErrBucketNameRequired } diff --git a/kvdb/postgres/schema.go b/kvdb/sqlbase/schema.go similarity index 99% rename from kvdb/postgres/schema.go rename to kvdb/sqlbase/schema.go index 9276326e355..6cced1c3d7a 100644 --- a/kvdb/postgres/schema.go +++ b/kvdb/sqlbase/schema.go @@ -1,6 +1,6 @@ //go:build kvdb_postgres -package postgres +package sqlbase import ( "fmt" From 3d91bb65f79df6672353def05855a7091ee33d1f Mon Sep 17 00:00:00 2001 From: Chris Geihsler Date: Wed, 14 Dec 2022 10:07:14 +0200 Subject: [PATCH 6/8] kvdb: unify how the db backend for tests is set --- kvdb/backend.go | 30 +++++++++++++++++------------- kvdb/kvdb_etcd.go | 4 ++-- kvdb/kvdb_no_etcd.go | 6 +++--- 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/kvdb/backend.go b/kvdb/backend.go index 7d4cbc36fd3..da2161c1c69 100644 --- a/kvdb/backend.go +++ b/kvdb/backend.go @@ -247,12 +247,17 @@ func updateLastCompactionDate(dbFile string) error { func GetTestBackend(path, name string) (Backend, func(), error) { empty := func() {} + // Note that for tests, we expect only one db backend build flag + // (or none) to be set at a time and thus one of the following switch + // cases should ever be true switch { case PostgresBackend: key := filepath.Join(path, name) keyHash := sha256.Sum256([]byte(key)) - f, err := NewPostgresFixture("test_" + hex.EncodeToString(keyHash[:])) + f, err := NewPostgresFixture("test_" + hex.EncodeToString( + keyHash[:]), + ) if err != nil { return nil, func() {}, err } @@ -260,7 +265,17 @@ func GetTestBackend(path, name string) (Backend, func(), error) { _ = f.DB().Close() }, nil - case TestBackend == BoltBackendName: + case EtcdBackend: + etcdConfig, cancel, err := StartEtcdTestBackend(path, 0, 0, "") + if err != nil { + return nil, empty, err + } + backend, err := Open( + EtcdBackendName, context.TODO(), etcdConfig, + ) + return backend, cancel, err + + default: db, err := GetBoltBackend(&BoltBackendConfig{ DBPath: path, DBFileName: name, @@ -271,17 +286,6 @@ func GetTestBackend(path, name string) (Backend, func(), error) { return nil, nil, err } return db, empty, nil - - case TestBackend == EtcdBackendName: - etcdConfig, cancel, err := StartEtcdTestBackend(path, 0, 0, "") - if err != nil { - return nil, empty, err - } - backend, err := Open( - EtcdBackendName, context.TODO(), etcdConfig, - ) - return backend, cancel, err - } return nil, nil, fmt.Errorf("unknown backend") diff --git a/kvdb/kvdb_etcd.go b/kvdb/kvdb_etcd.go index 017388a3fc5..2e07d015283 100644 --- a/kvdb/kvdb_etcd.go +++ b/kvdb/kvdb_etcd.go @@ -7,9 +7,9 @@ import ( "github.com/lightningnetwork/lnd/kvdb/etcd" ) -// TestBackend is conditionally set to etcd when the kvdb_etcd build tag is +// EtcdBackend is conditionally set to etcd when the kvdb_etcd build tag is // defined, allowing testing our database code with etcd backend. -const TestBackend = EtcdBackendName +const EtcdBackend = true // GetEtcdTestBackend creates an embedded etcd backend for testing // storig the database at the passed path. diff --git a/kvdb/kvdb_no_etcd.go b/kvdb/kvdb_no_etcd.go index ad56a3775fa..79897635b1d 100644 --- a/kvdb/kvdb_no_etcd.go +++ b/kvdb/kvdb_no_etcd.go @@ -9,9 +9,9 @@ import ( "github.com/lightningnetwork/lnd/kvdb/etcd" ) -// TestBackend is conditionally set to bdb when the kvdb_etcd build tag is -// not defined, allowing testing our database code with bolt backend. -const TestBackend = BoltBackendName +// EtcdBackend is conditionally set to false when the kvdb_etcd build tag is not +// defined. This will allow testing of other database backends. +const EtcdBackend = false var errEtcdNotAvailable = fmt.Errorf("etcd backend not available") From 74b9c9ce9a28ca10704be4f63c95559a237d2a0c Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Thu, 15 Dec 2022 18:13:13 +0200 Subject: [PATCH 7/8] kvdb: add sqlite --- kvdb/backend.go | 14 +++++ kvdb/config.go | 5 ++ kvdb/go.mod | 19 ++++++- kvdb/go.sum | 40 ++++++++++++- kvdb/kvdb_no_sqlite.go | 25 ++++++++ kvdb/kvdb_sqlite.go | 39 +++++++++++++ kvdb/sqlbase/db.go | 4 +- kvdb/sqlbase/no_sql.go | 2 +- kvdb/sqlbase/readwrite_bucket.go | 2 +- kvdb/sqlbase/readwrite_cursor.go | 2 +- kvdb/sqlbase/readwrite_tx.go | 2 +- kvdb/sqlbase/schema.go | 2 +- kvdb/sqlite/config.go | 13 +++++ kvdb/sqlite/db.go | 83 +++++++++++++++++++++++++++ kvdb/sqlite/db_test.go | 35 ++++++++++++ kvdb/sqlite/driver.go | 97 ++++++++++++++++++++++++++++++++ 16 files changed, 374 insertions(+), 10 deletions(-) create mode 100644 kvdb/kvdb_no_sqlite.go create mode 100644 kvdb/kvdb_sqlite.go create mode 100644 kvdb/sqlite/config.go create mode 100644 kvdb/sqlite/db.go create mode 100644 kvdb/sqlite/db_test.go create mode 100644 kvdb/sqlite/driver.go diff --git a/kvdb/backend.go b/kvdb/backend.go index da2161c1c69..76599311bbb 100644 --- a/kvdb/backend.go +++ b/kvdb/backend.go @@ -275,6 +275,20 @@ func GetTestBackend(path, name string) (Backend, func(), error) { ) return backend, cancel, err + case SqliteBackend: + dbPath := filepath.Join(path, name) + keyHash := sha256.Sum256([]byte(dbPath)) + sqliteDb, err := StartSqliteTestBackend( + path, name, "test_"+hex.EncodeToString(keyHash[:]), + ) + if err != nil { + return nil, empty, err + } + + return sqliteDb, func() { + _ = sqliteDb.Close() + }, nil + default: db, err := GetBoltBackend(&BoltBackendConfig{ DBPath: path, diff --git a/kvdb/config.go b/kvdb/config.go index b3a92309302..08fcc3ad93d 100644 --- a/kvdb/config.go +++ b/kvdb/config.go @@ -18,6 +18,11 @@ const ( // by a live instance of postgres. PostgresBackendName = "postgres" + // SqliteBackendName is the name of the backend that should be passed + // into kvdb.Create to initialize a new instance of kvdb.Backend backed + // by a live instance of sqlite. + SqliteBackendName = "sqlite" + // DefaultBoltAutoCompactMinAge is the default minimum time that must // have passed since a bolt database file was last compacted for the // compaction to be considered again. diff --git a/kvdb/go.mod b/kvdb/go.mod index a5c91d973a4..baba6ccfe0e 100644 --- a/kvdb/go.mod +++ b/kvdb/go.mod @@ -15,6 +15,7 @@ require ( go.etcd.io/etcd/client/v3 v3.5.0 go.etcd.io/etcd/server/v3 v3.5.0 golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 + modernc.org/sqlite v1.20.3 ) require ( @@ -29,6 +30,7 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/golang/snappy v0.0.4 // indirect + github.com/google/uuid v1.3.0 // indirect github.com/gorilla/websocket v1.4.2 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect @@ -42,10 +44,12 @@ require ( github.com/jackc/pgtype v1.8.1 // indirect github.com/jonboulle/clockwork v0.2.2 // indirect github.com/json-iterator/go v1.1.11 // indirect + github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/klauspost/compress v1.13.6 // indirect github.com/klauspost/pgzip v1.2.5 // indirect github.com/lib/pq v1.10.3 // indirect github.com/lightningnetwork/lnd/ticker v1.0.0 // indirect + github.com/mattn/go-isatty v0.0.16 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect github.com/mholt/archiver/v3 v3.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect @@ -57,6 +61,7 @@ require ( github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.26.0 // indirect github.com/prometheus/procfs v0.6.0 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect github.com/sirupsen/logrus v1.7.0 // indirect github.com/soheilhy/cmux v0.1.5 // indirect github.com/spf13/pflag v1.0.5 // indirect @@ -81,15 +86,27 @@ require ( go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.17.0 // indirect golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect - golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 // indirect + golang.org/x/mod v0.4.2 // indirect + golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect golang.org/x/text v0.3.6 // indirect golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect + golang.org/x/tools v0.1.2 // indirect + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c // indirect google.golang.org/grpc v1.38.0 // indirect google.golang.org/protobuf v1.26.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect + lukechampine.com/uint128 v1.2.0 // indirect + modernc.org/cc/v3 v3.40.0 // indirect + modernc.org/ccgo/v3 v3.16.13 // indirect + modernc.org/libc v1.22.2 // indirect + modernc.org/mathutil v1.5.0 // indirect + modernc.org/memory v1.4.0 // indirect + modernc.org/opt v0.1.3 // indirect + modernc.org/strutil v1.1.3 // indirect + modernc.org/token v1.0.1 // indirect sigs.k8s.io/yaml v1.2.0 // indirect ) diff --git a/kvdb/go.sum b/kvdb/go.sum index aa327cd0314..30ae3ace3e9 100644 --- a/kvdb/go.sum +++ b/kvdb/go.sum @@ -148,14 +148,17 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -247,6 +250,8 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1 github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= @@ -287,6 +292,9 @@ github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mholt/archiver/v3 v3.5.0 h1:nE8gZIrw66cu4osS/U7UW7YDuGMHssxKutU8IfWxwWE= @@ -349,6 +357,8 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= +github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -505,6 +515,7 @@ golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -578,8 +589,9 @@ golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210426080607-c94f62235c83/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -707,6 +719,30 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI= +lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= +modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw= +modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0= +modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw= +modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY= +modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk= +modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM= +modernc.org/libc v1.22.2 h1:4U7v51GyhlWqQmwCHj28Rdq2Yzwk55ovjFrdPjs8Hb0= +modernc.org/libc v1.22.2/go.mod h1:uvQavJ1pZ0hIoC/jfqNoMLURIMhKzINIWypNM17puug= +modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ= +modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/memory v1.4.0 h1:crykUfNSnMAXaOJnnxcSzbUGMqkLWjklJKkBK2nwZwk= +modernc.org/memory v1.4.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= +modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= +modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/sqlite v1.20.3 h1:SqGJMMxjj1PHusLxdYxeQSodg7Jxn9WWkaAQjKrntZs= +modernc.org/sqlite v1.20.3/go.mod h1:zKcGyrICaxNTMEHSr1HQ2GUraP0j+845GYw37+EyT6A= +modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY= +modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= +modernc.org/tcl v1.15.0 h1:oY+JeD11qVVSgVvodMJsu7Edf8tr5E/7tuhF5cNYz34= +modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg= +modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +modernc.org/z v1.7.0 h1:xkDw/KepgEjeizO2sNco+hqYkU12taxQFqPEmgm1GWE= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/kvdb/kvdb_no_sqlite.go b/kvdb/kvdb_no_sqlite.go new file mode 100644 index 00000000000..ff265e6caa7 --- /dev/null +++ b/kvdb/kvdb_no_sqlite.go @@ -0,0 +1,25 @@ +//go:build !kvdb_sqlite || (windows && (arm || 386)) || (linux && (ppc64 || mips || mipsle || mips64)) + +package kvdb + +import ( + "fmt" + "runtime" + + "github.com/btcsuite/btcwallet/walletdb" +) + +var errSqliteNotAvailable = fmt.Errorf("sqlite backend not available either "+ + "due to the `kvdb_sqlite` build tag not being set, or due to this "+ + "OS(%s) and/or architecture(%s) not being supported", runtime.GOOS, + runtime.GOARCH) + +// SqliteBackend is conditionally set to false when the kvdb_sqlite build tag is +// not defined. This will allow testing of other database backends. +const SqliteBackend = false + +// StartSqliteTestBackend is a stub returning nil, and errSqliteNotAvailable +// error. +func StartSqliteTestBackend(path, name, table string) (walletdb.DB, error) { + return nil, errSqliteNotAvailable +} diff --git a/kvdb/kvdb_sqlite.go b/kvdb/kvdb_sqlite.go new file mode 100644 index 00000000000..ec880da63ce --- /dev/null +++ b/kvdb/kvdb_sqlite.go @@ -0,0 +1,39 @@ +//go:build kvdb_sqlite && !(windows && (arm || 386)) && !(linux && (ppc64 || mips || mipsle || mips64)) + +package kvdb + +import ( + "context" + "os" + "time" + + "github.com/btcsuite/btcwallet/walletdb" + "github.com/lightningnetwork/lnd/kvdb/sqlbase" + "github.com/lightningnetwork/lnd/kvdb/sqlite" +) + +const ( + // SqliteBackend is conditionally set to true when the kvdb_sqlite build + // tag is defined. This will allow testing of other database backends. + SqliteBackend = true + + testMaxConnections = 50 +) + +// StartSqliteTestBackend starts a sqlite backed wallet.DB instance +func StartSqliteTestBackend(path, name, table string) (walletdb.DB, error) { + if !fileExists(path) { + err := os.Mkdir(path, 0700) + if err != nil { + return nil, err + } + } + + sqlbase.Init(testMaxConnections) + return sqlite.NewSqliteBackend( + context.Background(), &sqlite.Config{ + Timeout: time.Second * 30, + BusyTimeout: time.Second * 5, + }, path, name, table, + ) +} diff --git a/kvdb/sqlbase/db.go b/kvdb/sqlbase/db.go index 3e5d3c8e1d1..fda628a7400 100644 --- a/kvdb/sqlbase/db.go +++ b/kvdb/sqlbase/db.go @@ -1,4 +1,4 @@ -//go:build kvdb_postgres +//go:build kvdb_postgres || (kvdb_sqlite && !(windows && (arm || 386)) && !(linux && (ppc64 || mips || mipsle || mips64))) package sqlbase @@ -235,7 +235,7 @@ func (db *db) executeTransaction(f func(tx walletdb.ReadWriteTx) error, // PrintStats returns all collected stats pretty printed into a string. func (db *db) PrintStats() string { - return "stats not supported by Postgres driver" + return "stats not supported by SQL driver" } // BeginReadWriteTx opens a database read+write transaction. diff --git a/kvdb/sqlbase/no_sql.go b/kvdb/sqlbase/no_sql.go index 92c002d9e72..6661757820a 100644 --- a/kvdb/sqlbase/no_sql.go +++ b/kvdb/sqlbase/no_sql.go @@ -1,4 +1,4 @@ -//go:build !kvdb_postgres +//go:build !kvdb_postgres && (!kvdb_sqlite || (windows && (arm || 386)) || (linux && (ppc64 || mips || mipsle || mips64))) package sqlbase diff --git a/kvdb/sqlbase/readwrite_bucket.go b/kvdb/sqlbase/readwrite_bucket.go index c1eff687670..f8913723f86 100644 --- a/kvdb/sqlbase/readwrite_bucket.go +++ b/kvdb/sqlbase/readwrite_bucket.go @@ -1,4 +1,4 @@ -//go:build kvdb_postgres +//go:build kvdb_postgres || (kvdb_sqlite && !(windows && (arm || 386)) && !(linux && (ppc64 || mips || mipsle || mips64))) package sqlbase diff --git a/kvdb/sqlbase/readwrite_cursor.go b/kvdb/sqlbase/readwrite_cursor.go index bb5a0251e71..3124f915c0e 100644 --- a/kvdb/sqlbase/readwrite_cursor.go +++ b/kvdb/sqlbase/readwrite_cursor.go @@ -1,4 +1,4 @@ -//go:build kvdb_postgres +//go:build kvdb_postgres || (kvdb_sqlite && !(windows && (arm || 386)) && !(linux && (ppc64 || mips || mipsle || mips64))) package sqlbase diff --git a/kvdb/sqlbase/readwrite_tx.go b/kvdb/sqlbase/readwrite_tx.go index 7e82a1570d0..7286f1db9bb 100644 --- a/kvdb/sqlbase/readwrite_tx.go +++ b/kvdb/sqlbase/readwrite_tx.go @@ -1,4 +1,4 @@ -//go:build kvdb_postgres +//go:build kvdb_postgres || (kvdb_sqlite && !(windows && (arm || 386)) && !(linux && (ppc64 || mips || mipsle || mips64))) package sqlbase diff --git a/kvdb/sqlbase/schema.go b/kvdb/sqlbase/schema.go index 6cced1c3d7a..1ff3aefb942 100644 --- a/kvdb/sqlbase/schema.go +++ b/kvdb/sqlbase/schema.go @@ -1,4 +1,4 @@ -//go:build kvdb_postgres +//go:build kvdb_postgres || (kvdb_sqlite && !(windows && (arm || 386)) && !(linux && (ppc64 || mips || mipsle || mips64))) package sqlbase diff --git a/kvdb/sqlite/config.go b/kvdb/sqlite/config.go new file mode 100644 index 00000000000..f60abcfc009 --- /dev/null +++ b/kvdb/sqlite/config.go @@ -0,0 +1,13 @@ +package sqlite + +import "time" + +// Config holds sqlite configuration data. +// +//nolint:lll +type Config struct { + Timeout time.Duration `long:"timeout" description:"The time after which a database query should be timed out."` + BusyTimeout time.Duration `long:"busytimeout" description:"The maximum amount of time to wait for a database connection to become available for a query."` + MaxConnections int `long:"maxconnections" description:"The maximum number of open connections to the database. Set to zero for unlimited."` + PragmaOptions []string `long:"pragmaoptions" description:"A list of pragma options to set on a database connection. For example, 'auto_vacuum=incremental'. Note that the flag must be specified multiple times if multiple options are to be set."` +} diff --git a/kvdb/sqlite/db.go b/kvdb/sqlite/db.go new file mode 100644 index 00000000000..f04bec3be02 --- /dev/null +++ b/kvdb/sqlite/db.go @@ -0,0 +1,83 @@ +//go:build kvdb_sqlite && !(windows && (arm || 386)) && !(linux && (ppc64 || mips || mipsle || mips64)) + +package sqlite + +import ( + "context" + "fmt" + "net/url" + "path/filepath" + + "github.com/btcsuite/btcwallet/walletdb" + "github.com/lightningnetwork/lnd/kvdb/sqlbase" + _ "modernc.org/sqlite" // Register relevant drivers. +) + +const ( + // sqliteOptionPrefix is the string prefix sqlite uses to set various + // options. This is used in the following format: + // * sqliteOptionPrefix || option_name = option_value. + sqliteOptionPrefix = "_pragma" + + // sqliteTxLockImmediate is a dsn option used to ensure that write + // transactions are started immediately. + sqliteTxLockImmediate = "_txlock=immediate" +) + +// NewSqliteBackend returns a db object initialized with the passed backend +// config. If a sqlite connection cannot be established, then an error is +// returned. +func NewSqliteBackend(ctx context.Context, cfg *Config, dbPath, fileName, + prefix string) (walletdb.DB, error) { + + // First, we add a set of mandatory pragma options to the query. + pragmaOptions := []struct { + name string + value string + }{ + { + name: "busy_timeout", + value: fmt.Sprintf( + "%d", cfg.BusyTimeout.Milliseconds(), + ), + }, + { + name: "foreign_keys", + value: "on", + }, + { + name: "journal_mode", + value: "WAL", + }, + } + sqliteOptions := make(url.Values) + for _, option := range pragmaOptions { + sqliteOptions.Add( + sqliteOptionPrefix, + fmt.Sprintf("%v=%v", option.name, option.value), + ) + } + + // Then we add any user specified pragma options. Note that these can + // be of the form: "key=value", "key(N)" or "key". + for _, option := range cfg.PragmaOptions { + sqliteOptions.Add(sqliteOptionPrefix, option) + } + + // Construct the DSN which is just the database file name, appended + // with the series of pragma options as a query URL string. For more + // details on the formatting here, see the modernc.org/sqlite docs: + // https://pkg.go.dev/modernc.org/sqlite#Driver.Open. + dsn := fmt.Sprintf( + "%v?%v&%v", filepath.Join(dbPath, fileName), + sqliteOptions.Encode(), sqliteTxLockImmediate, + ) + sqlCfg := &sqlbase.Config{ + DriverName: "sqlite", + Dsn: dsn, + Timeout: cfg.Timeout, + TableNamePrefix: prefix, + } + + return sqlbase.NewSqlBackend(ctx, sqlCfg) +} diff --git a/kvdb/sqlite/db_test.go b/kvdb/sqlite/db_test.go new file mode 100644 index 00000000000..f12acf47f7c --- /dev/null +++ b/kvdb/sqlite/db_test.go @@ -0,0 +1,35 @@ +//go:build kvdb_sqlite && !(windows && (arm || 386)) && !(linux && (ppc64 || mips || mipsle || mips64)) + +package sqlite + +import ( + "testing" + "time" + + "github.com/btcsuite/btcwallet/walletdb/walletdbtest" + "github.com/lightningnetwork/lnd/kvdb/sqlbase" + "github.com/stretchr/testify/require" + "golang.org/x/net/context" +) + +// TestInterface performs all interfaces tests for this database driver. +func TestInterface(t *testing.T) { + // dbType is the database type name for this driver. + dir := t.TempDir() + ctx := context.Background() + + sqlbase.Init(0) + + cfg := &Config{ + BusyTimeout: time.Second * 5, + } + + sqlDB, err := NewSqliteBackend(ctx, cfg, dir, "tmp.db", "table") + require.NoError(t, err) + + t.Cleanup(func() { + require.NoError(t, sqlDB.Close()) + }) + + walletdbtest.TestInterface(t, dbType, ctx, cfg, dir, "tmp.db", "temp") +} diff --git a/kvdb/sqlite/driver.go b/kvdb/sqlite/driver.go new file mode 100644 index 00000000000..efd7892b18d --- /dev/null +++ b/kvdb/sqlite/driver.go @@ -0,0 +1,97 @@ +//go:build kvdb_sqlite && !(windows && (arm || 386)) && !(linux && (ppc64 || mips || mipsle || mips64)) + +package sqlite + +import ( + "context" + "fmt" + + "github.com/btcsuite/btcwallet/walletdb" +) + +const ( + dbType = "sqlite" +) + +// parseArgs parses the arguments from the walletdb Open/Create methods. +func parseArgs(funcName string, args ...interface{}) (context.Context, *Config, + string, string, string, error) { + + if len(args) != 5 { + return nil, nil, "", "", "", fmt.Errorf("invalid number of "+ + "arguments to %s.%s -- expected: context.Context, "+ + "sql.Config, string, string, string", dbType, funcName) + } + + ctx, ok := args[0].(context.Context) + if !ok { + return nil, nil, "", "", "", fmt.Errorf("argument 0 to %s.%s "+ + "is invalid -- expected: context.Context", dbType, + funcName) + } + + config, ok := args[1].(*Config) + if !ok { + return nil, nil, "", "", "", fmt.Errorf("argument 1 to %s.%s "+ + "is invalid -- expected: sqlite.Config", dbType, + funcName) + } + + dbPath, ok := args[2].(string) + if !ok { + return nil, nil, "", "", "", fmt.Errorf("argument 2 to %s.%s "+ + "is invalid -- expected string", dbType, dbPath) + } + + fileName, ok := args[3].(string) + if !ok { + return nil, nil, "", "", "", fmt.Errorf("argument 3 to %s.%s "+ + "is invalid -- expected string", dbType, funcName) + } + + prefix, ok := args[4].(string) + if !ok { + return nil, nil, "", "", "", fmt.Errorf("argument 4 to %s.%s "+ + "is invalid -- expected string", dbType, funcName, + ) + } + + return ctx, config, dbPath, fileName, prefix, nil +} + +// createDBDriver is the callback provided during driver registration that +// creates, initializes, and opens a database for use. +func createDBDriver(args ...interface{}) (walletdb.DB, error) { + ctx, config, dbPath, filename, prefix, err := parseArgs( + "Create", args..., + ) + if err != nil { + return nil, err + } + + return NewSqliteBackend(ctx, config, dbPath, filename, prefix) +} + +// openDBDriver is the callback provided during driver registration that opens +// an existing database for use. +func openDBDriver(args ...interface{}) (walletdb.DB, error) { + ctx, config, dbPath, filename, prefix, err := parseArgs("Open", args...) + if err != nil { + return nil, err + } + + return NewSqliteBackend(ctx, config, dbPath, filename, prefix) +} + +func init() { + // Register the driver. + driver := walletdb.Driver{ + DbType: dbType, + Create: createDBDriver, + Open: openDBDriver, + } + if err := walletdb.RegisterDriver(driver); err != nil { + panic(fmt.Sprintf("Failed to regiser database driver '%s': %v", + dbType, err)) + } +} From 43bc2737c1e5bb97a2bc9cb5df04eee96a0270a0 Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Wed, 14 Dec 2022 20:16:48 +0200 Subject: [PATCH 8/8] docs: update release notes --- docs/release-notes/release-notes-0.16.0.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/release-notes/release-notes-0.16.0.md b/docs/release-notes/release-notes-0.16.0.md index ed27b2ebdf8..f31e2c8e061 100644 --- a/docs/release-notes/release-notes-0.16.0.md +++ b/docs/release-notes/release-notes-0.16.0.md @@ -330,6 +330,12 @@ Keysend](https://github.com/lightningnetwork/lnd/pull/7334). * [Store AckedUpdates in a more compact way](https://github.com/lightningnetwork/lnd/pull/7055) +## DB + +* [Add a sqlite backend + option](https://github.com/lightningnetwork/lnd/pull/7251) to the kvdb + package. + ## Pathfinding * [Pathfinding takes capacity of edges into account to improve success @@ -389,6 +395,7 @@ refactor the itest for code health and maintenance. * Antoni Spaanderman * Carla Kirk-Cohen * Carsten Otto +* Chris Geihsler * Conner Babinchak * cutiful * Daniel McNally