Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
c23e105
DROPME: Bump LDK for async store migration
tnull Jun 3, 2026
cb67f6c
DROPME: Account for `vss-server` location change
tnull Jun 3, 2026
7b7f35a
Move BDK wallet helpers onto async KV storage
tnull Jun 3, 2026
b9217a6
Use async KV storage for static invoices
tnull Jun 3, 2026
0d6c350
f Simplify
tnull Jun 3, 2026
ffceaca
Move peer persistence onto async KV storage
tnull Jun 3, 2026
90e8e59
Move DataStore persistence onto async KV storage
tnull Jun 3, 2026
333e9a4
Move node metrics persistence onto async KV storage
tnull Jun 3, 2026
8d7d47b
f - Move on-chain wallet update helper out of macro
tnull Jun 3, 2026
b4cf32f
Use BDK's async wallet persister
tnull Jun 3, 2026
47f80ba
Use async KVStore migration for filesystem stores
tnull Jun 3, 2026
867b0f9
Move test InMemoryStore into shared module
tnull Jun 3, 2026
1843e38
f - Preserve moved InMemoryStore code exactly
tnull Jun 3, 2026
bfbedc7
Move test store checks onto async KV storage
tnull Jun 3, 2026
40e4aad
Remove blocking KV store support
tnull Jun 3, 2026
0ba01de
f - Await async VSS store test helper
tnull Jun 3, 2026
3e76ccc
Stop retaining a VSS runtime
tnull Jun 3, 2026
9ad8f2d
Stop retaining a PostgreSQL runtime
tnull Jun 3, 2026
32ea1e6
Add database persistence benchmarks
benthecarman May 28, 2026
ef8694f
Benchmark payments across stores
benthecarman May 28, 2026
22d7511
Wait for return payment in benches
benthecarman Jun 3, 2026
8e9fa64
Add forwarding operations benchmark
benthecarman Jun 3, 2026
f23517b
Add channel open operations benchmark
benthecarman Jun 3, 2026
1508155
Add seeded startup operations benchmark
benthecarman Jun 3, 2026
6bd3945
Centralize PostgreSQL test URL env var
benthecarman Jun 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion .github/workflows/benchmarks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,20 @@ jobs:
runs-on: ubuntu-latest
env:
TOOLCHAIN: stable
services:
postgres:
image: postgres:latest
ports:
- 5432:5432
env:
POSTGRES_DB: postgres
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- name: Checkout source code
uses: actions/checkout@v6
Expand Down Expand Up @@ -42,5 +56,7 @@ jobs:
echo "BITCOIND_EXE=$( pwd )/bin/bitcoind-${{ runner.os }}-${{ runner.arch }}" >> "$GITHUB_ENV"
echo "ELECTRS_EXE=$( pwd )/bin/electrs-${{ runner.os }}-${{ runner.arch }}" >> "$GITHUB_ENV"
- name: Run benchmarks
env:
TEST_POSTGRES_URL: "host=localhost user=postgres password=postgres"
run: |
cargo bench
cargo test --benches --features "bench postgres"
2 changes: 1 addition & 1 deletion .github/workflows/vss-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ jobs:

- name: Build and Deploy VSS Server
run: |
cd vss-server/rust
cd vss-server
cargo run server/vss-server-config.toml&
- name: Run VSS Integration tests
run: |
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/vss-no-auth-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ jobs:

- name: Build and Deploy VSS Server
run: |
cd vss-server/rust
cd vss-server
RUSTFLAGS=--cfg=noop_authorizer cargo run --no-default-features server/vss-server-config.toml&
- name: Run VSS Integration tests
run: |
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk

# Env file used in benches
.env

# Ignore generated swift related files
.swiftpm/
LDKNodeFFI.*
Expand Down
25 changes: 25 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ panic = 'abort' # Abort on panic

[features]
default = []
bench = []
postgres = ["dep:tokio-postgres", "dep:native-tls", "dep:postgres-native-tls"]

