Skip to content

Commit 8becefe

Browse files
benthecarmanclaude
andcommitted
Add PostgreSQL storage backend
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>
1 parent 771c147 commit 8becefe

File tree

7 files changed

+1215
-0
lines changed

7 files changed

+1215
-0
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
name: CI Checks - PostgreSQL Integration Tests
2+
3+
on: [push, pull_request]
4+
5+
concurrency:
6+
group: ${{ github.workflow }}-${{ github.ref }}
7+
cancel-in-progress: true
8+
9+
jobs:
10+
build-and-test:
11+
runs-on: ubuntu-latest
12+
13+
services:
14+
postgres:
15+
image: postgres:latest
16+
ports:
17+
- 5432:5432
18+
env:
19+
POSTGRES_DB: postgres
20+
POSTGRES_USER: postgres
21+
POSTGRES_PASSWORD: postgres
22+
options: >-
23+
--health-cmd pg_isready
24+
--health-interval 10s
25+
--health-timeout 5s
26+
--health-retries 5
27+
28+
steps:
29+
- name: Checkout code
30+
uses: actions/checkout@v3
31+
- name: Install Rust stable toolchain
32+
run: |
33+
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile=minimal --default-toolchain stable
34+
- name: Run PostgreSQL store tests
35+
env:
36+
TEST_POSTGRES_URL: "host=localhost user=postgres password=postgres"
37+
run: cargo test --features postgres io::postgres_store
38+
- name: Run PostgreSQL integration tests
39+
env:
40+
TEST_POSTGRES_URL: "host=localhost user=postgres password=postgres"
41+
run: |
42+
RUSTFLAGS="--cfg no_download --cfg cycle_tests" cargo test --features postgres --test integration_tests_postgres

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ panic = 'abort' # Abort on panic
2626
[features]
2727
default = ["sqlite"]
2828
sqlite = ["dep:rusqlite"]
29+
postgres = ["dep:tokio-postgres"]
2930

3031
[dependencies]
3132
#lightning = { version = "0.2.0", features = ["std"] }
@@ -77,6 +78,7 @@ serde_json = { version = "1.0.128", default-features = false, features = ["std"]
7778
log = { version = "0.4.22", default-features = false, features = ["std"]}
7879

7980
async-trait = { version = "0.1", default-features = false }
81+
tokio-postgres = { version = "0.7", default-features = false, features = ["runtime"], optional = true }
8082
vss-client = { package = "vss-client-ng", version = "0.5" }
8183
prost = { version = "0.11.6", default-features = false}
8284
#bitcoin-payment-instructions = { version = "0.6" }

src/builder.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,24 @@ impl NodeBuilder {
633633
self.build_with_store(node_entropy, kv_store)
634634
}
635635

636+
/// Builds a [`Node`] instance with a [PostgreSQL] backend and according to the options
637+
/// previously configured.
638+
///
639+
/// Connects to the PostgreSQL database at the given `connection_string`.
640+
/// The given `kv_table_name` will be used or default to
641+
/// [`DEFAULT_KV_TABLE_NAME`](crate::io::postgres_store::DEFAULT_KV_TABLE_NAME).
642+
///
643+
/// [PostgreSQL]: https://www.postgresql.org
644+
#[cfg(feature = "postgres")]
645+
pub fn build_with_postgres_store(
646+
&self, node_entropy: NodeEntropy, connection_string: &str, kv_table_name: Option<String>,
647+
) -> Result<Node, BuildError> {
648+
let kv_store =
649+
crate::io::postgres_store::PostgresStore::new(connection_string, kv_table_name)
650+
.map_err(|_| BuildError::KVStoreSetupFailed)?;
651+
self.build_with_store(node_entropy, kv_store)
652+
}
653+
636654
/// Builds a [`Node`] instance with a [`FilesystemStore`] backend and according to the options
637655
/// previously configured.
638656
pub fn build_with_fs_store(&self, node_entropy: NodeEntropy) -> Result<Node, BuildError> {
@@ -1092,6 +1110,22 @@ impl ArcedNodeBuilder {
10921110
self.inner.read().unwrap().build(*node_entropy).map(Arc::new)
10931111
}
10941112

1113+
/// Builds a [`Node`] instance with a [PostgreSQL] backend and according to the options
1114+
/// previously configured.
1115+
///
1116+
/// [PostgreSQL]: https://www.postgresql.org
1117+
#[cfg(feature = "postgres")]
1118+
pub fn build_with_postgres_store(
1119+
&self, node_entropy: Arc<NodeEntropy>, connection_string: String,
1120+
kv_table_name: Option<String>,
1121+
) -> Result<Arc<Node>, BuildError> {
1122+
self.inner
1123+
.read()
1124+
.unwrap()
1125+
.build_with_postgres_store(*node_entropy, &connection_string, kv_table_name)
1126+
.map(Arc::new)
1127+
}
1128+
10951129
/// Builds a [`Node`] instance with a [`FilesystemStore`] backend and according to the options
10961130
/// previously configured.
10971131
pub fn build_with_fs_store(

src/io/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
//! Objects and traits for data persistence.
99
10+
#[cfg(feature = "postgres")]
11+
pub mod postgres_store;
1012
#[cfg(feature = "sqlite")]
1113
pub mod sqlite_store;
1214
#[cfg(test)]
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// This file is Copyright its original authors, visible in version control history.
2+
//
3+
// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5+
// http://opensource.org/licenses/MIT>, at your option. You may not use this file except in
6+
// accordance with one or both of these licenses.
7+
8+
use lightning::io;
9+
use tokio_postgres::Client;
10+
11+
pub(super) async fn migrate_schema(
12+
_client: &Client, _kv_table_name: &str, from_version: u16, to_version: u16,
13+
) -> io::Result<()> {
14+
assert!(from_version < to_version);
15+
// Future migrations go here, e.g.:
16+
// if from_version == 1 && to_version >= 2 {
17+
// migrate_v1_to_v2(client, kv_table_name).await?;
18+
// from_version = 2;
19+
// }
20+
Ok(())
21+
}

0 commit comments

Comments
 (0)