mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-01-19 05:45:21 +01:00
commit
266cd97573
@ -333,6 +333,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
|
||||
@ -392,6 +398,7 @@ refactor the itest for code health and maintenance.
|
||||
* Antoni Spaanderman
|
||||
* Carla Kirk-Cohen
|
||||
* Carsten Otto
|
||||
* Chris Geihsler
|
||||
* Conner Babinchak
|
||||
* cutiful
|
||||
* Daniel McNally
|
||||
|
@ -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,31 @@ 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
|
||||
|
||||
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,
|
||||
DBFileName: name,
|
||||
@ -271,17 +300,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")
|
||||
|
@ -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.
|
||||
|
103
kvdb/go.mod
103
kvdb/go.mod
@ -1,28 +1,113 @@
|
||||
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
|
||||
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 (
|
||||
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/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
|
||||
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/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
|
||||
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/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
|
||||
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/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
|
||||
)
|
||||
|
||||
// This replace is for https://github.com/advisories/GHSA-w73w-5m7g-f7qc
|
||||
@ -35,4 +120,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
|
||||
|
44
kvdb/go.sum
44
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=
|
||||
@ -150,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=
|
||||
@ -192,7 +193,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 +213,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=
|
||||
@ -251,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=
|
||||
@ -291,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=
|
||||
@ -353,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=
|
||||
@ -509,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=
|
||||
@ -582,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=
|
||||
@ -711,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=
|
||||
|
@ -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.
|
||||
|
@ -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")
|
||||
|
||||
|
25
kvdb/kvdb_no_sqlite.go
Normal file
25
kvdb/kvdb_no_sqlite.go
Normal file
@ -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
|
||||
}
|
39
kvdb/kvdb_sqlite.go
Normal file
39
kvdb/kvdb_sqlite.go
Normal file
@ -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,
|
||||
)
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
@ -1,262 +1,35 @@
|
||||
//go:build kvdb_postgres
|
||||
// +build kvdb_postgres
|
||||
|
||||
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"
|
||||
)
|
||||
|
||||
// KV stores a key/value pair.
|
||||
type KV struct {
|
||||
key string
|
||||
val string
|
||||
}
|
||||
|
||||
// db holds a reference to the postgres connection connection.
|
||||
type db struct {
|
||||
// cfg is the postgres 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.
|
||||
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)
|
||||
|
||||
// Global set of database connections.
|
||||
var dbConns *dbConnSet
|
||||
|
||||
// Init initializes the global set of database connections.
|
||||
func Init(maxConnections int) {
|
||||
dbConns = newDbConnSet(maxConnections)
|
||||
// sqliteCmdReplacements defines a mapping from some SQLite keywords and phrases
|
||||
// to their postgres counterparts.
|
||||
var sqliteCmdReplacements = sqlbase.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 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) {
|
||||
walletdb.DB, error) {
|
||||
|
||||
if prefix == "" {
|
||||
return nil, errors.New("empty postgres prefix")
|
||||
cfg := &sqlbase.Config{
|
||||
DriverName: "pgx",
|
||||
Dsn: config.Dsn,
|
||||
Timeout: config.Timeout,
|
||||
Schema: "public",
|
||||
TableNamePrefix: prefix,
|
||||
SQLiteCmdReplacements: sqliteCmdReplacements,
|
||||
WithTxLevelLock: true,
|
||||
}
|
||||
|
||||
if dbConns == nil {
|
||||
return nil, errors.New("db connection set not initialized")
|
||||
}
|
||||
|
||||
dbConn, err := dbConns.Open(config.Dsn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Compose system table names.
|
||||
table := fmt.Sprintf(
|
||||
"%s_%s", prefix, kvTableName,
|
||||
)
|
||||
|
||||
// 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 <table>_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 (<table>_up and
|
||||
// <table>_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;
|
||||
`)
|
||||
if err != nil {
|
||||
_ = dbConn.Close()
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
backend := &db{
|
||||
cfg: config,
|
||||
prefix: prefix,
|
||||
ctx: ctx,
|
||||
db: dbConn,
|
||||
table: table,
|
||||
}
|
||||
|
||||
return backend, 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 {
|
||||
log.Infof("Closing database %v", db.prefix)
|
||||
|
||||
return dbConns.Close(db.cfg.Dsn)
|
||||
return sqlbase.NewSqlBackend(ctx, cfg)
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
//go:build kvdb_postgres
|
||||
// +build kvdb_postgres
|
||||
|
||||
package postgres
|
||||
|
||||
@ -42,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)
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
//go:build kvdb_postgres
|
||||
// +build kvdb_postgres
|
||||
|
||||
package postgres
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
//go:build kvdb_postgres
|
||||
// +build kvdb_postgres
|
||||
|
||||
package postgres
|
||||
|
||||
@ -14,6 +13,7 @@ import (
|
||||
|
||||
"github.com/btcsuite/btcwallet/walletdb"
|
||||
embeddedpostgres "github.com/fergusstrange/embedded-postgres"
|
||||
"github.com/lightningnetwork/lnd/kvdb/sqlbase"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -33,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().
|
||||
|
@ -1,6 +0,0 @@
|
||||
//go:build !kvdb_postgres
|
||||
// +build !kvdb_postgres
|
||||
|
||||
package postgres
|
||||
|
||||
func Init(maxConnections int) {}
|
267
kvdb/sqlbase/db.go
Normal file
267
kvdb/sqlbase/db.go
Normal file
@ -0,0 +1,267 @@
|
||||
//go:build kvdb_postgres || (kvdb_sqlite && !(windows && (arm || 386)) && !(linux && (ppc64 || mips || mipsle || mips64)))
|
||||
|
||||
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 SQL 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)
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package postgres
|
||||
package sqlbase
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
@ -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.
|
||||
@ -32,9 +33,9 @@ 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()
|
||||
func (d *dbConnSet) Open(driver, dsn string) (*sql.DB, error) {
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
|
||||
if dbConn, ok := d.dbConn[dsn]; ok {
|
||||
dbConn.count++
|
||||
@ -42,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
|
||||
}
|
||||
@ -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 {
|
@ -1,4 +1,4 @@
|
||||
package postgres
|
||||
package sqlbase
|
||||
|
||||
import "github.com/btcsuite/btclog"
|
||||
|
5
kvdb/sqlbase/no_sql.go
Normal file
5
kvdb/sqlbase/no_sql.go
Normal file
@ -0,0 +1,5 @@
|
||||
//go:build !kvdb_postgres && (!kvdb_sqlite || (windows && (arm || 386)) || (linux && (ppc64 || mips || mipsle || mips64)))
|
||||
|
||||
package sqlbase
|
||||
|
||||
func Init(maxConnections int) {}
|
@ -1,7 +1,6 @@
|
||||
//go:build kvdb_postgres
|
||||
// +build kvdb_postgres
|
||||
//go:build kvdb_postgres || (kvdb_sqlite && !(windows && (arm || 386)) && !(linux && (ppc64 || mips || mipsle || mips64)))
|
||||
|
||||
package postgres
|
||||
package sqlbase
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
@ -90,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
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
//go:build kvdb_postgres
|
||||
// +build kvdb_postgres
|
||||
//go:build kvdb_postgres || (kvdb_sqlite && !(windows && (arm || 386)) && !(linux && (ppc64 || mips || mipsle || mips64)))
|
||||
|
||||
package postgres
|
||||
package sqlbase
|
||||
|
||||
import (
|
||||
"database/sql"
|
@ -1,7 +1,6 @@
|
||||
//go:build kvdb_postgres
|
||||
// +build kvdb_postgres
|
||||
//go:build kvdb_postgres || (kvdb_sqlite && !(windows && (arm || 386)) && !(linux && (ppc64 || mips || mipsle || mips64)))
|
||||
|
||||
package postgres
|
||||
package sqlbase
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -29,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()
|
||||
|
||||
@ -108,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
|
||||
}
|
||||
@ -199,3 +203,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)
|
74
kvdb/sqlbase/schema.go
Normal file
74
kvdb/sqlbase/schema.go
Normal file
@ -0,0 +1,74 @@
|
||||
//go:build kvdb_postgres || (kvdb_sqlite && !(windows && (arm || 386)) && !(linux && (ppc64 || mips || mipsle || mips64)))
|
||||
|
||||
package sqlbase
|
||||
|
||||
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 <table>_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 (<table>_up and
|
||||
// <table>_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
|
||||
}
|
13
kvdb/sqlite/config.go
Normal file
13
kvdb/sqlite/config.go
Normal file
@ -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."`
|
||||
}
|
83
kvdb/sqlite/db.go
Normal file
83
kvdb/sqlite/db.go
Normal file
@ -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)
|
||||
}
|
35
kvdb/sqlite/db_test.go
Normal file
35
kvdb/sqlite/db_test.go
Normal file
@ -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")
|
||||
}
|
97
kvdb/sqlite/driver.go
Normal file
97
kvdb/sqlite/driver.go
Normal file
@ -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))
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user