Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

multi: add sqlite backend option #7252

Merged
merged 7 commits into from
Jan 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ jobs:
- btcd unit-cover
- unit tags="kvdb_etcd"
- unit tags="kvdb_postgres"
- unit tags="kvdb_sqlite"
- btcd unit-race
steps:
- name: git checkout
Expand Down Expand Up @@ -204,6 +205,8 @@ jobs:
args: backend=bitcoind dbbackend=etcd
- name: bitcoind-postgres
args: backend=bitcoind dbbackend=postgres
- name: bitcoind-sqlite
args: backend=bitcoind dbbackend=sqlite
- name: neutrino
args: backend=neutrino
steps:
Expand Down Expand Up @@ -287,6 +290,8 @@ jobs:
args: backend=bitcoind dbbackend=etcd
- name: bitcoind-postgres
args: backend=bitcoind dbbackend=postgres
- name: bitcoind-sqlite
args: backend=bitcoind dbbackend=sqlite
- name: neutrino
args: backend=neutrino
steps:
Expand Down Expand Up @@ -360,7 +365,7 @@ jobs:
fail-fast: false
matrix:
pinned_dep:
- google.golang.org/grpc v1.38.0
- google.golang.org/grpc v1.41.0
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Phew, I think this might just work out without any issues... But the next version will give us problems I think: lightninglabs/aperture#62
So probably this is the last "painless" etcd upgrade 😬

- github.com/golang/protobuf v1.5.2

steps:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ jobs:

Our release binaries are fully reproducible. Third parties are able to verify that the release binaries were produced properly without having to trust the release manager(s). See our [reproducible builds guide](https://github.com/lightningnetwork/lnd/tree/master/build/release) for how this can be achieved.
The release binaries are compiled with `go${{ env.GO_VERSION }}`, which is required by verifiers to arrive at the same ones.
They include the following build tags: `autopilotrpc`, `signrpc`, `walletrpc`, `chainrpc`, `invoicesrpc`, `neutrinorpc`, `routerrpc`, `watchtowerrpc`, `monitoring`, `peersrpc`, `kvdb_postrgres`, and `kvdb_etcd`. Note that these are already included in the release script, so they do not need to be provided.
They include the following build tags: `autopilotrpc`, `signrpc`, `walletrpc`, `chainrpc`, `invoicesrpc`, `neutrinorpc`, `routerrpc`, `watchtowerrpc`, `monitoring`, `peersrpc`, `kvdb_postrgres`, `kvdb_etcd` and `kvdb_sqlite`. Note that these are already included in the release script, so they do not need to be provided.

The `make release` command can be used to ensure one rebuilds with all the same flags used for the release. If one wishes to build for only a single platform, then `make release sys=<OS-ARCH> tag=<tag>` can be used.

Expand Down
1 change: 1 addition & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ run:
- watchtowerrpc
- kvdb_etcd
- kvdb_postgres
- kvdb_sqlite

linters-settings:
govet:
Expand Down
49 changes: 48 additions & 1 deletion config.go
Original file line number Diff line number Diff line change
Expand Up @@ -1404,12 +1404,18 @@ func ValidateConfig(cfg Config, interceptor signal.Interceptor, fileParser,
)
}

towerDir := filepath.Join(
cfg.Watchtower.TowerDir,
cfg.registeredChains.PrimaryChain().String(),
lncfg.NormalizeNetwork(cfg.ActiveNetParams.Name),
)

