From 2e16efed309c694857ec8e774b71a9cd158d1e09 Mon Sep 17 00:00:00 2001 From: ziggie Date: Thu, 27 Nov 2025 20:01:24 +0100 Subject: [PATCH 1/3] sqldb: add global lock config options for postgres Add two configuration options to control global lock usage for different postgres database backends: - ChannelDBWithGlobalLock: for channeldb access (default: false) - WalletDBWithGlobalLock: for wallet database access (default: true) These allow fine-grained control over which databases use global locks, rather than hardcoding the behavior. This is a temporary measure until the revocation log and wallet are migrated to native SQL and become fully concurrent-safe. --- sqldb/config.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/sqldb/config.go b/sqldb/config.go index 34de293c18..59801dbeaf 100644 --- a/sqldb/config.go +++ b/sqldb/config.go @@ -44,11 +44,13 @@ func (p *SqliteConfig) Validate() error { // //nolint:ll type PostgresConfig struct { - Dsn string `long:"dsn" description:"Database connection string."` - Timeout time.Duration `long:"timeout" description:"Database connection timeout. Set to zero to disable."` - MaxConnections int `long:"maxconnections" description:"The maximum number of open connections to the database. Set to zero for unlimited."` - SkipMigrations bool `long:"skipmigrations" description:"Skip applying migrations on startup."` - QueryConfig `group:"query" namespace:"query"` + Dsn string `long:"dsn" description:"Database connection string."` + Timeout time.Duration `long:"timeout" description:"Database connection timeout. Set to zero to disable."` + MaxConnections int `long:"maxconnections" description:"The maximum number of open connections to the database. Set to zero for unlimited."` + SkipMigrations bool `long:"skipmigrations" description:"Skip applying migrations on startup."` + ChannelDBWithGlobalLock bool `long:"channeldb-with-global-lock" description:"Use a global lock for channeldb access. This ensures only a single writer at a time but reduces concurrency. This is a temporary workaround until the revocation log is migrated to a native sql schema."` + WalletDBWithGlobalLock bool `long:"walletdb-with-global-lock" description:"Use a global lock for wallet database access. This ensures only a single writer at a time but reduces concurrency. This is a temporary workaround until the wallet subsystem is upgraded to a native sql schema."` + QueryConfig `group:"query" namespace:"query"` } // Validate checks that the PostgresConfig values are valid. From 4f051d97eac3f03c227f52caed74decfe3f5021c Mon Sep 17 00:00:00 2001 From: ziggie Date: Thu, 27 Nov 2025 20:03:17 +0100 Subject: [PATCH 2/3] lncfg+scripts: use configurable global lock for postgres backends Replace hardcoded WithGlobalLock assignment with configurable options wallet postgres backends. Also add the WithGlobalLock option to the channeldb table for postgres backends. Defaults: - channeldb: false (allow concurrent access) - wallet: true (maintain safe single-writer behavior) Users can now override these defaults via: - db.postgres.channeldb-with-global-lock - db.postgres.walletdb-with-global-lock This gives operators flexibility while maintaining safe defaults until full native SQL migration is complete. Moreover exclude db.postgres.walletdb-with-global-lock check in the sample config file script. We cannot easily check the correct default because we set it later in the LND startup sequence so we exclude it. --- docs/postgres.md | 10 ++++++++++ lncfg/db.go | 29 ++++++++++++++++++++--------- sample-lnd.conf | 11 +++++++++++ scripts/check-sample-lnd-conf.sh | 2 +- 4 files changed, 42 insertions(+), 10 deletions(-) diff --git a/docs/postgres.md b/docs/postgres.md index 423efc7901..89b16ebcf2 100644 --- a/docs/postgres.md +++ b/docs/postgres.md @@ -42,6 +42,16 @@ db.postgres.timeout=0 Connection timeout is disabled, to account for situations where the database might be slow for unexpected reasons. +Moreover for particular kv tables we also add the option to access the +tables via a global lock (single wirter). This is a temorpary measure until +these particular tables have a native sql schema. This helps to mitigate +resource exhaustion in case LND experiencing high concurrent load: + +* `db.postgres.walletdb-with-global-lock=true` to run LND with a single writer + for the walletdb_kv table (default is true). +* `db.postgres.channeldb-with-global-lock=false` to run the channeldb_kv table + with a single writer (default is false). + ## Important note about replication In case a replication architecture is planned, streaming replication should be avoided, as the master does not verify the replica is indeed identical, but it will only forward the edits queue, and let the slave catch up autonomously; synchronous mode, albeit slower, is paramount for `lnd` data integrity across the copies, as it will finalize writes only after the slave confirmed successful replication. diff --git a/lncfg/db.go b/lncfg/db.go index 9eb0278876..5bd4d2e19b 100644 --- a/lncfg/db.go +++ b/lncfg/db.go @@ -115,7 +115,15 @@ func DefaultDB() *DB { }, Postgres: &sqldb.PostgresConfig{ MaxConnections: defaultPostgresMaxConnections, - QueryConfig: *sqldb.DefaultPostgresConfig(), + // Normally we don't use a global lock for channeldb + // access, but if a user encounters huge concurrency + // issues, they can enable this to use a global lock. + ChannelDBWithGlobalLock: false, + // Default to true to maintain safe single-writer + // behavior until the wallet subsystem is upgraded to + // a native sql schema. + WalletDBWithGlobalLock: true, + QueryConfig: *sqldb.DefaultPostgresConfig(), }, Sqlite: &sqldb.SqliteConfig{ MaxConnections: defaultSqliteMaxConnections, @@ -400,9 +408,15 @@ func (db *DB) GetBackends(ctx context.Context, chanDBPath, // users to native SQL. postgresConfig := GetPostgresConfigKVDB(db.Postgres) + // Create a separate config for channeldb with the global lock + // setting if configured. + postgresConfigChannelDB := GetPostgresConfigKVDB(db.Postgres) + postgresConfigChannelDB.WithGlobalLock = db.Postgres. + ChannelDBWithGlobalLock + postgresBackend, err := kvdb.Open( kvdb.PostgresBackendName, ctx, - postgresConfig, NSChannelDB, + postgresConfigChannelDB, NSChannelDB, ) if err != nil { return nil, fmt.Errorf("error opening postgres graph "+ @@ -450,14 +464,11 @@ func (db *DB) GetBackends(ctx context.Context, chanDBPath, } closeFuncs[NSTowerServerDB] = postgresTowerServerBackend.Close - // The wallet subsystem is still not robust enough to run it - // without a single writer in postgres therefore we create a - // new config with the global lock enabled. - // - // NOTE: This is a temporary measure and should be removed as - // soon as the wallet code is more robust. + // Create a separate config for wallet with the global lock + // setting if configured. postgresConfigWalletDB := GetPostgresConfigKVDB(db.Postgres) - postgresConfigWalletDB.WithGlobalLock = true + postgresConfigWalletDB.WithGlobalLock = db.Postgres. + WalletDBWithGlobalLock postgresWalletBackend, err := kvdb.Open( kvdb.PostgresBackendName, ctx, diff --git a/sample-lnd.conf b/sample-lnd.conf index ed5dabd43c..c9a2865b85 100644 --- a/sample-lnd.conf +++ b/sample-lnd.conf @@ -1625,6 +1625,17 @@ ; Whether to skip executing schema migrations. ; db.postgres.skipmigrations=false +; Use a global lock for channeldb access. This ensures only a single writer at +; a time but reduces concurrency. This is a temporary workaround until the +; revocation log is migrated to native SQL. +; db.postgres.channeldb-with-global-lock=false + + +; Use a global lock for wallet database access. This is a temporary workaround +; until the wallet subsystem is upgraded to a native sql schema. +; db.postgres.walletdb-with-global-lock=true + + ; The maximum number of elements to use in a native-SQL batch query IN clause. ; db.postgres.query.max-batch-size=5000 diff --git a/scripts/check-sample-lnd-conf.sh b/scripts/check-sample-lnd-conf.sh index 48cbad7f60..0f51e47f0b 100755 --- a/scripts/check-sample-lnd-conf.sh +++ b/scripts/check-sample-lnd-conf.sh @@ -59,7 +59,7 @@ OPTIONS_NO_LND_DEFAULT_VALUE_CHECK="channel-max-fee-exposure adminmacaroonpath \ backupfilepath maxchansize bitcoin.chaindir bitcoin.defaultchanconfs \ bitcoin.defaultremotedelay bitcoin.dnsseed signrpc.signermacaroonpath \ walletrpc.walletkitmacaroonpath chainrpc.notifiermacaroonpath \ - routerrpc.routermacaroonpath" + routerrpc.routermacaroonpath db.postgres.walletdb-with-global-lock" # EXITCODE is returned at the end after all checks are performed and set to 1 From 565633578ffc1b6f59f7b67adec4283afc29ded8 Mon Sep 17 00:00:00 2001 From: ziggie Date: Thu, 27 Nov 2025 20:15:24 +0100 Subject: [PATCH 3/3] docs: add release-notes for LND 20.1 --- docs/release-notes/release-notes-0.20.1.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/release-notes/release-notes-0.20.1.md b/docs/release-notes/release-notes-0.20.1.md index 59370eaae7..4cc4556fb0 100644 --- a/docs/release-notes/release-notes-0.20.1.md +++ b/docs/release-notes/release-notes-0.20.1.md @@ -51,6 +51,15 @@ ## Performance Improvements +* [Added new Postgres configuration + options](https://github.com/lightningnetwork/lnd/pull/10394) + `db.postgres.channeldb-with-global-lock` and + `db.postgres.walletdb-with-global-lock` to allow fine-grained control over + database concurrency. The channeldb global lock defaults to `false` to enable + concurrent access, while the wallet global lock defaults to `true` to maintain + safe single-writer behavior until the wallet subsystem is fully + concurrent-safe. + ## Deprecations # Technical and Architectural Updates