Skip to content

Add Postgres database#863

Open
benthecarman wants to merge 1 commit intolightningdevkit:mainfrom
benthecarman:postgres
Open

Add Postgres database#863
benthecarman wants to merge 1 commit intolightningdevkit:mainfrom
benthecarman:postgres

Conversation

@benthecarman
Copy link
Copy Markdown
Contributor

Add a PostgresStore implementation behind the postgres feature flag, mirroring the existing SqliteStore. Uses tokio-postgres (async-native) with an internal tokio runtime for the sync KVStoreSync trait, following the VssStore pattern.

Also gates sqlite behind a feature flag, since most tests relied on this, we ended up needing to gate a lot of them behind the flag. Curious on thoughts on how to handle this best

@benthecarman benthecarman requested a review from tnull April 1, 2026 19:55
@ldk-reviews-bot
Copy link
Copy Markdown

ldk-reviews-bot commented Apr 1, 2026

👋 Thanks for assigning @tnull as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

Copy link
Copy Markdown
Collaborator

@tnull tnull left a comment

Choose a reason for hiding this comment

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

Took a first high-level look.

Also gates sqlite behind a feature flag, since most tests relied on this, we ended up needing to gate a lot of them behind the flag. Curious on thoughts on how to handle this best

Not sure I'm following? Why do we want to feature-gate tests based on SQLite?

Also tagging @tankyleo as a secondary reviewer here as he has a lot of recent experience with Postgres on VSS Server.

Comment thread .github/workflows/postgres-integration.yml Outdated

// An internal runtime we use to avoid any deadlocks we could hit when waiting on async
// operations to finish from a sync context.
internal_runtime: Option<tokio::runtime::Runtime>,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think we could avoid using an internal runtime for this and rather just reuse crate::runtime::Runtime, if we wanted. Though, when upstreaming to LDK we'll need to rethink all of this anyways (cf. VssStore upstreaming PR).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yeah I just copied VSS to be safe.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Yeah, let's drop the extra runtime then.

Comment thread src/io/postgres_store/mod.rs
Comment thread src/io/postgres_store/mod.rs Outdated
Comment thread src/io/postgres_store/mod.rs Outdated
Comment thread src/io/postgres_store/mod.rs Outdated
Comment thread src/io/postgres_store/mod.rs
Comment thread src/io/postgres_store/mod.rs Outdated
Comment thread src/builder.rs Outdated

/// Builds a [`Node`] instance with a [`SqliteStore`] backend and according to the options
/// previously configured.
#[cfg(feature = "sqlite")]
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Why do we suddenly introduce features? We so far held off and meant to do that in a dedicated PR at some point for this or next release. Is it necessary to make Postgres work?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

okay removed for now

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Seems the postgres feature is still present in the current version?

@tnull tnull requested a review from tankyleo April 2, 2026 11:10
@benthecarman benthecarman self-assigned this Apr 2, 2026
@benthecarman benthecarman force-pushed the postgres branch 2 times, most recently from b5d297e to e8f478b Compare April 3, 2026 19:57
@benthecarman
Copy link
Copy Markdown
Contributor Author

Did fixes in follow up commits for ease of review.

@ldk-reviews-bot
Copy link
Copy Markdown

🔔 1st Reminder

Hey @tankyleo! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@ldk-reviews-bot
Copy link
Copy Markdown

🔔 2nd Reminder

Hey @tankyleo! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

Copy link
Copy Markdown
Contributor

@tankyleo tankyleo left a comment

Choose a reason for hiding this comment

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

Verbose review, please dismiss aggressively :)

Comment thread src/builder.rs
Comment thread src/io/postgres_store/mod.rs Outdated
Comment thread src/builder.rs Outdated
Comment thread src/builder.rs
Comment thread src/io/postgres_store/mod.rs Outdated
Comment thread src/io/postgres_store/mod.rs
Comment thread src/io/postgres_store/mod.rs
Comment thread src/io/postgres_store/mod.rs Outdated
Comment thread src/io/postgres_store/mod.rs
Comment thread src/io/postgres_store/mod.rs Outdated
@benthecarman
Copy link
Copy Markdown
Contributor Author

@tankyleo addressed most of your comments. Lots of docs improvements. Better handling around the db name and creating the db. And did the code clean ups + test improvements you suggested.

@benthecarman benthecarman force-pushed the postgres branch 4 times, most recently from a8589ea to a43b13b Compare April 8, 2026 01:08
@tankyleo tankyleo self-requested a review April 9, 2026 15:52
@ldk-reviews-bot
Copy link
Copy Markdown

🔔 1st Reminder

Hey @tankyleo! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@ldk-reviews-bot
Copy link
Copy Markdown

🔔 2nd Reminder

Hey @tankyleo! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

Comment thread src/io/postgres_store/mod.rs
Comment thread src/builder.rs
Comment thread src/io/postgres_store/mod.rs Outdated
Comment thread src/io/postgres_store/mod.rs
Comment thread src/io/postgres_store/mod.rs
Comment thread src/io/postgres_store/mod.rs
Comment thread src/io/postgres_store/mod.rs
Comment thread src/io/postgres_store/mod.rs
Comment thread src/io/postgres_store/mod.rs Outdated
Comment thread src/builder.rs Outdated
@benthecarman benthecarman force-pushed the postgres branch 2 times, most recently from 3bb9810 to 437f11f Compare April 14, 2026 21:03
@tankyleo tankyleo self-requested a review April 14, 2026 21:42
Copy link
Copy Markdown
Contributor