// Create the lnd directory and all other sub-directories if they don't
// already exist. This makes sure that directory trees are also created
// for files that point to outside the lnddir.
dirs := []string{
lndDir, cfg.DataDir, cfg.networkDir,
cfg.LetsEncryptDir, cfg.Watchtower.TowerDir,
cfg.LetsEncryptDir, towerDir, cfg.graphDatabaseDir(),
filepath.Dir(cfg.TLSCertPath), filepath.Dir(cfg.TLSKeyPath),
filepath.Dir(cfg.AdminMacPath), filepath.Dir(cfg.ReadMacPath),
filepath.Dir(cfg.InvoiceMacPath),
Expand Down Expand Up @@ -1616,6 +1622,47 @@ func ValidateConfig(cfg Config, interceptor signal.Interceptor, fileParser,
cfg.DB.Bolt.NoFreelistSync = !cfg.SyncFreelist
}

// Parse any extra sqlite pragma options that may have been provided
// to determine if they override any of the defaults that we will
// otherwise add.
var (
defaultSynchronous = true
defaultAutoVacuum = true
defaultFullfsync = true
)
for _, option := range cfg.DB.Sqlite.PragmaOptions {
switch {
case strings.HasPrefix(option, "synchronous="):
defaultSynchronous = false

case strings.HasPrefix(option, "auto_vacuum="):
defaultAutoVacuum = false

case strings.HasPrefix(option, "fullfsync="):
defaultFullfsync = false

default:
}
}

if defaultSynchronous {
cfg.DB.Sqlite.PragmaOptions = append(
cfg.DB.Sqlite.PragmaOptions, "synchronous=full",
)
}

if defaultAutoVacuum {
cfg.DB.Sqlite.PragmaOptions = append(
cfg.DB.Sqlite.PragmaOptions, "auto_vacuum=incremental",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apparently auto_vacuum does not reclaim deleted rows (https://www.sqlite.org/pragma.html#pragma_auto_vacuum). What's your thoughts on this? It seems that if you close a channel and want to get the disk space back, you still need a manual vacuum. Should it be an option to perform this on startup, similar to bbolt auto compaction?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes. it is actually the incremental_vacuum flag that must be set (not vacuum as that only needs to be run if auto_vacuum is added after the first connection). But since we have exposed PragmaOptions to the users, they already have the ability to set this.

However, I forgot to add this to the docs, so I will add a note about it there 👍

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah - also just realised not all pragma options are kv pairs. So need to update the config to allow for non kv pairs

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated the docs and removed the "kv" form requirement 👍

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to get vacuum to clean up a test db, but didn't succeed.

This is what I did:

~ sqlite3 /tmp/test.db
SQLite version 3.37.0 2021-12-09 01:34:53
Enter ".help" for usage hints.
sqlite> pragma auto_vacuum=incremental;
sqlite> pragma incremental_vacuum;
sqlite> create table x (key blob primary key);
sqlite> insert into x (key) values (readfile('Downloads/vlc-3.0.17.3-arm64.dmg'));
sqlite> delete from x;

The db file is still big at this point. Only issuing vacuum; brings it back to a tiny size.

Am I missing something?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah right, after all this, I need to do pragma incremental_vacuum again and that cleans it up. All clear 👍

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I think we want to eventually have the current compact flag effectively just run vacuum.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Too bad there is no real auto-vacuum in sqlite.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah right, after all this, I need to do pragma incremental_vacuum again and that cleans it up. All clear

Cool - yeah the pragma incremental_vacuum only does something once there is something to vacuum. It is not a set-and-forget pragma

)
}
joostjager marked this conversation as resolved.
Show resolved Hide resolved

if defaultFullfsync {
cfg.DB.Sqlite.PragmaOptions = append(
cfg.DB.Sqlite.PragmaOptions, "fullfsync=true",
)
}

// Ensure that the user hasn't chosen a remote-max-htlc value greater
// than the protocol maximum.
maxRemoteHtlcs := uint16(input.MaxHTLCNumber / 2)
Expand Down
29 changes: 21 additions & 8 deletions config_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ func (d *DefaultWalletImpl) BuildWalletConfig(ctx context.Context,
var neutrinoCS *neutrino.ChainService
if mainChain.Node == "neutrino" {
neutrinoBackend, neutrinoCleanUp, err := initNeutrinoBackend(
d.cfg, mainChain.ChainDir, blockCache,
ctx, d.cfg, mainChain.ChainDir, blockCache,
)
if err != nil {
err := fmt.Errorf("unable to initialize neutrino "+
Expand Down Expand Up @@ -830,7 +830,7 @@ func (d *DefaultDatabaseBuilder) BuildDatabase(
cfg.Watchtower.TowerDir,
cfg.registeredChains.PrimaryChain().String(),
lncfg.NormalizeNetwork(cfg.ActiveNetParams.Name),
), cfg.WtClient.Active, cfg.Watchtower.Active,
), cfg.WtClient.Active, cfg.Watchtower.Active, d.logger,
)
if err != nil {
return nil, nil, fmt.Errorf("unable to obtain database "+
Expand Down Expand Up @@ -1173,7 +1173,7 @@ func importWatchOnlyAccounts(wallet *wallet.Wallet,

// initNeutrinoBackend inits a new instance of the neutrino light client
// backend given a target chain directory to store the chain state.
func initNeutrinoBackend(cfg *Config, chainDir string,
func initNeutrinoBackend(ctx context.Context, cfg *Config, chainDir string,
blockCache *blockcache.BlockCache) (*neutrino.ChainService,
func(), error) {

Expand All @@ -1200,13 +1200,26 @@ func initNeutrinoBackend(cfg *Config, chainDir string,
return nil, nil, err
}

dbName := filepath.Join(dbPath, "neutrino.db")
db, err := walletdb.Create(
"bdb", dbName, !cfg.SyncFreelist, cfg.DB.Bolt.DBTimeout,
var (
db walletdb.DB
err error
)
switch {
case cfg.DB.Backend == kvdb.SqliteBackendName:
ellemouton marked this conversation as resolved.
Show resolved Hide resolved
db, err = kvdb.Open(
kvdb.SqliteBackendName, ctx, cfg.DB.Sqlite, dbPath,
lncfg.SqliteNeutrinoDBName, lncfg.NSNeutrinoDB,
)

default:
dbName := filepath.Join(dbPath, "neutrino.db")
db, err = walletdb.Create(
"bdb", dbName, !cfg.SyncFreelist, cfg.DB.Bolt.DBTimeout,
)
}
if err != nil {
return nil, nil, fmt.Errorf("unable to create neutrino "+
"database: %v", err)
return nil, nil, fmt.Errorf("unable to create "+
"neutrino database: %v", err)
}

headerStateAssertion, err := parseHeaderStateAssertion(
Expand Down
1 change: 1 addition & 0 deletions docs/INSTALL.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ will have the following tags:
- [monitoring](/monitoring) (for Prometheus integration)
- [peersrpc](/lnrpc/peersrpc/peers.proto)
- [kvdb_postrgres](/docs/postgres.md)
- [kvdb_sqlite](/docs/sqlite.md)
- [kvdb_etcd](/docs/etcd.md)

The `dev` tag is used for development builds, and is not included in the
Expand Down
7 changes: 5 additions & 2 deletions docs/release-notes/release-notes-0.16.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -345,8 +345,11 @@ Keysend](https://github.com/lightningnetwork/lnd/pull/7334).
## DB

* [Add a sqlite backend
option](https://github.com/lightningnetwork/lnd/pull/7251) to the kvdb
package.
option](https://github.com/lightningnetwork/lnd/pull/7251) to the kvdb
package, and add it as a [backend option to
LND](https://github.com/lightningnetwork/lnd/pull/7252). Note that with this
upgrade, support for the `dragonfly-amd64`, `netbsd-386`, `netbsd-arm64` and
`openbsd-386` platforms has been dropped.

* [Bumped etcd dependencies to
3.5.7](https://github.com/lightningnetwork/lnd/pull/7353) to resolve linking
Expand Down
72 changes: 72 additions & 0 deletions docs/sqlite.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# SQLite support in LND
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Carrying over my comments from #7251 for convenience:

  • Mention that Windows i386/ARM and Linux PPC/MIPS aren't supported for SQLite backends (and why)
  • Mention that just switching the db.backend to another type will NOT migrate any data, so for the time being the SQLite backend can only be used for new nodes.


With the introduction of the `kvdb` interface, LND can support multiple database
backends. One of the supported backends is
[sqlite](https://www.sqlite.org/index.html). This document describes how use
LND with a sqlite backend.

Note that for the time being, the sqlite backend option can only be set for
_new_ nodes. Setting the option for an existing node will not migrate the data.

## Supported platforms and architectures

Note that the sqlite backend is _not_ supported for Windows (386/ARM) or for
Linux (PPC/MIPS) due to these platforms [not being supported by the sqlite
driver library.](
https://pkg.go.dev/modernc.org/sqlite#hdr-Supported_platforms_and_architectures)

## Configuring LND for SQLite

LND is configured for SQLite through the following configuration options:

* `db.backend=sqlite` to select the SQLite backend.
Roasbeef marked this conversation as resolved.
Show resolved Hide resolved
* `db.sqlite.timeout=...` to set the connection timeout. If not set, no
timeout applies.
* `db.sqlite.busytimeout=...` to set the maximum amount of time that a db call
should wait if the db is currently locked.
* `db.sqlite.pragmaoptions=...` to set a list of pragma options to be applied
to the db connection. See the
[sqlite documentation](https://www.sqlite.org/pragma.html) for more
information on the available pragma options.

## Default pragma options

Currently, the following pragma options are always set:

```
foreign_keys=on
journal_mode=wal
busy_timeout=5000 // Overried with the db.sqlite.busytimeout option.
```

The following pragma options are set by default but can be overridden using the
`db.sqlite.pragmaoptions` option:

```
synchronous=full
auto_vacuum=incremental
fullfsync=true // Only meaningful on a Mac.
```

## Auto-compaction

To activate auto-compaction on startup, the `incremental_vacuum` pragma option
should be used:
```
// Use N to restrict the maximum number of pages to be removed from the
// freelist.
db.sqlite.pragmaoptions=incremental_vacuum(N)

// Omit N if the entire freelist should be cleared.
db.sqlite.pragmaoptions=incremental_vacuum
```

Example as follows:
```
[db]
db.backend=sqlite
db.sqlite.timeout=0
db.sqlite.busytimeout=10s
db.sqlite.pragmaoptions=temp_store=memory
db.sqlite.pragmaoptions=incremental_vacuum
```
Loading