lncfg+scripts: use configurable global lock for postgres backends
What changed, and why it matters
This commit makes a database-locking setting configurable for users running LND with a PostgreSQL backend. Previously, the wallet database always used a single-writer 'global lock' and the channel database did not. Now operators can turn each on or off via new config options. The change is framed as a temporary safety/concurrency workaround, not as a fix for an active security vulnerability.
Treat as a configuration/robustness improvement rather than a critical security patch. Operators using Postgres should review whether their workload needs the new global-lock toggles and monitor for concurrency-related issues if disabling the wallet lock. No urgent upgrade is indicated by the commit itself.
Security signals we found
Concurrency-control knob exposed to operators
Single-writer global lock retained by default for wallet database
Comment describes wallet subsystem as 'not robust enough' without single writer
Temporary workaround pending native SQL migration
No patch of an exploit, injection, or cryptographic flaw in the diff
Evidence from the diff
The patch replaces hardcoded WithGlobalLock=true for the wallet Postgres backend and hardcoded false-equivalent behavior for channeldb with configurable fields: db.postgres.channeldb-with-global-lock (default false) and db.postgres.walletdb-with-global-lock (default true). It updates lncfg/db.go to copy the Postgres config and set WithGlobalLock from these options, adds documentation and sample config entries, and excludes the walletdb option from a sample-config default-value check because its default is set later during startup.
Changed components
lncfg/db.godocs/postgres.mdsample-lnd.confscripts/check-sample-lnd-conf.shPostgreSQL kvdb backends (channeldb_kv, walletdb_kv)Inspect captured patch +42 / −10
diff --git a/docs/postgres.md b/docs/postgres.md
index 423efc7..89b16eb 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 9eb0278..5bd4d2e 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 ed5dabd..c9a2865 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 48cbad7..0f51e47 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
Why this scored 32/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.