@tankyleo tankyleo left a comment

Choose a reason for hiding this comment

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

two small nits thank you

Comment thread src/builder.rs Outdated
Comment thread src/builder.rs Outdated
Comment thread src/io/postgres_store/mod.rs Outdated
@tankyleo tankyleo requested a review from tnull April 14, 2026 22:40
Add a PostgresStore implementation behind the "postgres" feature
flag, mirroring the existing SqliteStore. Uses tokio-postgres
(async-native) with an internal tokio runtime for the sync
KVStoreSync trait, following the VssStore pattern.

Includes unit tests, integration tests (channel full cycle and
node restart), and a CI workflow that runs both against a
PostgreSQL service container.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@benthecarman
Copy link
Copy Markdown
Contributor Author

benthecarman commented Apr 14, 2026

two small nits thank you

fixed

@ldk-reviews-bot
Copy link
Copy Markdown

🔔 1st Reminder

Hey @tnull! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@ldk-reviews-bot
Copy link
Copy Markdown

🔔 2nd Reminder

Hey @tnull! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

}

// Default to sslmode=require when TLS is configured
if matches!(tls, PgTlsConnector::NativeTls(_)) && config.get_ssl_mode() == SslMode::Prefer {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Claude:

 - Silent TLS downgrade. new() only upgrades SslMode::Prefer → Require when tls_config is Some. If a caller passes tls_config=Some(...) but a connection string with sslmode=disable, the connection is plaintext despite having opted into TLS. Consider erroring (or forcing Require) when
  tls_config.is_some() and sslmode is Disable/Allow. src/io/postgres_store/mod.rs:385.

);

// Try connecting to the target database directly — if it exists we're done.
let initial_err = match Self::make_config_connection(config, tls).await {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Claude:

- Swallowed initial connect error. create_database_if_not_exists treats any connect failure to the target DB as "DB doesn't exist" (src/io/postgres_store/mod.rs:490). Auth failures, wrong port, etc. fall through to the bootstrap-connect path. The final error message does include initial_err,
   which is good, but logging it at debug! before swallowing would help diagnose the common "looks like it worked, actually didn't" case.

Comment thread src/builder.rs
/// [`DEFAULT_DB_NAME`](io::postgres_store::DEFAULT_DB_NAME). The database will be created
/// automatically if it doesn't already exist. The `connection_string` must not include a
/// `dbname` when `db_name` is set, providing both is an error. When auto-creating the
/// database if is doesn't exist, the initial connection is made using the default database
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
/// database if is doesn't exist, the initial connection is made using the default database
/// database if it doesn't exist, the initial connection is made using the default database

check_namespace_key_validity(primary_namespace, secondary_namespace, Some(key), "read")?;

let mut locked_client = self.client.lock().await;
self.ensure_connected(&mut locked_client).await?;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Calling this on every operation should cost us an extra RTT each time, no?

}

struct PostgresStoreInner {
client: tokio::sync::Mutex<Client>,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Rather than just using a single client, can we steal vss-server's SmallPool to not make all operations fully sync and sequenced?

Comment thread src/builder.rs
///
/// [PostgreSQL]: https://www.postgresql.org
#[cfg(feature = "postgres")]
pub fn build_with_postgres_store(
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We'll need to add this to ldk_node.udl to actually make this useful in bindings.

secondary_namespace TEXT NOT NULL DEFAULT '',
key TEXT NOT NULL CHECK (key <> ''),
value BYTEA,
sort_order BIGINT NOT NULL DEFAULT 0 CHECK (sort_order >= 0),
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Should we now also follow what we did in lightningdevkit/vss-server#96 and use BIGSERIAL?

Comment thread src/io/postgres_store/mod.rs Outdated
Comment thread src/builder.rs
Comment on lines +638 to +643
/// The given `db_name` will be used or default to
/// [`DEFAULT_DB_NAME`](io::postgres_store::DEFAULT_DB_NAME). The database will be created
/// automatically if it doesn't already exist. The `connection_string` must not include a
/// `dbname` when `db_name` is set, providing both is an error. When auto-creating the
/// database if it doesn't exist, the initial connection is made using the default database
/// `postgres`.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Small doc nit, the "initial connection is made using the default database postgres" isn't quite accurate, as the initial connection actually goes to the target database, and postgres is only used as a fallback to create it. Could we reorder this so it reads in the order the code actually runs? The same applies to the ArcedNodeBuilder doc below and PostgresStore::new in postgres_store/mod.rs. Suggested wording:

The given `db_name` will be used or default to [`DEFAULT_DB_NAME`](io::postgres_store::DEFAULT_DB_NAME). The `connection_string` must not include a `dbname` when `db_name` is set, providing both is an error. The database will be created automatically if it doesn't already exist. The initial connection is made to the target database, and if it fails we fall back to the default `postgres` database to create it.

}

struct PostgresStoreInner {
client: tokio::sync::Mutex<Client>,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I noticed Mutex<Client> is used here. Since Postgres handles concurrent queries server-side (unlike SQLite), is this intentional for simplicity or to match SqliteStore?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

5 participants