[dependencies]
Expand Down Expand Up @@ -96,6 +97,7 @@ rand = { version = "0.9.2", default-features = false, features = ["std", "thread
proptest = "1.0.0"
regex = "1.5.6"
criterion = { version = "0.7.0", features = ["async_tokio"] }
dotenvy = "0.15"
ldk-node-062 = { package = "ldk-node", version = "=0.6.2" }
ldk-node-070 = { package = "ldk-node", version = "=0.7.0" }

Expand Down Expand Up @@ -140,6 +142,15 @@ check-cfg = [
name = "payments"
harness = false

[[bench]]
name = "operations"
harness = false

[[bench]]
name = "database"
harness = false
required-features = ["bench"]

#[patch.crates-io]
#lightning = { path = "../rust-lightning/lightning" }
#lightning-types = { path = "../rust-lightning/lightning-types" }
Expand Down Expand Up @@ -196,3 +207,17 @@ harness = false
#lightning-liquidity = { path = "../rust-lightning/lightning-liquidity" }
#lightning-macros = { path = "../rust-lightning/lightning-macros" }
#lightning-dns-resolver = { path = "../rust-lightning/lightning-dns-resolver" }

[patch."https://github.com/lightningdevkit/rust-lightning"]
lightning = { git = "https://github.com/tnull/rust-lightning", rev = "2611766e6a5913d6e33afdc1932485575ee8ea4e" }
lightning-types = { git = "https://github.com/tnull/rust-lightning", rev = "2611766e6a5913d6e33afdc1932485575ee8ea4e" }
lightning-invoice = { git = "https://github.com/tnull/rust-lightning", rev = "2611766e6a5913d6e33afdc1932485575ee8ea4e" }
lightning-net-tokio = { git = "https://github.com/tnull/rust-lightning", rev = "2611766e6a5913d6e33afdc1932485575ee8ea4e" }
lightning-persister = { git = "https://github.com/tnull/rust-lightning", rev = "2611766e6a5913d6e33afdc1932485575ee8ea4e" }
lightning-background-processor = { git = "https://github.com/tnull/rust-lightning", rev = "2611766e6a5913d6e33afdc1932485575ee8ea4e" }
lightning-rapid-gossip-sync = { git = "https://github.com/tnull/rust-lightning", rev = "2611766e6a5913d6e33afdc1932485575ee8ea4e" }
lightning-block-sync = { git = "https://github.com/tnull/rust-lightning", rev = "2611766e6a5913d6e33afdc1932485575ee8ea4e" }
lightning-transaction-sync = { git = "https://github.com/tnull/rust-lightning", rev = "2611766e6a5913d6e33afdc1932485575ee8ea4e" }
lightning-liquidity = { git = "https://github.com/tnull/rust-lightning", rev = "2611766e6a5913d6e33afdc1932485575ee8ea4e" }
lightning-macros = { git = "https://github.com/tnull/rust-lightning", rev = "2611766e6a5913d6e33afdc1932485575ee8ea4e" }
lightning-dns-resolver = { git = "https://github.com/tnull/rust-lightning", rev = "2611766e6a5913d6e33afdc1932485575ee8ea4e" }
267 changes: 267 additions & 0 deletions benches/database.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,267 @@
// This file is Copyright its original authors, visible in version control history.
//
// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. You may not use this file except in
// accordance with one or both of these licenses.

use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;

use criterion::{criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion, Throughput};
use ldk_node::bench::{
configured_backends, payment_details_batch, payment_key, payment_update_batch_from_offset,
pending_payment_details_batch_from_offset, pending_payment_update_batch_from_offset, Backend,
PaginatedStoreFixture, StoreFixture, BATCH_LEN, PAGINATED_PAGE_LEN,
};

fn database_benchmark(c: &mut Criterion) {
dotenvy::dotenv().ok();
let backends = configured_backends();

benchmark_payment_store_single_ops(c, &backends);
benchmark_payment_store_warm_sequential(c, &backends);
benchmark_payment_store_concurrent(c, &backends);
benchmark_payment_store(c, &backends);
benchmark_payment_store_paginated(c, &backends);
benchmark_payment_store_lifecycle(c, &backends);
benchmark_pending_payment_store(c, &backends);
}

fn benchmark_payment_store(c: &mut Criterion, backends: &[Backend]) {
let mut group = c.benchmark_group("database/payment_store");
group.throughput(Throughput::Elements(BATCH_LEN));
for backend in backends.iter().copied() {
group.bench_with_input(
BenchmarkId::new("insert_100_sequential_cold", backend.name()),
&backend,
|b, backend| {
b.iter_batched(
|| {
let fixture = StoreFixture::new(*backend, "payment_insert");
let payments = payment_details_batch(0);
(fixture, payments)
},
|(fixture, payments)| fixture.write_payment_batch(payments),
BatchSize::SmallInput,
)
},
);

group.bench_with_input(
BenchmarkId::new("update_100_sequential_cold", backend.name()),
&backend,
|b, backend| {
b.iter_batched(
|| {
let fixture = StoreFixture::new(*backend, "payment_update");
fixture.write_payment_batch_from_offset(0);
let updates = payment_update_batch_from_offset(0);
(fixture, updates)
},
|(fixture, updates)| fixture.write_payment_update_batch(updates),
BatchSize::SmallInput,
)
},
);

group.bench_with_input(
BenchmarkId::new("reload_100_cold", backend.name()),
&backend,
|b, backend| {
b.iter_batched(
|| {
let fixture = StoreFixture::new(*backend, "payment_reload");
fixture.write_payment_batch_from_offset(0);
fixture
},
|fixture| {
let payments = fixture.reload_payments();
std::hint::black_box(payments);
},
BatchSize::SmallInput,
)
},
);
}
group.finish();
}

fn benchmark_payment_store_paginated(c: &mut Criterion, backends: &[Backend]) {
let mut group = c.benchmark_group("database/payment_store_paginated");
group.throughput(Throughput::Elements(PAGINATED_PAGE_LEN));
let runtime = Arc::new(
tokio::runtime::Builder::new_multi_thread().worker_threads(2).enable_all().build().unwrap(),
);
for backend in backends.iter().copied() {
let Some(fixture) =
PaginatedStoreFixture::new(backend, "payment_list_page", Arc::clone(&runtime))
else {
continue;
};
group.bench_function(BenchmarkId::new("list_page_from_10k", backend.name()), |b| {
b.to_async(runtime.as_ref()).iter(|| async {
let page_len = fixture.list_first_page();
let page_len = page_len.await;
debug_assert_eq!(page_len, PAGINATED_PAGE_LEN as usize);
std::hint::black_box(page_len);
})
});
group.bench_function(BenchmarkId::new("list_second_page_from_10k", backend.name()), |b| {
b.to_async(runtime.as_ref()).iter(|| async {
let page_len = fixture.list_second_page();
let page_len = page_len.await;
debug_assert_eq!(page_len, PAGINATED_PAGE_LEN as usize);
std::hint::black_box(page_len);
})
});
}
group.finish();
}

fn benchmark_pending_payment_store(c: &mut Criterion, backends: &[Backend]) {
let mut group = c.benchmark_group("database/pending_payment_store");
group.throughput(Throughput::Elements(BATCH_LEN));
for backend in backends.iter().copied() {
group.bench_with_input(
BenchmarkId::new("insert_100_sequential_cold", backend.name()),
&backend,
|b, backend| {
b.iter_batched(
|| {
let fixture = StoreFixture::new(*backend, "pending_payment_insert");
let payments = pending_payment_details_batch_from_offset(0);
(fixture, payments)
},
|(fixture, payments)| fixture.write_pending_payment_batch(payments),
BatchSize::SmallInput,
)
},
);

group.bench_with_input(
BenchmarkId::new("update_100_sequential_cold", backend.name()),
&backend,
|b, backend| {
b.iter_batched(
|| {
let fixture = StoreFixture::new(*backend, "pending_payment_update");
let payments = pending_payment_details_batch_from_offset(0);
fixture.write_pending_payment_batch(payments);
let updates = pending_payment_update_batch_from_offset(0);
(fixture, updates)
},
|(fixture, updates)| fixture.write_pending_payment_update_batch(updates),
BatchSize::SmallInput,
)
},
);
}
group.finish();
}

fn benchmark_payment_store_single_ops(c: &mut Criterion, backends: &[Backend]) {
let mut group = c.benchmark_group("database/payment_store_single");
group.throughput(Throughput::Elements(1));
for backend in backends.iter().copied() {
let fixture = StoreFixture::new(backend, "single_write_new_key");
let next_key = AtomicU64::new(0);
group.bench_function(BenchmarkId::new("write_new_key", backend.name()), |b| {
b.iter(|| {
let idx = next_key.fetch_add(1, Ordering::Relaxed);
fixture.write_payment(idx);
})
});

let fixture = StoreFixture::new(backend, "single_update_existing_key");
fixture.write_payment(0);
group.bench_function(BenchmarkId::new("write_existing_key", backend.name()), |b| {
b.iter(|| fixture.write_payment_update(0))
});

let fixture = StoreFixture::new(backend, "single_read_existing_key");
fixture.write_payment(0);
group.bench_function(BenchmarkId::new("read_existing_key", backend.name()), |b| {
b.iter(|| {
let payment = fixture.read_payment(0);
std::hint::black_box(payment);
})
});

group.bench_with_input(
BenchmarkId::new("remove_existing_key", backend.name()),
&backend,
|b, backend| {
let next_key = AtomicU64::new(0);
b.iter_batched(
|| {
let fixture = StoreFixture::new(*backend, "single_remove_existing_key");
let idx = next_key.fetch_add(1, Ordering::Relaxed);
fixture.write_payment(idx);
(fixture, payment_key(idx))
},
|(fixture, key)| fixture.remove_payment_key(&key),
BatchSize::SmallInput,
)
},
);
}
group.finish();
}

fn benchmark_payment_store_warm_sequential(c: &mut Criterion, backends: &[Backend]) {
let mut group = c.benchmark_group("database/payment_store_warm");
group.throughput(Throughput::Elements(BATCH_LEN));
for backend in backends.iter().copied() {
let fixture = StoreFixture::new(backend, "payment_insert_100_sequential_warm");
let next_offset = AtomicU64::new(0);
group.bench_function(BenchmarkId::new("insert_100_sequential", backend.name()), |b| {
b.iter(|| {
let offset = next_offset.fetch_add(BATCH_LEN, Ordering::Relaxed);
fixture.write_payment_batch_from_offset(offset);
})
});
}
group.finish();
}

fn benchmark_payment_store_concurrent(c: &mut Criterion, backends: &[Backend]) {
let mut group = c.benchmark_group("database/payment_store_concurrent");
group.throughput(Throughput::Elements(BATCH_LEN));
for backend in backends.iter().copied() {
let fixture = StoreFixture::new(backend, "payment_insert_100_concurrent_distinct");
let next_offset = AtomicU64::new(0);
group.bench_function(BenchmarkId::new("insert_100_distinct_keys", backend.name()), |b| {
b.iter(|| {
let offset = next_offset.fetch_add(BATCH_LEN, Ordering::Relaxed);
fixture.write_payment_batch_concurrent(offset, false);
})
});

let fixture = StoreFixture::new(backend, "payment_insert_100_concurrent_same_key");
group.bench_function(BenchmarkId::new("insert_100_same_key", backend.name()), |b| {
b.iter(|| fixture.write_payment_batch_concurrent(0, true))
});
}
group.finish();
}

fn benchmark_payment_store_lifecycle(c: &mut Criterion, backends: &[Backend]) {
let mut group = c.benchmark_group("database/payment_store_lifecycle");
group.throughput(Throughput::Elements(1));
for backend in backends.iter().copied() {
let fixture = StoreFixture::new(backend, "payment_lifecycle");
let next_key = AtomicU64::new(0);
group.bench_function(BenchmarkId::new("insert_update_read", backend.name()), |b| {
b.iter(|| {
let idx = next_key.fetch_add(1, Ordering::Relaxed);
let payment = fixture.insert_update_read_payment(idx);
std::hint::black_box(payment);
})
});
}
group.finish();
}

criterion_group!(benches, database_benchmark);
criterion_main!(benches);
Loading