-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
Conversation
a0be4d2
to
eb64383
Compare
5ea0d29
to
cf419c5
Compare
go.mod
Outdated
@@ -166,6 +182,8 @@ 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 | |||
|
|||
replace github.com/lightningnetwork/lnd/kvdb => github.com/ellemouton/lnd/kvdb v0.0.0-20221216101740-c01520c57f3c |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to be replaced with kvdb module tag before landing :)
629e146
to
eca1a9e
Compare
96e9d7c
to
6dc510c
Compare
I used the wallet.db and sphinx.db separate from main db:All transactions starting with "BEGIN DEFERRED" (the default directive):
All transactions starting with "BEGIN IMMEDIATE":
Write txs use the "BEGIN IMMEDIATE" and reads use the "BEGIN DEFERRED":
wallet.db and sphinx.db in main db:All transactions starting with "BEGIN DEFERRED" (the default directive):
All transactions starting with "BEGIN IMMEDIATE":
Write txs use the "BEGIN IMMEDIATE" and reads use the "BEGIN DEFERRED"
The current PR state is equivalent to this last configuration: reads are deferred, writes are immediate and the macaroon & sphinx dbs are in the main db. Only the wallet.db is kept separate due to the deadlocks it would cause if kept in the same db. |
6dc510c
to
748856a
Compare
@Roasbeef: review reminder |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome PR, great work!
Did a first pass and it looks mostly ready. I want to do some manual testing first before giving my thumbs up though.
@@ -0,0 +1,26 @@ | |||
# SQLite support in LND |
There was a problem hiding this comment.
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.
Some benchmark runs on my laptop (M1):
It's actually faster than bolt on this hardware/context for me (bolt run), but on a linux VM the opposite is true:
I then tried some of the options in this blog post: diff --git a/kvdb/sqlite/db.go b/kvdb/sqlite/db.go
index ae5ff9295..c683e3425 100644
--- a/kvdb/sqlite/db.go
+++ b/kvdb/sqlite/db.go
@@ -51,6 +51,18 @@ func NewSqliteBackend(ctx context.Context, cfg *Config, fileName,
name: "journal_mode",
value: "WAL",
},
+ {
+ name: "synchronous",
+ value: "NORMAL",
+ },
+ {
+ name: "temp_store",
+ value: "MEMORY",
+ },
+ {
+ name: "mmap_size",
+ value: "1000000000",
+ },
}
sqliteOptions := make(url.Values)
for _, option := range pragmaOptions {
Linux VM run (4 vCPU, 8 GB RAM, bolt):
(sqlite, new options linked above)
default options:
So totally not scientific benchmarks, hard to really conclude the impact of the options. In the other PR I suggested allowing a raw pragma query string to be passed in as an opt, so ppl can tune these w/o recompiling. |
} | ||
closeFuncs[NSMacaroonDB] = sqliteMacaroonBackend.Close | ||
|
||
sqliteDecayedLogBackend, err := kvdb.Open( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Realized that we aren't storing the neutrino data in here. Current entrypoint is in initNeutrinoBackend
. It actually still uses the walletdb
directly also vs kvdb
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I used this diff to have the neutrino data be stored in sqlite as well:
diff --git a/config_builder.go b/config_builder.go
index 6f427ffd2..f0d425f1a 100644
--- a/config_builder.go
+++ b/config_builder.go
@@ -1190,13 +1190,28 @@ 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
)
- if err != nil {
- return nil, nil, fmt.Errorf("unable to create neutrino "+
- "database: %v", err)
+ switch {
+ case cfg.DB.Backend == kvdb.SqliteBackendName:
+ db, err = kvdb.Open(
+ kvdb.SqliteBackendName, context.Background(), cfg.DB.Sqlite,
+ "neutrino.db", "neutrino",
+ )
+ if err != nil {
+ return nil, nil, err
+ }
+ 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)
+ }
}
headerStateAssertion, err := parseHeaderStateAssertion(
That places it into its own database (which seems like what we want to do here?).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
great catch!! included your diff. I think we can just put it in lnd.db too if it doesnt cause any deadlocks right? will see if itests pass like that
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok with neutrino in same file as the other DBs and running make itest dbbackend=sqlite backend=neutrino
seems to behave ok. Just ran some of the usual flakes but looks like majority of tests pass. So perhaps fine to keep it in same file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To same file, or not to same file....🤔
Only arg I can think of not putting it in the same file is if ppl frequently just delete that db if they want to force a resync (tho they can do so already by deleting the on disk headers).
Looking at the way the files are set up, if we put it all in the same file (or diff file) with the distinct directory, then the flat header files end up in diff a diff directory, so things end up being more scattered vs retaining our existing pretty well established file format.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok so even with the latest update where we dont have the distinct directory?
In any case, latest update keeps the neutrino db in separate file and puts all sqlite db files in the main data/chain/bitcoin/<network>/
dir. This includes all dbs that would usually have been in the graph path... is that ok?
Lemme know what you think
226ec69
to
9a177f4
Compare
Thanks for all the performance tests @Roasbeef 🚀 |
00540cb
to
7ebc4b9
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code looks good to me, nice work!
So this can be merged once we come to a conclusion concerning what pragma flags to enable by default.
@@ -360,7 +360,7 @@ jobs: | |||
fail-fast: false | |||
matrix: | |||
pinned_dep: | |||
- google.golang.org/grpc v1.38.0 | |||
- google.golang.org/grpc v1.41.0 |
There was a problem hiding this comment.
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 😬
looks like the |
Use kvdb package v1.4.1. This update also forced the protobuf version to be bumped which required `make rpc` to be run to update the generated files. This also required a bump in the github pinned dependencies config for the grpc and protobuf libs.
In this diff, we expose the option to use sqlite as a db backend.
Warn a user if they attempt to initialise a new db type (sqlite or postgres) if an old bbolt db file is found.
7ebc4b9
to
fe52545
Compare
works if I change maxDbConns to 2 (up from 1) |
I am not convinced of this. On the branch below, I set max connections to 1 and tried to detect parallelism in the master...joostjager:lnd:parallel-canary When settings connections to 2, it does detect parallelism (itest crashes because canary panics).
I think it could be that this itest requires parallelism. Still the question remains then whether a low number of max connections is ideal for performance given that readers do seem to block each other. And also how to avoid deadlocks caused by multiple readers using a small set of connnections? |
See my tests on the linux machine above, if you have enough cores, then things can scale well. Otherwise with a "normal" amount of cores, then performance suffers as you increase the number of max connections.
IIUC, the prior approach of distinct read/write connections allows this to succeed, as with the WAL mode, then readers don't block writers and the other way around. I think it's cool we were able to update the |
Dug up some more documentation/discussion re multi-threading and concurrency: https://sqlite.org/forum/forumpost/2a53b75034770302?raw
Which argues for the prior approach where we have a read connection that can parallelize reads using the WAL, and another write connection that serializes the writes with the exclusive lock. |
Also on a dedicated read connection with |
Here's the source for As was shown above, there seems to be a sweet spot: too many active connections (managed by the The purpose of the pooled connections in the stdlib package is to get around that libraries like At this point, all we have are conjectures w/o proper instrumentation, so we're just in bikeshedding territory here. From all my tests above, |
The only question that is still on my mind is why exactly more connections make it so much slower. I also thought of removing A limit of 1 leading to deadlocks and a limit of 10 making it incredibly slow feels like there is not too much leeway in setting the right number of connections. |
Yeah I tried that above, so leaving the library to its defaults, and that has similar degradation. I think the next route here once we land this PR is to run the same benchmarks connected to profiling tools, so we can get things like flame graphs to see why perf degrades so much. It might be something in FWIW as mentioned above, if you have a lot of cores and a fast SSD then things don't drop off so much. With my runs above, it at least appears as though the current defaults are as fast, or even faster than default |
I tried to reproduce the slow down with a simple (non One thing that I remember seeing in one of my tests is that the scheduling of sqlite transactions may not be optimal. In some cases I got one connection that got starved because it was never its turn to run. Maybe that has something to do with it too. To me it seems that executing the benchmark that you propose isn't that much work, and this could hopefully complete the picture. I tried, but ran into:
|
The issue doesn't seem to be reads, but instead long running or frequent writes that end up starving out reads. We also have instances where at time around a dozen db transactions are created in the process of fully settling a payment: #5186. I think at one point you counted something like 40 fsyncs for a single payment.
That's odd, which version of Go are you running? That looks like a Also re CLN: confirmed that all db access for them is single threaded (one connection for the entire process). My point above was simply that further benchmarking shouldn't block forward progress towards landing this PR so users can start testing against it in master. The main open param now (number of active connections and idle connections) can be tuned via a command line flag. We may also end up changing ht defaults given further observations. |
Not sure what it was, but |
lnd PR lightningnetwork/lnd#7252 removed support for 10 OS / architectures As Pool requires lnd to run, this PR replicates the build list of lnd for Pool.
In this PR, the changes in #7251 are made use of & a new sqlite db backend option is given.
Depends on #7251
Fixes #6176
Replaces #6570