Add database persistence benchmarks - #915

Open
benthecarman wants to merge 6 commits into
lightningdevkit:mainfrom
benthecarman:db-bench
Open

Add database persistence benchmarks#915
benthecarman wants to merge 6 commits into
lightningdevkit:mainfrom
benthecarman:db-bench

Conversation

@benthecarman

@benthecarmanbenthecarman commented May 31, 2026

Copy link
Copy Markdown
Contributor

Closes#908

Benchmark realistic payment and pending-payment persistence workloads
across filesystem, SQLite, and optional PostgreSQL stores. Use the async
KV-store APIs so the measured paths match the database interfaces used by
async persistence.

Also adds support for different db backends to the current payment benchmark.

Benchmark Results

Results on my computer with a Ryzen 7 5800XT 8-Core, 16-Thread CPU

Operations

Benchfilesystemsqlitepostgres
channel_open4.99 ms5.38 ms14.74 ms
forwarding5.13 s5.16 s5.70 s
channel_open_close5.07 s5.20 s5.08 s
payments5.69 s6.26 s11.73 s

Startup

Scenariofilesystemsqlitepostgres
channels_1_payments_210.37 ms12.04 ms57.57 ms
channels_10_payments_212.02 ms16.68 ms56.77 ms
channels_100_payments_218.37 ms50.61 ms62.03 ms
channels_100_payments_100017.77 ms70.26 ms61.86 ms

Database Hot Path

Benchfilesystemsqlitepostgres
channel_open_like0.26 ms0.46 ms7.54 ms
forwarding_25_like5.00 ms10.56 ms124.89 ms

@ldk-reviews-bot

ldk-reviews-bot commented May 31, 2026

Copy link
Copy Markdown

👋 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.

@benthecarmanbenthecarman mentioned this pull request May 31, 2026
@benthecarman
benthecarmanforce-pushed the db-bench branch 3 times, most recently from 5f75dd9 to bc97c3aCompareJune 2, 2026 22:19
@tnull

tnull commented Jun 3, 2026

Copy link
Copy Markdown
Collaborator

Feel free to rebase on top of #919 and let me know if you see any differences in the benchmarks.

@benthecarman

benthecarman commented Jun 3, 2026

Copy link
Copy Markdown
ContributorAuthor

codex summary:

Benchmark Results

Payment Benchmarks

BackendPreviousCurrentChange
filesystem6.2475 s6.2138 sslightly faster
sqlite6.7378 s6.6388 sfaster
postgres17.669 s17.489 sslightly faster; Criterion reported no significant change

Lower is better. Delta compares the async-branch rerun against the previous saved Criterion results.

BenchBackendPreviousAsync rerunDelta
single write newfilesystem1.844 ms1.847 ms+0.2%
single write newsqlite2.673 ms2.658 ms-0.6%
single write newpostgres636.0 us630.1 us-0.9%
single write existingfilesystem1.860 ms1.881 ms+1.1%
single write existingsqlite22.1 us22.5 us+2.0%
single write existingpostgres638.1 us624.9 us-2.1%
single readfilesystem14.7 us15.1 us+2.2%
single readsqlite17.0 us17.3 us+1.7%
single readpostgres145.0 us146.2 us+0.8%
single removefilesystem908.8 us920.0 us+1.2%
single removesqlite2.842 ms2.728 ms-4.0%
single removepostgres1.445 ms1.470 ms+1.8%
warm insert 100filesystem185.626 ms185.919 ms+0.2%
warm insert 100sqlite276.593 ms269.530 ms-2.6%
warm insert 100postgres63.417 ms62.791 ms-1.0%
concurrent distinct 100filesystem10.964 ms10.909 ms-0.5%
concurrent distinct 100sqlite272.638 ms261.112 ms-4.2%
concurrent distinct 100postgres9.177 ms9.230 ms+0.6%
concurrent same-key 100filesystem11.495 ms13.050 ms+13.5%
concurrent same-key 100sqlite249.663 ms191.029 ms-23.5%
concurrent same-key 100postgres58.739 ms56.400 ms-4.0%
cold insert 100filesystem185.829 ms183.843 ms-1.1%
cold insert 100sqlite266.497 ms307.448 ms+15.4%
cold insert 100postgres74.440 ms72.031 ms-3.2%
cold update 100filesystem188.561 ms186.808 ms-0.9%
cold update 100sqlite247.062 ms246.978 ms-0.0%
cold update 100postgres78.208 ms74.185 ms-5.1%
reload 100filesystem2.995 ms3.002 ms+0.2%
reload 100sqlite3.532 ms3.499 ms-0.9%
reload 100postgres3.237 ms3.152 ms-2.7%
first page from 10kfilesystem25.290 ms25.090 ms-0.8%
first page from 10ksqlite29.8 us24.8 us-17.0%
first page from 10kpostgres184.5 us182.3 us-1.2%
second page from 10kfilesystem50.665 ms50.422 ms-0.5%
second page from 10ksqlite66.5 us50.8 us-23.7%
second page from 10kpostgres376.7 us372.7 us-1.1%
lifecycle insert-update-readfilesystem6.786 ms3.736 ms-44.9%
lifecycle insert-update-readsqlite9.866 ms5.216 ms-47.1%
lifecycle insert-update-readpostgres2.533 ms1.617 ms-36.2%
pending insert 100filesystem273.515 ms183.380 ms-33.0%
pending insert 100sqlite333.859 ms262.138 ms-21.5%
pending insert 100postgres75.244 ms75.999 ms+1.0%
pending update 100filesystem197.544 ms187.656 ms-5.0%
pending update 100sqlite257.055 ms264.137 ms+2.8%
pending update 100postgres75.756 ms63.715 ms-15.9%

Takeaways

  • The async branch materially improves the lifecycle workload across all stores: filesystem improved by 44.9%, sqlite by 47.1%, and Postgres by 36.2%.
  • Postgres pending-payment updates improved meaningfully, dropping from 75.756 ms to 63.715 ms, a 15.9% reduction.
  • Most single-operation Postgres results are roughly unchanged, with small improvements on writes and small regressions/noise on reads/removes.
  • Postgres cold payment-store writes improved modestly: inserts improved by 3.2% and updates by 5.1%.
  • SQLite saw large improvements in paginated reads and same-key concurrent writes, but cold insert regressed by 15.4%, so that path is worth a closer look.
  • Payment benchmarks are still not trustworthy for comparison because the workload does not complete cleanly and times out waiting for HTLC slots.

@joostjager

Copy link
Copy Markdown
Contributor

It would have been nice if async persistence would show a clear performance win here. The store-level smoke tests are useful, but the closest thing to real node load seems to be the payment benchmark, and that does not show a meaningful improvement, putting aside that it is marked as not reliable yet.

Does this tell us much about high-load node performance in practice? If not, what would be the next useful benchmark?

@tnull

tnull commented Jun 4, 2026

Copy link
Copy Markdown
Collaborator

It would have been nice if async persistence would show a clear performance win here. The store-level smoke tests are useful, but the closest thing to real node load seems to be the payment benchmark, and that does not show a meaningful improvement, putting aside that it is marked as not reliable yet.

Hmm, note that 'async persistence' means pre and post #919 AFAIU? I.e., it does not refer to LDK's async persistence itself, which we switched to pre-#919.

@benthecarman

Copy link
Copy Markdown
ContributorAuthor

Hmm, note that 'async persistence' means pre and post #919 AFAIU? I.e., it does not refer to LDK's async persistence itself, which we switched to pre-#919.

yeah that is pre/post 919

@benthecarman

Copy link
Copy Markdown
ContributorAuthor

Does this tell us much about high-load node performance in practice? If not, what would be the next useful benchmark?

Added a few more with start up time, forwarding payments, and channel opens. But working on making these more robust

@benthecarman
benthecarmanforce-pushed the db-bench branch 3 times, most recently from fedeaf4 to c8f415fCompareJune 9, 2026 15:11
@benthecarman
benthecarman requested a review from tnullJune 9, 2026 15:25
@benthecarman
benthecarman marked this pull request as ready for review June 9, 2026 15:25
@benthecarman

benthecarman commented Jun 9, 2026

Copy link
Copy Markdown
ContributorAuthor

Since #919 is merged marking this ready for review.

Hopefully we get CI re-enabled because I am not sure how much these changes effect how long the bench CI job takes. The whole suite takes awhile so I tried trimming it down for CI. That subset takes a few minutes on my computer but unsure with the CI computers

@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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 3rd 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

🔔 4th 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

🔔 5th 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

🔔 6th 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.

@tnulltnull left a comment

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 when we add this we should also document somewhere how to run benchmarks as guidance for contributors.

Besides that, current PR is a quite sloppy. Please clean up before we can move forward here (and split out MigratebleKVStore impls, for instance).

Comment threadsrc/io/postgres_store/mod.rs Outdated

fn test_connection_string() -> String {
std::env::var("TEST_POSTGRES_URL")
dotenvy::dotenv().ok();

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.

What is dotenvy? This commit wont compile.

Comment threadCargo.toml Outdated
proptest = "1.0.0"
regex = "1.5.6"
criterion = { version = "0.7.0", features = ["async_tokio"] }
dotenvy = "0.15"

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 don't think we should take a dependency for this?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

was simple way to read .env files, made doing test loops faster instead of always doing TEST_POSTGRES_URL=blah cargo test but can remove if not wanted. Figured was fine because just dev-dep

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, we're generally more lax about adding dev-dependencies, but this seems very unnecessary as reading env variables can be done via std, and reading files should be a ~5 line helper?

Comment threadsrc/bench.rs Outdated

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.

Can all benchmark mark-related files rather live in the benches sub-folder? Not sure we want to take on this huge file that's only boilerplate/utilities in the main src dir..

Comment threadbenches/payments.rs

// Set up two nodes. Because this is slow, we reuse the same nodes for each sample.
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();
let chain_source = random_chain_source(&bitcoind, &electrsd);

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.

Please don't introduce that much churn for code you just added in the last commit.

Comment threadbenches/payments.rs
Comment threadbenches/operations.rs
panic!("Timed out waiting for forwarding path readiness");
}

async fn open_channel(

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.

This is just a duplicate of the test util? Why copy this over?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Needed a separate one because the test utils one waits for the tx to be in the mempool. Didn't want to that to leak into the benchmark. Refactored to use a shared util

Comment threadsrc/io/postgres_store/mod.rs
Comment threadbenches/operations.rs Outdated
Comment threadsrc/io/postgres_store/mod.rs Outdated
Comment threadbenches/operations.rs Outdated
Comment threadsrc/bench.rs Outdated
@joostjager

Copy link
Copy Markdown
Contributor

Small note: I’m experimenting with this PR locally and extending the benchmarks to measure single-channel payment performance with many open but unused channels, up to 5000. Since deferred writing makes channel updates scale with the total number of channels, I’m curious to see how much impact that has.

@joostjager

Copy link
Copy Markdown
Contributor

Small note: I’m experimenting with this PR locally and extending the benchmarks to measure single-channel payment performance with many open but unused channels, up to 5000. Since deferred writing makes channel updates scale with the total number of channels, I’m curious to see how much impact that has.

https://git.rust-bitcoin.org/lightningdevkit/rust-lightning/issues/4768

AI-Assisted-By: OpenAI Codex
Comment threadbenches/operations.rs
Comment threadbenches/operations.rs Outdated
Comment threadbenches/common/db_store.rs Outdated
Benchmark realistic payment and pending-payment persistence workloads across
filesystem, SQLite, and optional PostgreSQL stores. Use the async KV-store
APIs so the measured paths match the database interfaces used by async
persistence.
AI-Assisted: Codex
Run the existing payments benchmark once per configured store backend so
filesystem, SQLite, and optional PostgreSQL results are reported under the
same payment flow.
AI-assisted-by: OpenAI Codex
Add an operations bench target with a forwarding benchmark that compares
sqlite, filesystem, and postgres stores over a settled multi-hop payment.
AI-assisted-by: OpenAI Codex
Add a channel-open benchmark that measures the open_channel call while
leaving chain confirmation cleanup outside the timed section.
AI-assisted-by: OpenAI Codex
Add a startup benchmark that restarts a node whose store already
contains channel and payment data, so startup cost reflects persisted
node state.
AI-assisted-by: OpenAI Codex
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Benchmark database options

4 participants

@benthecarman@ldk-reviews-bot@tnull@joostjager
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Add database persistence benchmarks - #915

Open
benthecarman wants to merge 6 commits into
lightningdevkit:mainfrom
benthecarman:db-bench
Open

Add database persistence benchmarks#915
benthecarman wants to merge 6 commits into
lightningdevkit:mainfrom
benthecarman:db-bench

Conversation

@benthecarman

@benthecarmanbenthecarman commented May 31, 2026

Copy link
Copy Markdown
Contributor

Closes#908

Benchmark realistic payment and pending-payment persistence workloads
across filesystem, SQLite, and optional PostgreSQL stores. Use the async
KV-store APIs so the measured paths match the database interfaces used by
async persistence.

Also adds support for different db backends to the current payment benchmark.

Benchmark Results

Results on my computer with a Ryzen 7 5800XT 8-Core, 16-Thread CPU

Operations

Benchfilesystemsqlitepostgres
channel_open4.99 ms5.38 ms14.74 ms
forwarding5.13 s5.16 s5.70 s
channel_open_close5.07 s5.20 s5.08 s
payments5.69 s6.26 s11.73 s

Startup

Scenariofilesystemsqlitepostgres
channels_1_payments_210.37 ms12.04 ms57.57 ms
channels_10_payments_212.02 ms16.68 ms56.77 ms
channels_100_payments_218.37 ms50.61 ms62.03 ms
channels_100_payments_100017.77 ms70.26 ms61.86 ms

Database Hot Path

Benchfilesystemsqlitepostgres
channel_open_like0.26 ms0.46 ms7.54 ms
forwarding_25_like5.00 ms10.56 ms124.89 ms

@ldk-reviews-bot

ldk-reviews-bot commented May 31, 2026

Copy link
Copy Markdown

👋 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.

@benthecarmanbenthecarman mentioned this pull request May 31, 2026
@benthecarman
benthecarmanforce-pushed the db-bench branch 3 times, most recently from 5f75dd9 to bc97c3aCompareJune 2, 2026 22:19
@tnull

tnull commented Jun 3, 2026

Copy link
Copy Markdown
Collaborator

Feel free to rebase on top of #919 and let me know if you see any differences in the benchmarks.

@benthecarman

benthecarman commented Jun 3, 2026

Copy link
Copy Markdown
ContributorAuthor

codex summary:

Benchmark Results

Payment Benchmarks

BackendPreviousCurrentChange
filesystem6.2475 s6.2138 sslightly faster
sqlite6.7378 s6.6388 sfaster
postgres17.669 s17.489 sslightly faster; Criterion reported no significant change

Lower is better. Delta compares the async-branch rerun against the previous saved Criterion results.

BenchBackendPreviousAsync rerunDelta
single write newfilesystem1.844 ms1.847 ms+0.2%
single write newsqlite2.673 ms2.658 ms-0.6%
single write newpostgres636.0 us630.1 us-0.9%
single write existingfilesystem1.860 ms1.881 ms+1.1%
single write existingsqlite22.1 us22.5 us+2.0%
single write existingpostgres638.1 us624.9 us-2.1%
single readfilesystem14.7 us15.1 us+2.2%
single readsqlite17.0 us17.3 us+1.7%
single readpostgres145.0 us146.2 us+0.8%
single removefilesystem908.8 us920.0 us+1.2%
single removesqlite2.842 ms2.728 ms-4.0%
single removepostgres1.445 ms1.470 ms+1.8%
warm insert 100filesystem185.626 ms185.919 ms+0.2%
warm insert 100sqlite276.593 ms269.530 ms-2.6%
warm insert 100postgres63.417 ms62.791 ms-1.0%
concurrent distinct 100filesystem10.964 ms10.909 ms-0.5%
concurrent distinct 100sqlite272.638 ms261.112 ms-4.2%
concurrent distinct 100postgres9.177 ms9.230 ms+0.6%
concurrent same-key 100filesystem11.495 ms13.050 ms+13.5%
concurrent same-key 100sqlite249.663 ms191.029 ms-23.5%
concurrent same-key 100postgres58.739 ms56.400 ms-4.0%
cold insert 100filesystem185.829 ms183.843 ms-1.1%
cold insert 100sqlite266.497 ms307.448 ms+15.4%
cold insert 100postgres74.440 ms72.031 ms-3.2%
cold update 100filesystem188.561 ms186.808 ms-0.9%
cold update 100sqlite247.062 ms246.978 ms-0.0%
cold update 100postgres78.208 ms74.185 ms-5.1%
reload 100filesystem2.995 ms3.002 ms+0.2%
reload 100sqlite3.532 ms3.499 ms-0.9%
reload 100postgres3.237 ms3.152 ms-2.7%
first page from 10kfilesystem25.290 ms25.090 ms-0.8%
first page from 10ksqlite29.8 us24.8 us-17.0%
first page from 10kpostgres184.5 us182.3 us-1.2%
second page from 10kfilesystem50.665 ms50.422 ms-0.5%
second page from 10ksqlite66.5 us50.8 us-23.7%
second page from 10kpostgres376.7 us372.7 us-1.1%
lifecycle insert-update-readfilesystem6.786 ms3.736 ms-44.9%
lifecycle insert-update-readsqlite9.866 ms5.216 ms-47.1%
lifecycle insert-update-readpostgres2.533 ms1.617 ms-36.2%
pending insert 100filesystem273.515 ms183.380 ms-33.0%
pending insert 100sqlite333.859 ms262.138 ms-21.5%
pending insert 100postgres75.244 ms75.999 ms+1.0%
pending update 100filesystem197.544 ms187.656 ms-5.0%
pending update 100sqlite257.055 ms264.137 ms+2.8%
pending update 100postgres75.756 ms63.715 ms-15.9%

Takeaways

  • The async branch materially improves the lifecycle workload across all stores: filesystem improved by 44.9%, sqlite by 47.1%, and Postgres by 36.2%.
  • Postgres pending-payment updates improved meaningfully, dropping from 75.756 ms to 63.715 ms, a 15.9% reduction.
  • Most single-operation Postgres results are roughly unchanged, with small improvements on writes and small regressions/noise on reads/removes.
  • Postgres cold payment-store writes improved modestly: inserts improved by 3.2% and updates by 5.1%.
  • SQLite saw large improvements in paginated reads and same-key concurrent writes, but cold insert regressed by 15.4%, so that path is worth a closer look.
  • Payment benchmarks are still not trustworthy for comparison because the workload does not complete cleanly and times out waiting for HTLC slots.

@joostjager

Copy link
Copy Markdown
Contributor

It would have been nice if async persistence would show a clear performance win here. The store-level smoke tests are useful, but the closest thing to real node load seems to be the payment benchmark, and that does not show a meaningful improvement, putting aside that it is marked as not reliable yet.

Does this tell us much about high-load node performance in practice? If not, what would be the next useful benchmark?

@tnull

tnull commented Jun 4, 2026

Copy link
Copy Markdown
Collaborator

It would have been nice if async persistence would show a clear performance win here. The store-level smoke tests are useful, but the closest thing to real node load seems to be the payment benchmark, and that does not show a meaningful improvement, putting aside that it is marked as not reliable yet.

Hmm, note that 'async persistence' means pre and post #919 AFAIU? I.e., it does not refer to LDK's async persistence itself, which we switched to pre-#919.

@benthecarman

Copy link
Copy Markdown
ContributorAuthor

Hmm, note that 'async persistence' means pre and post #919 AFAIU? I.e., it does not refer to LDK's async persistence itself, which we switched to pre-#919.

yeah that is pre/post 919

@benthecarman

Copy link
Copy Markdown
ContributorAuthor

Does this tell us much about high-load node performance in practice? If not, what would be the next useful benchmark?

Added a few more with start up time, forwarding payments, and channel opens. But working on making these more robust

@benthecarman
benthecarmanforce-pushed the db-bench branch 3 times, most recently from fedeaf4 to c8f415fCompareJune 9, 2026 15:11
@benthecarman
benthecarman requested a review from tnullJune 9, 2026 15:25
@benthecarman
benthecarman marked this pull request as ready for review June 9, 2026 15:25
@benthecarman

benthecarman commented Jun 9, 2026

Copy link
Copy Markdown
ContributorAuthor

Since #919 is merged marking this ready for review.

Hopefully we get CI re-enabled because I am not sure how much these changes effect how long the bench CI job takes. The whole suite takes awhile so I tried trimming it down for CI. That subset takes a few minutes on my computer but unsure with the CI computers

@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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 3rd 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

🔔 4th 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

🔔 5th 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

🔔 6th 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.

@tnulltnull left a comment

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 when we add this we should also document somewhere how to run benchmarks as guidance for contributors.

Besides that, current PR is a quite sloppy. Please clean up before we can move forward here (and split out MigratebleKVStore impls, for instance).

Comment threadsrc/io/postgres_store/mod.rs Outdated

fn test_connection_string() -> String {
std::env::var("TEST_POSTGRES_URL")
dotenvy::dotenv().ok();

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.

What is dotenvy? This commit wont compile.

Comment threadCargo.toml Outdated
proptest = "1.0.0"
regex = "1.5.6"
criterion = { version = "0.7.0", features = ["async_tokio"] }
dotenvy = "0.15"

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 don't think we should take a dependency for this?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

was simple way to read .env files, made doing test loops faster instead of always doing TEST_POSTGRES_URL=blah cargo test but can remove if not wanted. Figured was fine because just dev-dep

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, we're generally more lax about adding dev-dependencies, but this seems very unnecessary as reading env variables can be done via std, and reading files should be a ~5 line helper?

Comment threadsrc/bench.rs Outdated

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.

Can all benchmark mark-related files rather live in the benches sub-folder? Not sure we want to take on this huge file that's only boilerplate/utilities in the main src dir..

Comment threadbenches/payments.rs

// Set up two nodes. Because this is slow, we reuse the same nodes for each sample.
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();
let chain_source = random_chain_source(&bitcoind, &electrsd);

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.

Please don't introduce that much churn for code you just added in the last commit.

Comment threadbenches/payments.rs
Comment threadbenches/operations.rs
panic!("Timed out waiting for forwarding path readiness");
}

async fn open_channel(

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.

This is just a duplicate of the test util? Why copy this over?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Needed a separate one because the test utils one waits for the tx to be in the mempool. Didn't want to that to leak into the benchmark. Refactored to use a shared util

Comment threadsrc/io/postgres_store/mod.rs
Comment threadbenches/operations.rs Outdated
Comment threadsrc/io/postgres_store/mod.rs Outdated
Comment threadbenches/operations.rs Outdated
Comment threadsrc/bench.rs Outdated
@joostjager

Copy link
Copy Markdown
Contributor

Small note: I’m experimenting with this PR locally and extending the benchmarks to measure single-channel payment performance with many open but unused channels, up to 5000. Since deferred writing makes channel updates scale with the total number of channels, I’m curious to see how much impact that has.

@joostjager

Copy link
Copy Markdown
Contributor

Small note: I’m experimenting with this PR locally and extending the benchmarks to measure single-channel payment performance with many open but unused channels, up to 5000. Since deferred writing makes channel updates scale with the total number of channels, I’m curious to see how much impact that has.

https://git.rust-bitcoin.org/lightningdevkit/rust-lightning/issues/4768

AI-Assisted-By: OpenAI Codex
Comment threadbenches/operations.rs
Comment threadbenches/operations.rs Outdated
Comment threadbenches/common/db_store.rs Outdated
Benchmark realistic payment and pending-payment persistence workloads across
filesystem, SQLite, and optional PostgreSQL stores. Use the async KV-store
APIs so the measured paths match the database interfaces used by async
persistence.
AI-Assisted: Codex
Run the existing payments benchmark once per configured store backend so
filesystem, SQLite, and optional PostgreSQL results are reported under the
same payment flow.
AI-assisted-by: OpenAI Codex
Add an operations bench target with a forwarding benchmark that compares
sqlite, filesystem, and postgres stores over a settled multi-hop payment.
AI-assisted-by: OpenAI Codex
Add a channel-open benchmark that measures the open_channel call while
leaving chain confirmation cleanup outside the timed section.
AI-assisted-by: OpenAI Codex
Add a startup benchmark that restarts a node whose store already
contains channel and payment data, so startup cost reflects persisted
node state.
AI-assisted-by: OpenAI Codex
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Benchmark database options

4 participants

@benthecarman@ldk-reviews-bot@tnull@joostjager
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add database persistence benchmarks - #915

Open
benthecarman wants to merge 6 commits into
lightningdevkit:mainfrom
benthecarman:db-bench
Open

Add database persistence benchmarks#915
benthecarman wants to merge 6 commits into
lightningdevkit:mainfrom
benthecarman:db-bench

Conversation

@benthecarman

@benthecarmanbenthecarman commented May 31, 2026

Copy link
Copy Markdown
Contributor

Closes#908

Benchmark realistic payment and pending-payment persistence workloads
across filesystem, SQLite, and optional PostgreSQL stores. Use the async
KV-store APIs so the measured paths match the database interfaces used by
async persistence.

Also adds support for different db backends to the current payment benchmark.

Benchmark Results

Results on my computer with a Ryzen 7 5800XT 8-Core, 16-Thread CPU

Operations

Benchfilesystemsqlitepostgres
channel_open4.99 ms5.38 ms14.74 ms
forwarding5.13 s5.16 s5.70 s
channel_open_close5.07 s5.20 s5.08 s
payments5.69 s6.26 s11.73 s

Startup

Scenariofilesystemsqlitepostgres
channels_1_payments_210.37 ms12.04 ms57.57 ms
channels_10_payments_212.02 ms16.68 ms56.77 ms
channels_100_payments_218.37 ms50.61 ms62.03 ms
channels_100_payments_100017.77 ms70.26 ms61.86 ms

Database Hot Path

Benchfilesystemsqlitepostgres
channel_open_like0.26 ms0.46 ms7.54 ms
forwarding_25_like5.00 ms10.56 ms124.89 ms

@ldk-reviews-bot

ldk-reviews-bot commented May 31, 2026

Copy link
Copy Markdown

👋 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.

@benthecarmanbenthecarman mentioned this pull request May 31, 2026
@benthecarman
benthecarmanforce-pushed the db-bench branch 3 times, most recently from 5f75dd9 to bc97c3aCompareJune 2, 2026 22:19
@tnull

tnull commented Jun 3, 2026

Copy link
Copy Markdown
Collaborator

Feel free to rebase on top of #919 and let me know if you see any differences in the benchmarks.

@benthecarman

benthecarman commented Jun 3, 2026

Copy link
Copy Markdown
ContributorAuthor

codex summary:

Benchmark Results

Payment Benchmarks

BackendPreviousCurrentChange
filesystem6.2475 s6.2138 sslightly faster
sqlite6.7378 s6.6388 sfaster
postgres17.669 s17.489 sslightly faster; Criterion reported no significant change

Lower is better. Delta compares the async-branch rerun against the previous saved Criterion results.

BenchBackendPreviousAsync rerunDelta
single write newfilesystem1.844 ms1.847 ms+0.2%
single write newsqlite2.673 ms2.658 ms-0.6%
single write newpostgres636.0 us630.1 us-0.9%
single write existingfilesystem1.860 ms1.881 ms+1.1%
single write existingsqlite22.1 us22.5 us+2.0%
single write existingpostgres638.1 us624.9 us-2.1%
single readfilesystem14.7 us15.1 us+2.2%
single readsqlite17.0 us17.3 us+1.7%
single readpostgres145.0 us146.2 us+0.8%
single removefilesystem908.8 us920.0 us+1.2%
single removesqlite2.842 ms2.728 ms-4.0%
single removepostgres1.445 ms1.470 ms+1.8%
warm insert 100filesystem185.626 ms185.919 ms+0.2%
warm insert 100sqlite276.593 ms269.530 ms-2.6%
warm insert 100postgres63.417 ms62.791 ms-1.0%
concurrent distinct 100filesystem10.964 ms10.909 ms-0.5%
concurrent distinct 100sqlite272.638 ms261.112 ms-4.2%
concurrent distinct 100postgres9.177 ms9.230 ms+0.6%
concurrent same-key 100filesystem11.495 ms13.050 ms+13.5%
concurrent same-key 100sqlite249.663 ms191.029 ms-23.5%
concurrent same-key 100postgres58.739 ms56.400 ms-4.0%
cold insert 100filesystem185.829 ms183.843 ms-1.1%
cold insert 100sqlite266.497 ms307.448 ms+15.4%
cold insert 100postgres74.440 ms72.031 ms-3.2%
cold update 100filesystem188.561 ms186.808 ms-0.9%
cold update 100sqlite247.062 ms246.978 ms-0.0%
cold update 100postgres78.208 ms74.185 ms-5.1%
reload 100filesystem2.995 ms3.002 ms+0.2%
reload 100sqlite3.532 ms3.499 ms-0.9%
reload 100postgres3.237 ms3.152 ms-2.7%
first page from 10kfilesystem25.290 ms25.090 ms-0.8%
first page from 10ksqlite29.8 us24.8 us-17.0%
first page from 10kpostgres184.5 us182.3 us-1.2%
second page from 10kfilesystem50.665 ms50.422 ms-0.5%
second page from 10ksqlite66.5 us50.8 us-23.7%
second page from 10kpostgres376.7 us372.7 us-1.1%
lifecycle insert-update-readfilesystem6.786 ms3.736 ms-44.9%
lifecycle insert-update-readsqlite9.866 ms5.216 ms-47.1%
lifecycle insert-update-readpostgres2.533 ms1.617 ms-36.2%
pending insert 100filesystem273.515 ms183.380 ms-33.0%
pending insert 100sqlite333.859 ms262.138 ms-21.5%
pending insert 100postgres75.244 ms75.999 ms+1.0%
pending update 100filesystem197.544 ms187.656 ms-5.0%
pending update 100sqlite257.055 ms264.137 ms+2.8%
pending update 100postgres75.756 ms63.715 ms-15.9%

Takeaways

  • The async branch materially improves the lifecycle workload across all stores: filesystem improved by 44.9%, sqlite by 47.1%, and Postgres by 36.2%.
  • Postgres pending-payment updates improved meaningfully, dropping from 75.756 ms to 63.715 ms, a 15.9% reduction.
  • Most single-operation Postgres results are roughly unchanged, with small improvements on writes and small regressions/noise on reads/removes.
  • Postgres cold payment-store writes improved modestly: inserts improved by 3.2% and updates by 5.1%.
  • SQLite saw large improvements in paginated reads and same-key concurrent writes, but cold insert regressed by 15.4%, so that path is worth a closer look.
  • Payment benchmarks are still not trustworthy for comparison because the workload does not complete cleanly and times out waiting for HTLC slots.

@joostjager

Copy link
Copy Markdown
Contributor

It would have been nice if async persistence would show a clear performance win here. The store-level smoke tests are useful, but the closest thing to real node load seems to be the payment benchmark, and that does not show a meaningful improvement, putting aside that it is marked as not reliable yet.

Does this tell us much about high-load node performance in practice? If not, what would be the next useful benchmark?

@tnull

tnull commented Jun 4, 2026

Copy link
Copy Markdown
Collaborator

It would have been nice if async persistence would show a clear performance win here. The store-level smoke tests are useful, but the closest thing to real node load seems to be the payment benchmark, and that does not show a meaningful improvement, putting aside that it is marked as not reliable yet.

Hmm, note that 'async persistence' means pre and post #919 AFAIU? I.e., it does not refer to LDK's async persistence itself, which we switched to pre-#919.

@benthecarman

Copy link
Copy Markdown
ContributorAuthor

Hmm, note that 'async persistence' means pre and post #919 AFAIU? I.e., it does not refer to LDK's async persistence itself, which we switched to pre-#919.

yeah that is pre/post 919

@benthecarman

Copy link
Copy Markdown
ContributorAuthor

Does this tell us much about high-load node performance in practice? If not, what would be the next useful benchmark?

Added a few more with start up time, forwarding payments, and channel opens. But working on making these more robust

@benthecarman
benthecarmanforce-pushed the db-bench branch 3 times, most recently from fedeaf4 to c8f415fCompareJune 9, 2026 15:11
@benthecarman
benthecarman requested a review from tnullJune 9, 2026 15:25
@benthecarman
benthecarman marked this pull request as ready for review June 9, 2026 15:25
@benthecarman

benthecarman commented Jun 9, 2026

Copy link
Copy Markdown
ContributorAuthor

Since #919 is merged marking this ready for review.

Hopefully we get CI re-enabled because I am not sure how much these changes effect how long the bench CI job takes. The whole suite takes awhile so I tried trimming it down for CI. That subset takes a few minutes on my computer but unsure with the CI computers

@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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 3rd 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

🔔 4th 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

🔔 5th 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

🔔 6th 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.

@tnulltnull left a comment

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 when we add this we should also document somewhere how to run benchmarks as guidance for contributors.

Besides that, current PR is a quite sloppy. Please clean up before we can move forward here (and split out MigratebleKVStore impls, for instance).

Comment threadsrc/io/postgres_store/mod.rs Outdated

fn test_connection_string() -> String {
std::env::var("TEST_POSTGRES_URL")
dotenvy::dotenv().ok();

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.

What is dotenvy? This commit wont compile.

Comment threadCargo.toml Outdated
proptest = "1.0.0"
regex = "1.5.6"
criterion = { version = "0.7.0", features = ["async_tokio"] }
dotenvy = "0.15"

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 don't think we should take a dependency for this?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

was simple way to read .env files, made doing test loops faster instead of always doing TEST_POSTGRES_URL=blah cargo test but can remove if not wanted. Figured was fine because just dev-dep

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, we're generally more lax about adding dev-dependencies, but this seems very unnecessary as reading env variables can be done via std, and reading files should be a ~5 line helper?

Comment threadsrc/bench.rs Outdated

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.

Can all benchmark mark-related files rather live in the benches sub-folder? Not sure we want to take on this huge file that's only boilerplate/utilities in the main src dir..

Comment threadbenches/payments.rs

// Set up two nodes. Because this is slow, we reuse the same nodes for each sample.
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();
let chain_source = random_chain_source(&bitcoind, &electrsd);

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.

Please don't introduce that much churn for code you just added in the last commit.

Comment threadbenches/payments.rs
Comment threadbenches/operations.rs
panic!("Timed out waiting for forwarding path readiness");
}

async fn open_channel(

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.

This is just a duplicate of the test util? Why copy this over?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Needed a separate one because the test utils one waits for the tx to be in the mempool. Didn't want to that to leak into the benchmark. Refactored to use a shared util

Comment threadsrc/io/postgres_store/mod.rs
Comment threadbenches/operations.rs Outdated
Comment threadsrc/io/postgres_store/mod.rs Outdated
Comment threadbenches/operations.rs Outdated
Comment threadsrc/bench.rs Outdated
@joostjager

Copy link
Copy Markdown
Contributor

Small note: I’m experimenting with this PR locally and extending the benchmarks to measure single-channel payment performance with many open but unused channels, up to 5000. Since deferred writing makes channel updates scale with the total number of channels, I’m curious to see how much impact that has.

@joostjager

Copy link
Copy Markdown
Contributor

Small note: I’m experimenting with this PR locally and extending the benchmarks to measure single-channel payment performance with many open but unused channels, up to 5000. Since deferred writing makes channel updates scale with the total number of channels, I’m curious to see how much impact that has.

https://git.rust-bitcoin.org/lightningdevkit/rust-lightning/issues/4768

AI-Assisted-By: OpenAI Codex
Comment threadbenches/operations.rs
Comment threadbenches/operations.rs Outdated
Comment threadbenches/common/db_store.rs Outdated
Benchmark realistic payment and pending-payment persistence workloads across
filesystem, SQLite, and optional PostgreSQL stores. Use the async KV-store
APIs so the measured paths match the database interfaces used by async
persistence.
AI-Assisted: Codex
Run the existing payments benchmark once per configured store backend so
filesystem, SQLite, and optional PostgreSQL results are reported under the
same payment flow.
AI-assisted-by: OpenAI Codex
Add an operations bench target with a forwarding benchmark that compares
sqlite, filesystem, and postgres stores over a settled multi-hop payment.
AI-assisted-by: OpenAI Codex
Add a channel-open benchmark that measures the open_channel call while
leaving chain confirmation cleanup outside the timed section.
AI-assisted-by: OpenAI Codex
Add a startup benchmark that restarts a node whose store already
contains channel and payment data, so startup cost reflects persisted
node state.
AI-assisted-by: OpenAI Codex
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Benchmark database options

4 participants

@benthecarman@ldk-reviews-bot@tnull@joostjager
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add database persistence benchmarks - #915

Open
benthecarman wants to merge 6 commits into
lightningdevkit:mainfrom
benthecarman:db-bench
Open

Add database persistence benchmarks#915
benthecarman wants to merge 6 commits into
lightningdevkit:mainfrom
benthecarman:db-bench

Conversation

@benthecarman

@benthecarmanbenthecarman commented May 31, 2026

Copy link
Copy Markdown
Contributor

Closes#908

Benchmark realistic payment and pending-payment persistence workloads
across filesystem, SQLite, and optional PostgreSQL stores. Use the async
KV-store APIs so the measured paths match the database interfaces used by
async persistence.

Also adds support for different db backends to the current payment benchmark.

Benchmark Results

Results on my computer with a Ryzen 7 5800XT 8-Core, 16-Thread CPU

Operations

Benchfilesystemsqlitepostgres
channel_open4.99 ms5.38 ms14.74 ms
forwarding5.13 s5.16 s5.70 s
channel_open_close5.07 s5.20 s5.08 s
payments5.69 s6.26 s11.73 s

Startup

Scenariofilesystemsqlitepostgres
channels_1_payments_210.37 ms12.04 ms57.57 ms
channels_10_payments_212.02 ms16.68 ms56.77 ms
channels_100_payments_218.37 ms50.61 ms62.03 ms
channels_100_payments_100017.77 ms70.26 ms61.86 ms

Database Hot Path

Benchfilesystemsqlitepostgres
channel_open_like0.26 ms0.46 ms7.54 ms
forwarding_25_like5.00 ms10.56 ms124.89 ms

@ldk-reviews-bot

ldk-reviews-bot commented May 31, 2026

Copy link
Copy Markdown

👋 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.

@benthecarmanbenthecarman mentioned this pull request May 31, 2026
@benthecarman
benthecarmanforce-pushed the db-bench branch 3 times, most recently from 5f75dd9 to bc97c3aCompareJune 2, 2026 22:19
@tnull

tnull commented Jun 3, 2026

Copy link
Copy Markdown
Collaborator

Feel free to rebase on top of #919 and let me know if you see any differences in the benchmarks.

@benthecarman

benthecarman commented Jun 3, 2026

Copy link
Copy Markdown
ContributorAuthor

codex summary:

Benchmark Results

Payment Benchmarks

BackendPreviousCurrentChange
filesystem6.2475 s6.2138 sslightly faster
sqlite6.7378 s6.6388 sfaster
postgres17.669 s17.489 sslightly faster; Criterion reported no significant change

Lower is better. Delta compares the async-branch rerun against the previous saved Criterion results.

BenchBackendPreviousAsync rerunDelta
single write newfilesystem1.844 ms1.847 ms+0.2%
single write newsqlite2.673 ms2.658 ms-0.6%
single write newpostgres636.0 us630.1 us-0.9%
single write existingfilesystem1.860 ms1.881 ms+1.1%
single write existingsqlite22.1 us22.5 us+2.0%
single write existingpostgres638.1 us624.9 us-2.1%
single readfilesystem14.7 us15.1 us+2.2%
single readsqlite17.0 us17.3 us+1.7%
single readpostgres145.0 us146.2 us+0.8%
single removefilesystem908.8 us920.0 us+1.2%
single removesqlite2.842 ms2.728 ms-4.0%
single removepostgres1.445 ms1.470 ms+1.8%
warm insert 100filesystem185.626 ms185.919 ms+0.2%
warm insert 100sqlite276.593 ms269.530 ms-2.6%
warm insert 100postgres63.417 ms62.791 ms-1.0%
concurrent distinct 100filesystem10.964 ms10.909 ms-0.5%
concurrent distinct 100sqlite272.638 ms261.112 ms-4.2%
concurrent distinct 100postgres9.177 ms9.230 ms+0.6%
concurrent same-key 100filesystem11.495 ms13.050 ms+13.5%
concurrent same-key 100sqlite249.663 ms191.029 ms-23.5%
concurrent same-key 100postgres58.739 ms56.400 ms-4.0%
cold insert 100filesystem185.829 ms183.843 ms-1.1%
cold insert 100sqlite266.497 ms307.448 ms+15.4%
cold insert 100postgres74.440 ms72.031 ms-3.2%
cold update 100filesystem188.561 ms186.808 ms-0.9%
cold update 100sqlite247.062 ms246.978 ms-0.0%
cold update 100postgres78.208 ms74.185 ms-5.1%
reload 100filesystem2.995 ms3.002 ms+0.2%
reload 100sqlite3.532 ms3.499 ms-0.9%
reload 100postgres3.237 ms3.152 ms-2.7%
first page from 10kfilesystem25.290 ms25.090 ms-0.8%
first page from 10ksqlite29.8 us24.8 us-17.0%
first page from 10kpostgres184.5 us182.3 us-1.2%
second page from 10kfilesystem50.665 ms50.422 ms-0.5%
second page from 10ksqlite66.5 us50.8 us-23.7%
second page from 10kpostgres376.7 us372.7 us-1.1%
lifecycle insert-update-readfilesystem6.786 ms3.736 ms-44.9%
lifecycle insert-update-readsqlite9.866 ms5.216 ms-47.1%
lifecycle insert-update-readpostgres2.533 ms1.617 ms-36.2%
pending insert 100filesystem273.515 ms183.380 ms-33.0%
pending insert 100sqlite333.859 ms262.138 ms-21.5%
pending insert 100postgres75.244 ms75.999 ms+1.0%
pending update 100filesystem197.544 ms187.656 ms-5.0%
pending update 100sqlite257.055 ms264.137 ms+2.8%
pending update 100postgres75.756 ms63.715 ms-15.9%

Takeaways

  • The async branch materially improves the lifecycle workload across all stores: filesystem improved by 44.9%, sqlite by 47.1%, and Postgres by 36.2%.
  • Postgres pending-payment updates improved meaningfully, dropping from 75.756 ms to 63.715 ms, a 15.9% reduction.
  • Most single-operation Postgres results are roughly unchanged, with small improvements on writes and small regressions/noise on reads/removes.
  • Postgres cold payment-store writes improved modestly: inserts improved by 3.2% and updates by 5.1%.
  • SQLite saw large improvements in paginated reads and same-key concurrent writes, but cold insert regressed by 15.4%, so that path is worth a closer look.
  • Payment benchmarks are still not trustworthy for comparison because the workload does not complete cleanly and times out waiting for HTLC slots.

@joostjager

Copy link
Copy Markdown
Contributor

It would have been nice if async persistence would show a clear performance win here. The store-level smoke tests are useful, but the closest thing to real node load seems to be the payment benchmark, and that does not show a meaningful improvement, putting aside that it is marked as not reliable yet.

Does this tell us much about high-load node performance in practice? If not, what would be the next useful benchmark?

@tnull

tnull commented Jun 4, 2026

Copy link
Copy Markdown
Collaborator

It would have been nice if async persistence would show a clear performance win here. The store-level smoke tests are useful, but the closest thing to real node load seems to be the payment benchmark, and that does not show a meaningful improvement, putting aside that it is marked as not reliable yet.

Hmm, note that 'async persistence' means pre and post #919 AFAIU? I.e., it does not refer to LDK's async persistence itself, which we switched to pre-#919.

@benthecarman

Copy link
Copy Markdown
ContributorAuthor

Hmm, note that 'async persistence' means pre and post #919 AFAIU? I.e., it does not refer to LDK's async persistence itself, which we switched to pre-#919.

yeah that is pre/post 919

@benthecarman

Copy link
Copy Markdown
ContributorAuthor

Does this tell us much about high-load node performance in practice? If not, what would be the next useful benchmark?

Added a few more with start up time, forwarding payments, and channel opens. But working on making these more robust

@benthecarman
benthecarmanforce-pushed the db-bench branch 3 times, most recently from fedeaf4 to c8f415fCompareJune 9, 2026 15:11
@benthecarman
benthecarman requested a review from tnullJune 9, 2026 15:25
@benthecarman
benthecarman marked this pull request as ready for review June 9, 2026 15:25
@benthecarman

benthecarman commented Jun 9, 2026

Copy link
Copy Markdown
ContributorAuthor

Since #919 is merged marking this ready for review.

Hopefully we get CI re-enabled because I am not sure how much these changes effect how long the bench CI job takes. The whole suite takes awhile so I tried trimming it down for CI. That subset takes a few minutes on my computer but unsure with the CI computers

@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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 3rd 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

🔔 4th 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

🔔 5th 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

🔔 6th 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.

@tnulltnull left a comment

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 when we add this we should also document somewhere how to run benchmarks as guidance for contributors.

Besides that, current PR is a quite sloppy. Please clean up before we can move forward here (and split out MigratebleKVStore impls, for instance).

Comment threadsrc/io/postgres_store/mod.rs Outdated

fn test_connection_string() -> String {
std::env::var("TEST_POSTGRES_URL")
dotenvy::dotenv().ok();

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.

What is dotenvy? This commit wont compile.

Comment threadCargo.toml Outdated
proptest = "1.0.0"
regex = "1.5.6"
criterion = { version = "0.7.0", features = ["async_tokio"] }
dotenvy = "0.15"

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 don't think we should take a dependency for this?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

was simple way to read .env files, made doing test loops faster instead of always doing TEST_POSTGRES_URL=blah cargo test but can remove if not wanted. Figured was fine because just dev-dep

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, we're generally more lax about adding dev-dependencies, but this seems very unnecessary as reading env variables can be done via std, and reading files should be a ~5 line helper?

Comment threadsrc/bench.rs Outdated

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.

Can all benchmark mark-related files rather live in the benches sub-folder? Not sure we want to take on this huge file that's only boilerplate/utilities in the main src dir..

Comment threadbenches/payments.rs

// Set up two nodes. Because this is slow, we reuse the same nodes for each sample.
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();
let chain_source = random_chain_source(&bitcoind, &electrsd);

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.

Please don't introduce that much churn for code you just added in the last commit.

Comment threadbenches/payments.rs
Comment threadbenches/operations.rs
panic!("Timed out waiting for forwarding path readiness");
}

async fn open_channel(

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.

This is just a duplicate of the test util? Why copy this over?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Needed a separate one because the test utils one waits for the tx to be in the mempool. Didn't want to that to leak into the benchmark. Refactored to use a shared util

Comment threadsrc/io/postgres_store/mod.rs
Comment threadbenches/operations.rs Outdated
Comment threadsrc/io/postgres_store/mod.rs Outdated
Comment threadbenches/operations.rs Outdated
Comment threadsrc/bench.rs Outdated
@joostjager

Copy link
Copy Markdown
Contributor

Small note: I’m experimenting with this PR locally and extending the benchmarks to measure single-channel payment performance with many open but unused channels, up to 5000. Since deferred writing makes channel updates scale with the total number of channels, I’m curious to see how much impact that has.

@joostjager

Copy link
Copy Markdown
Contributor

Small note: I’m experimenting with this PR locally and extending the benchmarks to measure single-channel payment performance with many open but unused channels, up to 5000. Since deferred writing makes channel updates scale with the total number of channels, I’m curious to see how much impact that has.

https://git.rust-bitcoin.org/lightningdevkit/rust-lightning/issues/4768

AI-Assisted-By: OpenAI Codex
Comment threadbenches/operations.rs
Comment threadbenches/operations.rs Outdated
Comment threadbenches/common/db_store.rs Outdated
Benchmark realistic payment and pending-payment persistence workloads across
filesystem, SQLite, and optional PostgreSQL stores. Use the async KV-store
APIs so the measured paths match the database interfaces used by async
persistence.
AI-Assisted: Codex
Run the existing payments benchmark once per configured store backend so
filesystem, SQLite, and optional PostgreSQL results are reported under the
same payment flow.
AI-assisted-by: OpenAI Codex
Add an operations bench target with a forwarding benchmark that compares
sqlite, filesystem, and postgres stores over a settled multi-hop payment.
AI-assisted-by: OpenAI Codex
Add a channel-open benchmark that measures the open_channel call while
leaving chain confirmation cleanup outside the timed section.
AI-assisted-by: OpenAI Codex
Add a startup benchmark that restarts a node whose store already
contains channel and payment data, so startup cost reflects persisted
node state.
AI-assisted-by: OpenAI Codex
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Benchmark database options

4 participants

@benthecarman@ldk-reviews-bot@tnull@joostjager
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Add database persistence benchmarks - #915

Open
benthecarman wants to merge 6 commits into
lightningdevkit:mainfrom
benthecarman:db-bench
Open

Add database persistence benchmarks#915
benthecarman wants to merge 6 commits into
lightningdevkit:mainfrom
benthecarman:db-bench

Conversation

@benthecarman

@benthecarmanbenthecarman commented May 31, 2026

Copy link
Copy Markdown
Contributor

Closes#908

Benchmark realistic payment and pending-payment persistence workloads
across filesystem, SQLite, and optional PostgreSQL stores. Use the async
KV-store APIs so the measured paths match the database interfaces used by
async persistence.

Also adds support for different db backends to the current payment benchmark.

Benchmark Results

Results on my computer with a Ryzen 7 5800XT 8-Core, 16-Thread CPU

Operations

Benchfilesystemsqlitepostgres
channel_open4.99 ms5.38 ms14.74 ms
forwarding5.13 s5.16 s5.70 s
channel_open_close5.07 s5.20 s5.08 s
payments5.69 s6.26 s11.73 s

Startup

Scenariofilesystemsqlitepostgres
channels_1_payments_210.37 ms12.04 ms57.57 ms
channels_10_payments_212.02 ms16.68 ms56.77 ms
channels_100_payments_218.37 ms50.61 ms62.03 ms
channels_100_payments_100017.77 ms70.26 ms61.86 ms

Database Hot Path

Benchfilesystemsqlitepostgres
channel_open_like0.26 ms0.46 ms7.54 ms
forwarding_25_like5.00 ms10.56 ms124.89 ms

@ldk-reviews-bot

ldk-reviews-bot commented May 31, 2026

Copy link
Copy Markdown

👋 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.

@benthecarmanbenthecarman mentioned this pull request May 31, 2026
@benthecarman
benthecarmanforce-pushed the db-bench branch 3 times, most recently from 5f75dd9 to bc97c3aCompareJune 2, 2026 22:19
@tnull

tnull commented Jun 3, 2026

Copy link
Copy Markdown
Collaborator

Feel free to rebase on top of #919 and let me know if you see any differences in the benchmarks.

@benthecarman

benthecarman commented Jun 3, 2026

Copy link
Copy Markdown
ContributorAuthor

codex summary:

Benchmark Results

Payment Benchmarks

BackendPreviousCurrentChange
filesystem6.2475 s6.2138 sslightly faster
sqlite6.7378 s6.6388 sfaster
postgres17.669 s17.489 sslightly faster; Criterion reported no significant change

Lower is better. Delta compares the async-branch rerun against the previous saved Criterion results.

BenchBackendPreviousAsync rerunDelta
single write newfilesystem1.844 ms1.847 ms+0.2%
single write newsqlite2.673 ms2.658 ms-0.6%
single write newpostgres636.0 us630.1 us-0.9%
single write existingfilesystem1.860 ms1.881 ms+1.1%
single write existingsqlite22.1 us22.5 us+2.0%
single write existingpostgres638.1 us624.9 us-2.1%
single readfilesystem14.7 us15.1 us+2.2%
single readsqlite17.0 us17.3 us+1.7%
single readpostgres145.0 us146.2 us+0.8%
single removefilesystem908.8 us920.0 us+1.2%
single removesqlite2.842 ms2.728 ms-4.0%
single removepostgres1.445 ms1.470 ms+1.8%
warm insert 100filesystem185.626 ms185.919 ms+0.2%
warm insert 100sqlite276.593 ms269.530 ms-2.6%
warm insert 100postgres63.417 ms62.791 ms-1.0%
concurrent distinct 100filesystem10.964 ms10.909 ms-0.5%
concurrent distinct 100sqlite272.638 ms261.112 ms-4.2%
concurrent distinct 100postgres9.177 ms9.230 ms+0.6%
concurrent same-key 100filesystem11.495 ms13.050 ms+13.5%
concurrent same-key 100sqlite249.663 ms191.029 ms-23.5%
concurrent same-key 100postgres58.739 ms56.400 ms-4.0%
cold insert 100filesystem185.829 ms183.843 ms-1.1%
cold insert 100sqlite266.497 ms307.448 ms+15.4%
cold insert 100postgres74.440 ms72.031 ms-3.2%
cold update 100filesystem188.561 ms186.808 ms-0.9%
cold update 100sqlite247.062 ms246.978 ms-0.0%
cold update 100postgres78.208 ms74.185 ms-5.1%
reload 100filesystem2.995 ms3.002 ms+0.2%
reload 100sqlite3.532 ms3.499 ms-0.9%
reload 100postgres3.237 ms3.152 ms-2.7%
first page from 10kfilesystem25.290 ms25.090 ms-0.8%
first page from 10ksqlite29.8 us24.8 us-17.0%
first page from 10kpostgres184.5 us182.3 us-1.2%
second page from 10kfilesystem50.665 ms50.422 ms-0.5%
second page from 10ksqlite66.5 us50.8 us-23.7%
second page from 10kpostgres376.7 us372.7 us-1.1%
lifecycle insert-update-readfilesystem6.786 ms3.736 ms-44.9%
lifecycle insert-update-readsqlite9.866 ms5.216 ms-47.1%
lifecycle insert-update-readpostgres2.533 ms1.617 ms-36.2%
pending insert 100filesystem273.515 ms183.380 ms-33.0%
pending insert 100sqlite333.859 ms262.138 ms-21.5%
pending insert 100postgres75.244 ms75.999 ms+1.0%
pending update 100filesystem197.544 ms187.656 ms-5.0%
pending update 100sqlite257.055 ms264.137 ms+2.8%
pending update 100postgres75.756 ms63.715 ms-15.9%

Takeaways

  • The async branch materially improves the lifecycle workload across all stores: filesystem improved by 44.9%, sqlite by 47.1%, and Postgres by 36.2%.
  • Postgres pending-payment updates improved meaningfully, dropping from 75.756 ms to 63.715 ms, a 15.9% reduction.
  • Most single-operation Postgres results are roughly unchanged, with small improvements on writes and small regressions/noise on reads/removes.
  • Postgres cold payment-store writes improved modestly: inserts improved by 3.2% and updates by 5.1%.
  • SQLite saw large improvements in paginated reads and same-key concurrent writes, but cold insert regressed by 15.4%, so that path is worth a closer look.
  • Payment benchmarks are still not trustworthy for comparison because the workload does not complete cleanly and times out waiting for HTLC slots.

@joostjager

Copy link
Copy Markdown
Contributor

It would have been nice if async persistence would show a clear performance win here. The store-level smoke tests are useful, but the closest thing to real node load seems to be the payment benchmark, and that does not show a meaningful improvement, putting aside that it is marked as not reliable yet.

Does this tell us much about high-load node performance in practice? If not, what would be the next useful benchmark?

@tnull

tnull commented Jun 4, 2026

Copy link
Copy Markdown
Collaborator

It would have been nice if async persistence would show a clear performance win here. The store-level smoke tests are useful, but the closest thing to real node load seems to be the payment benchmark, and that does not show a meaningful improvement, putting aside that it is marked as not reliable yet.

Hmm, note that 'async persistence' means pre and post #919 AFAIU? I.e., it does not refer to LDK's async persistence itself, which we switched to pre-#919.

@benthecarman

Copy link
Copy Markdown
ContributorAuthor

Hmm, note that 'async persistence' means pre and post #919 AFAIU? I.e., it does not refer to LDK's async persistence itself, which we switched to pre-#919.

yeah that is pre/post 919

@benthecarman

Copy link
Copy Markdown
ContributorAuthor

Does this tell us much about high-load node performance in practice? If not, what would be the next useful benchmark?

Added a few more with start up time, forwarding payments, and channel opens. But working on making these more robust

@benthecarman
benthecarmanforce-pushed the db-bench branch 3 times, most recently from fedeaf4 to c8f415fCompareJune 9, 2026 15:11
@benthecarman
benthecarman requested a review from tnullJune 9, 2026 15:25
@benthecarman
benthecarman marked this pull request as ready for review June 9, 2026 15:25
@benthecarman

benthecarman commented Jun 9, 2026

Copy link
Copy Markdown
ContributorAuthor

Since #919 is merged marking this ready for review.

Hopefully we get CI re-enabled because I am not sure how much these changes effect how long the bench CI job takes. The whole suite takes awhile so I tried trimming it down for CI. That subset takes a few minutes on my computer but unsure with the CI computers

@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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 3rd 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

🔔 4th 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

🔔 5th 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

🔔 6th 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.

@tnulltnull left a comment

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 when we add this we should also document somewhere how to run benchmarks as guidance for contributors.

Besides that, current PR is a quite sloppy. Please clean up before we can move forward here (and split out MigratebleKVStore impls, for instance).

Comment threadsrc/io/postgres_store/mod.rs Outdated

fn test_connection_string() -> String {
std::env::var("TEST_POSTGRES_URL")
dotenvy::dotenv().ok();

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.

What is dotenvy? This commit wont compile.

Comment threadCargo.toml Outdated
proptest = "1.0.0"
regex = "1.5.6"
criterion = { version = "0.7.0", features = ["async_tokio"] }
dotenvy = "0.15"

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 don't think we should take a dependency for this?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

was simple way to read .env files, made doing test loops faster instead of always doing TEST_POSTGRES_URL=blah cargo test but can remove if not wanted. Figured was fine because just dev-dep

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, we're generally more lax about adding dev-dependencies, but this seems very unnecessary as reading env variables can be done via std, and reading files should be a ~5 line helper?

Comment threadsrc/bench.rs Outdated

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.

Can all benchmark mark-related files rather live in the benches sub-folder? Not sure we want to take on this huge file that's only boilerplate/utilities in the main src dir..

Comment threadbenches/payments.rs

// Set up two nodes. Because this is slow, we reuse the same nodes for each sample.
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();
let chain_source = random_chain_source(&bitcoind, &electrsd);

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.

Please don't introduce that much churn for code you just added in the last commit.

Comment threadbenches/payments.rs
Comment threadbenches/operations.rs
panic!("Timed out waiting for forwarding path readiness");
}

async fn open_channel(

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.

This is just a duplicate of the test util? Why copy this over?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Needed a separate one because the test utils one waits for the tx to be in the mempool. Didn't want to that to leak into the benchmark. Refactored to use a shared util

Comment threadsrc/io/postgres_store/mod.rs
Comment threadbenches/operations.rs Outdated
Comment threadsrc/io/postgres_store/mod.rs Outdated
Comment threadbenches/operations.rs Outdated
Comment threadsrc/bench.rs Outdated
@joostjager

Copy link
Copy Markdown
Contributor

Small note: I’m experimenting with this PR locally and extending the benchmarks to measure single-channel payment performance with many open but unused channels, up to 5000. Since deferred writing makes channel updates scale with the total number of channels, I’m curious to see how much impact that has.

@joostjager

Copy link
Copy Markdown
Contributor

Small note: I’m experimenting with this PR locally and extending the benchmarks to measure single-channel payment performance with many open but unused channels, up to 5000. Since deferred writing makes channel updates scale with the total number of channels, I’m curious to see how much impact that has.

https://git.rust-bitcoin.org/lightningdevkit/rust-lightning/issues/4768

AI-Assisted-By: OpenAI Codex
Comment threadbenches/operations.rs
Comment threadbenches/operations.rs Outdated
Comment threadbenches/common/db_store.rs Outdated
Benchmark realistic payment and pending-payment persistence workloads across
filesystem, SQLite, and optional PostgreSQL stores. Use the async KV-store
APIs so the measured paths match the database interfaces used by async
persistence.
AI-Assisted: Codex
Run the existing payments benchmark once per configured store backend so
filesystem, SQLite, and optional PostgreSQL results are reported under the
same payment flow.
AI-assisted-by: OpenAI Codex
Add an operations bench target with a forwarding benchmark that compares
sqlite, filesystem, and postgres stores over a settled multi-hop payment.
AI-assisted-by: OpenAI Codex
Add a channel-open benchmark that measures the open_channel call while
leaving chain confirmation cleanup outside the timed section.
AI-assisted-by: OpenAI Codex
Add a startup benchmark that restarts a node whose store already
contains channel and payment data, so startup cost reflects persisted
node state.
AI-assisted-by: OpenAI Codex
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Benchmark database options

4 participants

@benthecarman@ldk-reviews-bot@tnull@joostjager
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add database persistence benchmarks - #915

Open
benthecarman wants to merge 6 commits into
lightningdevkit:mainfrom
benthecarman:db-bench
Open

Add database persistence benchmarks#915
benthecarman wants to merge 6 commits into
lightningdevkit:mainfrom
benthecarman:db-bench

Conversation

@benthecarman

@benthecarmanbenthecarman commented May 31, 2026

Copy link
Copy Markdown
Contributor

Closes#908

Benchmark realistic payment and pending-payment persistence workloads
across filesystem, SQLite, and optional PostgreSQL stores. Use the async
KV-store APIs so the measured paths match the database interfaces used by
async persistence.

Also adds support for different db backends to the current payment benchmark.

Benchmark Results

Results on my computer with a Ryzen 7 5800XT 8-Core, 16-Thread CPU

Operations

Benchfilesystemsqlitepostgres
channel_open4.99 ms5.38 ms14.74 ms
forwarding5.13 s5.16 s5.70 s
channel_open_close5.07 s5.20 s5.08 s
payments5.69 s6.26 s11.73 s

Startup

Scenariofilesystemsqlitepostgres
channels_1_payments_210.37 ms12.04 ms57.57 ms
channels_10_payments_212.02 ms16.68 ms56.77 ms
channels_100_payments_218.37 ms50.61 ms62.03 ms
channels_100_payments_100017.77 ms70.26 ms61.86 ms

Database Hot Path

Benchfilesystemsqlitepostgres
channel_open_like0.26 ms0.46 ms7.54 ms
forwarding_25_like5.00 ms10.56 ms124.89 ms

@ldk-reviews-bot

ldk-reviews-bot commented May 31, 2026

Copy link
Copy Markdown

👋 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.

@benthecarmanbenthecarman mentioned this pull request May 31, 2026
@benthecarman
benthecarmanforce-pushed the db-bench branch 3 times, most recently from 5f75dd9 to bc97c3aCompareJune 2, 2026 22:19
@tnull

tnull commented Jun 3, 2026

Copy link
Copy Markdown
Collaborator

Feel free to rebase on top of #919 and let me know if you see any differences in the benchmarks.

@benthecarman

benthecarman commented Jun 3, 2026

Copy link
Copy Markdown
ContributorAuthor

codex summary:

Benchmark Results

Payment Benchmarks

BackendPreviousCurrentChange
filesystem6.2475 s6.2138 sslightly faster
sqlite6.7378 s6.6388 sfaster
postgres17.669 s17.489 sslightly faster; Criterion reported no significant change

Lower is better. Delta compares the async-branch rerun against the previous saved Criterion results.

BenchBackendPreviousAsync rerunDelta
single write newfilesystem1.844 ms1.847 ms+0.2%
single write newsqlite2.673 ms2.658 ms-0.6%
single write newpostgres636.0 us630.1 us-0.9%
single write existingfilesystem1.860 ms1.881 ms+1.1%
single write existingsqlite22.1 us22.5 us+2.0%
single write existingpostgres638.1 us624.9 us-2.1%
single readfilesystem14.7 us15.1 us+2.2%
single readsqlite17.0 us17.3 us+1.7%
single readpostgres145.0 us146.2 us+0.8%
single removefilesystem908.8 us920.0 us+1.2%
single removesqlite2.842 ms2.728 ms-4.0%
single removepostgres1.445 ms1.470 ms+1.8%
warm insert 100filesystem185.626 ms185.919 ms+0.2%
warm insert 100sqlite276.593 ms269.530 ms-2.6%
warm insert 100postgres63.417 ms62.791 ms-1.0%
concurrent distinct 100filesystem10.964 ms10.909 ms-0.5%
concurrent distinct 100sqlite272.638 ms261.112 ms-4.2%
concurrent distinct 100postgres9.177 ms9.230 ms+0.6%
concurrent same-key 100filesystem11.495 ms13.050 ms+13.5%
concurrent same-key 100sqlite249.663 ms191.029 ms-23.5%
concurrent same-key 100postgres58.739 ms56.400 ms-4.0%
cold insert 100filesystem185.829 ms183.843 ms-1.1%
cold insert 100sqlite266.497 ms307.448 ms+15.4%
cold insert 100postgres74.440 ms72.031 ms-3.2%
cold update 100filesystem188.561 ms186.808 ms-0.9%
cold update 100sqlite247.062 ms246.978 ms-0.0%
cold update 100postgres78.208 ms74.185 ms-5.1%
reload 100filesystem2.995 ms3.002 ms+0.2%
reload 100sqlite3.532 ms3.499 ms-0.9%
reload 100postgres3.237 ms3.152 ms-2.7%
first page from 10kfilesystem25.290 ms25.090 ms-0.8%
first page from 10ksqlite29.8 us24.8 us-17.0%
first page from 10kpostgres184.5 us182.3 us-1.2%
second page from 10kfilesystem50.665 ms50.422 ms-0.5%
second page from 10ksqlite66.5 us50.8 us-23.7%
second page from 10kpostgres376.7 us372.7 us-1.1%
lifecycle insert-update-readfilesystem6.786 ms3.736 ms-44.9%
lifecycle insert-update-readsqlite9.866 ms5.216 ms-47.1%
lifecycle insert-update-readpostgres2.533 ms1.617 ms-36.2%
pending insert 100filesystem273.515 ms183.380 ms-33.0%
pending insert 100sqlite333.859 ms262.138 ms-21.5%
pending insert 100postgres75.244 ms75.999 ms+1.0%
pending update 100filesystem197.544 ms187.656 ms-5.0%
pending update 100sqlite257.055 ms264.137 ms+2.8%
pending update 100postgres75.756 ms63.715 ms-15.9%

Takeaways

  • The async branch materially improves the lifecycle workload across all stores: filesystem improved by 44.9%, sqlite by 47.1%, and Postgres by 36.2%.
  • Postgres pending-payment updates improved meaningfully, dropping from 75.756 ms to 63.715 ms, a 15.9% reduction.
  • Most single-operation Postgres results are roughly unchanged, with small improvements on writes and small regressions/noise on reads/removes.
  • Postgres cold payment-store writes improved modestly: inserts improved by 3.2% and updates by 5.1%.
  • SQLite saw large improvements in paginated reads and same-key concurrent writes, but cold insert regressed by 15.4%, so that path is worth a closer look.
  • Payment benchmarks are still not trustworthy for comparison because the workload does not complete cleanly and times out waiting for HTLC slots.

@joostjager

Copy link
Copy Markdown
Contributor

It would have been nice if async persistence would show a clear performance win here. The store-level smoke tests are useful, but the closest thing to real node load seems to be the payment benchmark, and that does not show a meaningful improvement, putting aside that it is marked as not reliable yet.

Does this tell us much about high-load node performance in practice? If not, what would be the next useful benchmark?

@tnull

tnull commented Jun 4, 2026

Copy link
Copy Markdown
Collaborator

It would have been nice if async persistence would show a clear performance win here. The store-level smoke tests are useful, but the closest thing to real node load seems to be the payment benchmark, and that does not show a meaningful improvement, putting aside that it is marked as not reliable yet.

Hmm, note that 'async persistence' means pre and post #919 AFAIU? I.e., it does not refer to LDK's async persistence itself, which we switched to pre-#919.

@benthecarman

Copy link
Copy Markdown
ContributorAuthor

Hmm, note that 'async persistence' means pre and post #919 AFAIU? I.e., it does not refer to LDK's async persistence itself, which we switched to pre-#919.

yeah that is pre/post 919

@benthecarman

Copy link
Copy Markdown
ContributorAuthor

Does this tell us much about high-load node performance in practice? If not, what would be the next useful benchmark?

Added a few more with start up time, forwarding payments, and channel opens. But working on making these more robust

@benthecarman
benthecarmanforce-pushed the db-bench branch 3 times, most recently from fedeaf4 to c8f415fCompareJune 9, 2026 15:11
@benthecarman
benthecarman requested a review from tnullJune 9, 2026 15:25
@benthecarman
benthecarman marked this pull request as ready for review June 9, 2026 15:25
@benthecarman

benthecarman commented Jun 9, 2026

Copy link
Copy Markdown
ContributorAuthor

Since #919 is merged marking this ready for review.

Hopefully we get CI re-enabled because I am not sure how much these changes effect how long the bench CI job takes. The whole suite takes awhile so I tried trimming it down for CI. That subset takes a few minutes on my computer but unsure with the CI computers

@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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 3rd 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

🔔 4th 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

🔔 5th 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

🔔 6th 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.

@tnulltnull left a comment

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 when we add this we should also document somewhere how to run benchmarks as guidance for contributors.

Besides that, current PR is a quite sloppy. Please clean up before we can move forward here (and split out MigratebleKVStore impls, for instance).

Comment threadsrc/io/postgres_store/mod.rs Outdated

fn test_connection_string() -> String {
std::env::var("TEST_POSTGRES_URL")
dotenvy::dotenv().ok();

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.

What is dotenvy? This commit wont compile.

Comment threadCargo.toml Outdated
proptest = "1.0.0"
regex = "1.5.6"
criterion = { version = "0.7.0", features = ["async_tokio"] }
dotenvy = "0.15"

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 don't think we should take a dependency for this?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

was simple way to read .env files, made doing test loops faster instead of always doing TEST_POSTGRES_URL=blah cargo test but can remove if not wanted. Figured was fine because just dev-dep

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, we're generally more lax about adding dev-dependencies, but this seems very unnecessary as reading env variables can be done via std, and reading files should be a ~5 line helper?

Comment threadsrc/bench.rs Outdated

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.

Can all benchmark mark-related files rather live in the benches sub-folder? Not sure we want to take on this huge file that's only boilerplate/utilities in the main src dir..

Comment threadbenches/payments.rs

// Set up two nodes. Because this is slow, we reuse the same nodes for each sample.
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();
let chain_source = random_chain_source(&bitcoind, &electrsd);

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.

Please don't introduce that much churn for code you just added in the last commit.

Comment threadbenches/payments.rs
Comment threadbenches/operations.rs
panic!("Timed out waiting for forwarding path readiness");
}

async fn open_channel(

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.

This is just a duplicate of the test util? Why copy this over?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Needed a separate one because the test utils one waits for the tx to be in the mempool. Didn't want to that to leak into the benchmark. Refactored to use a shared util

Comment threadsrc/io/postgres_store/mod.rs
Comment threadbenches/operations.rs Outdated
Comment threadsrc/io/postgres_store/mod.rs Outdated
Comment threadbenches/operations.rs Outdated
Comment threadsrc/bench.rs Outdated
@joostjager

Copy link
Copy Markdown
Contributor

Small note: I’m experimenting with this PR locally and extending the benchmarks to measure single-channel payment performance with many open but unused channels, up to 5000. Since deferred writing makes channel updates scale with the total number of channels, I’m curious to see how much impact that has.

@joostjager

Copy link
Copy Markdown
Contributor

Small note: I’m experimenting with this PR locally and extending the benchmarks to measure single-channel payment performance with many open but unused channels, up to 5000. Since deferred writing makes channel updates scale with the total number of channels, I’m curious to see how much impact that has.

https://git.rust-bitcoin.org/lightningdevkit/rust-lightning/issues/4768

AI-Assisted-By: OpenAI Codex
Comment threadbenches/operations.rs
Comment threadbenches/operations.rs Outdated
Comment threadbenches/common/db_store.rs Outdated
Benchmark realistic payment and pending-payment persistence workloads across
filesystem, SQLite, and optional PostgreSQL stores. Use the async KV-store
APIs so the measured paths match the database interfaces used by async
persistence.
AI-Assisted: Codex
Run the existing payments benchmark once per configured store backend so
filesystem, SQLite, and optional PostgreSQL results are reported under the
same payment flow.
AI-assisted-by: OpenAI Codex
Add an operations bench target with a forwarding benchmark that compares
sqlite, filesystem, and postgres stores over a settled multi-hop payment.
AI-assisted-by: OpenAI Codex
Add a channel-open benchmark that measures the open_channel call while
leaving chain confirmation cleanup outside the timed section.
AI-assisted-by: OpenAI Codex
Add a startup benchmark that restarts a node whose store already
contains channel and payment data, so startup cost reflects persisted
node state.
AI-assisted-by: OpenAI Codex
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Benchmark database options

4 participants

@benthecarman@ldk-reviews-bot@tnull@joostjager
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add database persistence benchmarks - #915

Open
benthecarman wants to merge 6 commits into
lightningdevkit:mainfrom
benthecarman:db-bench
Open

Add database persistence benchmarks#915
benthecarman wants to merge 6 commits into
lightningdevkit:mainfrom
benthecarman:db-bench

Conversation

@benthecarman

@benthecarmanbenthecarman commented May 31, 2026

Copy link
Copy Markdown
Contributor

Closes#908

Benchmark realistic payment and pending-payment persistence workloads
across filesystem, SQLite, and optional PostgreSQL stores. Use the async
KV-store APIs so the measured paths match the database interfaces used by
async persistence.

Also adds support for different db backends to the current payment benchmark.

Benchmark Results

Results on my computer with a Ryzen 7 5800XT 8-Core, 16-Thread CPU

Operations

Benchfilesystemsqlitepostgres
channel_open4.99 ms5.38 ms14.74 ms
forwarding5.13 s5.16 s5.70 s
channel_open_close5.07 s5.20 s5.08 s
payments5.69 s6.26 s11.73 s

Startup

Scenariofilesystemsqlitepostgres
channels_1_payments_210.37 ms12.04 ms57.57 ms
channels_10_payments_212.02 ms16.68 ms56.77 ms
channels_100_payments_218.37 ms50.61 ms62.03 ms
channels_100_payments_100017.77 ms70.26 ms61.86 ms

Database Hot Path

Benchfilesystemsqlitepostgres
channel_open_like0.26 ms0.46 ms7.54 ms
forwarding_25_like5.00 ms10.56 ms124.89 ms

@ldk-reviews-bot

ldk-reviews-bot commented May 31, 2026

Copy link
Copy Markdown

👋 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.

@benthecarmanbenthecarman mentioned this pull request May 31, 2026
@benthecarman
benthecarmanforce-pushed the db-bench branch 3 times, most recently from 5f75dd9 to bc97c3aCompareJune 2, 2026 22:19
@tnull

tnull commented Jun 3, 2026

Copy link
Copy Markdown
Collaborator

Feel free to rebase on top of #919 and let me know if you see any differences in the benchmarks.

@benthecarman

benthecarman commented Jun 3, 2026

Copy link
Copy Markdown
ContributorAuthor

codex summary:

Benchmark Results

Payment Benchmarks

BackendPreviousCurrentChange
filesystem6.2475 s6.2138 sslightly faster
sqlite6.7378 s6.6388 sfaster
postgres17.669 s17.489 sslightly faster; Criterion reported no significant change

Lower is better. Delta compares the async-branch rerun against the previous saved Criterion results.

BenchBackendPreviousAsync rerunDelta
single write newfilesystem1.844 ms1.847 ms+0.2%
single write newsqlite2.673 ms2.658 ms-0.6%
single write newpostgres636.0 us630.1 us-0.9%
single write existingfilesystem1.860 ms1.881 ms+1.1%
single write existingsqlite22.1 us22.5 us+2.0%
single write existingpostgres638.1 us624.9 us-2.1%
single readfilesystem14.7 us15.1 us+2.2%
single readsqlite17.0 us17.3 us+1.7%
single readpostgres145.0 us146.2 us+0.8%
single removefilesystem908.8 us920.0 us+1.2%
single removesqlite2.842 ms2.728 ms-4.0%
single removepostgres1.445 ms1.470 ms+1.8%
warm insert 100filesystem185.626 ms185.919 ms+0.2%
warm insert 100sqlite276.593 ms269.530 ms-2.6%
warm insert 100postgres63.417 ms62.791 ms-1.0%
concurrent distinct 100filesystem10.964 ms10.909 ms-0.5%
concurrent distinct 100sqlite272.638 ms261.112 ms-4.2%
concurrent distinct 100postgres9.177 ms9.230 ms+0.6%
concurrent same-key 100filesystem11.495 ms13.050 ms+13.5%
concurrent same-key 100sqlite249.663 ms191.029 ms-23.5%
concurrent same-key 100postgres58.739 ms56.400 ms-4.0%
cold insert 100filesystem185.829 ms183.843 ms-1.1%
cold insert 100sqlite266.497 ms307.448 ms+15.4%
cold insert 100postgres74.440 ms72.031 ms-3.2%
cold update 100filesystem188.561 ms186.808 ms-0.9%
cold update 100sqlite247.062 ms246.978 ms-0.0%
cold update 100postgres78.208 ms74.185 ms-5.1%
reload 100filesystem2.995 ms3.002 ms+0.2%
reload 100sqlite3.532 ms3.499 ms-0.9%
reload 100postgres3.237 ms3.152 ms-2.7%
first page from 10kfilesystem25.290 ms25.090 ms-0.8%
first page from 10ksqlite29.8 us24.8 us-17.0%
first page from 10kpostgres184.5 us182.3 us-1.2%
second page from 10kfilesystem50.665 ms50.422 ms-0.5%
second page from 10ksqlite66.5 us50.8 us-23.7%
second page from 10kpostgres376.7 us372.7 us-1.1%
lifecycle insert-update-readfilesystem6.786 ms3.736 ms-44.9%
lifecycle insert-update-readsqlite9.866 ms5.216 ms-47.1%
lifecycle insert-update-readpostgres2.533 ms1.617 ms-36.2%
pending insert 100filesystem273.515 ms183.380 ms-33.0%
pending insert 100sqlite333.859 ms262.138 ms-21.5%
pending insert 100postgres75.244 ms75.999 ms+1.0%
pending update 100filesystem197.544 ms187.656 ms-5.0%
pending update 100sqlite257.055 ms264.137 ms+2.8%
pending update 100postgres75.756 ms63.715 ms-15.9%

Takeaways

  • The async branch materially improves the lifecycle workload across all stores: filesystem improved by 44.9%, sqlite by 47.1%, and Postgres by 36.2%.
  • Postgres pending-payment updates improved meaningfully, dropping from 75.756 ms to 63.715 ms, a 15.9% reduction.
  • Most single-operation Postgres results are roughly unchanged, with small improvements on writes and small regressions/noise on reads/removes.
  • Postgres cold payment-store writes improved modestly: inserts improved by 3.2% and updates by 5.1%.
  • SQLite saw large improvements in paginated reads and same-key concurrent writes, but cold insert regressed by 15.4%, so that path is worth a closer look.
  • Payment benchmarks are still not trustworthy for comparison because the workload does not complete cleanly and times out waiting for HTLC slots.

@joostjager

Copy link
Copy Markdown
Contributor

It would have been nice if async persistence would show a clear performance win here. The store-level smoke tests are useful, but the closest thing to real node load seems to be the payment benchmark, and that does not show a meaningful improvement, putting aside that it is marked as not reliable yet.

Does this tell us much about high-load node performance in practice? If not, what would be the next useful benchmark?

@tnull

tnull commented Jun 4, 2026

Copy link
Copy Markdown
Collaborator

It would have been nice if async persistence would show a clear performance win here. The store-level smoke tests are useful, but the closest thing to real node load seems to be the payment benchmark, and that does not show a meaningful improvement, putting aside that it is marked as not reliable yet.

Hmm, note that 'async persistence' means pre and post #919 AFAIU? I.e., it does not refer to LDK's async persistence itself, which we switched to pre-#919.

@benthecarman

Copy link
Copy Markdown
ContributorAuthor

Hmm, note that 'async persistence' means pre and post #919 AFAIU? I.e., it does not refer to LDK's async persistence itself, which we switched to pre-#919.

yeah that is pre/post 919

@benthecarman

Copy link
Copy Markdown
ContributorAuthor

Does this tell us much about high-load node performance in practice? If not, what would be the next useful benchmark?

Added a few more with start up time, forwarding payments, and channel opens. But working on making these more robust

@benthecarman
benthecarmanforce-pushed the db-bench branch 3 times, most recently from fedeaf4 to c8f415fCompareJune 9, 2026 15:11
@benthecarman
benthecarman requested a review from tnullJune 9, 2026 15:25
@benthecarman
benthecarman marked this pull request as ready for review June 9, 2026 15:25
@benthecarman

benthecarman commented Jun 9, 2026

Copy link
Copy Markdown
ContributorAuthor

Since #919 is merged marking this ready for review.

Hopefully we get CI re-enabled because I am not sure how much these changes effect how long the bench CI job takes. The whole suite takes awhile so I tried trimming it down for CI. That subset takes a few minutes on my computer but unsure with the CI computers

@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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 3rd 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

🔔 4th 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

🔔 5th 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

🔔 6th 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.

@tnulltnull left a comment

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 when we add this we should also document somewhere how to run benchmarks as guidance for contributors.

Besides that, current PR is a quite sloppy. Please clean up before we can move forward here (and split out MigratebleKVStore impls, for instance).

Comment threadsrc/io/postgres_store/mod.rs Outdated

fn test_connection_string() -> String {
std::env::var("TEST_POSTGRES_URL")
dotenvy::dotenv().ok();

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.

What is dotenvy? This commit wont compile.

Comment threadCargo.toml Outdated
proptest = "1.0.0"
regex = "1.5.6"
criterion = { version = "0.7.0", features = ["async_tokio"] }
dotenvy = "0.15"

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 don't think we should take a dependency for this?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

was simple way to read .env files, made doing test loops faster instead of always doing TEST_POSTGRES_URL=blah cargo test but can remove if not wanted. Figured was fine because just dev-dep

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, we're generally more lax about adding dev-dependencies, but this seems very unnecessary as reading env variables can be done via std, and reading files should be a ~5 line helper?

Comment threadsrc/bench.rs Outdated

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.

Can all benchmark mark-related files rather live in the benches sub-folder? Not sure we want to take on this huge file that's only boilerplate/utilities in the main src dir..

Comment threadbenches/payments.rs

// Set up two nodes. Because this is slow, we reuse the same nodes for each sample.
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();
let chain_source = random_chain_source(&bitcoind, &electrsd);

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.

Please don't introduce that much churn for code you just added in the last commit.

Comment threadbenches/payments.rs
Comment threadbenches/operations.rs
panic!("Timed out waiting for forwarding path readiness");
}

async fn open_channel(

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.

This is just a duplicate of the test util? Why copy this over?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Needed a separate one because the test utils one waits for the tx to be in the mempool. Didn't want to that to leak into the benchmark. Refactored to use a shared util

Comment threadsrc/io/postgres_store/mod.rs
Comment threadbenches/operations.rs Outdated
Comment threadsrc/io/postgres_store/mod.rs Outdated
Comment threadbenches/operations.rs Outdated
Comment threadsrc/bench.rs Outdated
@joostjager

Copy link
Copy Markdown
Contributor

Small note: I’m experimenting with this PR locally and extending the benchmarks to measure single-channel payment performance with many open but unused channels, up to 5000. Since deferred writing makes channel updates scale with the total number of channels, I’m curious to see how much impact that has.

@joostjager

Copy link
Copy Markdown
Contributor

Small note: I’m experimenting with this PR locally and extending the benchmarks to measure single-channel payment performance with many open but unused channels, up to 5000. Since deferred writing makes channel updates scale with the total number of channels, I’m curious to see how much impact that has.

https://git.rust-bitcoin.org/lightningdevkit/rust-lightning/issues/4768

AI-Assisted-By: OpenAI Codex
Comment threadbenches/operations.rs
Comment threadbenches/operations.rs Outdated
Comment threadbenches/common/db_store.rs Outdated
Benchmark realistic payment and pending-payment persistence workloads across
filesystem, SQLite, and optional PostgreSQL stores. Use the async KV-store
APIs so the measured paths match the database interfaces used by async
persistence.
AI-Assisted: Codex
Run the existing payments benchmark once per configured store backend so
filesystem, SQLite, and optional PostgreSQL results are reported under the
same payment flow.
AI-assisted-by: OpenAI Codex
Add an operations bench target with a forwarding benchmark that compares
sqlite, filesystem, and postgres stores over a settled multi-hop payment.
AI-assisted-by: OpenAI Codex
Add a channel-open benchmark that measures the open_channel call while
leaving chain confirmation cleanup outside the timed section.
AI-assisted-by: OpenAI Codex
Add a startup benchmark that restarts a node whose store already
contains channel and payment data, so startup cost reflects persisted
node state.
AI-assisted-by: OpenAI Codex
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Benchmark database options

4 participants

@benthecarman@ldk-reviews-bot@tnull@joostjager
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Add database persistence benchmarks - #915

Open
benthecarman wants to merge 6 commits into
lightningdevkit:mainfrom
benthecarman:db-bench
Open

Add database persistence benchmarks#915
benthecarman wants to merge 6 commits into
lightningdevkit:mainfrom
benthecarman:db-bench

Conversation

@benthecarman

@benthecarmanbenthecarman commented May 31, 2026

Copy link
Copy Markdown
Contributor

Closes#908

Benchmark realistic payment and pending-payment persistence workloads
across filesystem, SQLite, and optional PostgreSQL stores. Use the async
KV-store APIs so the measured paths match the database interfaces used by
async persistence.

Also adds support for different db backends to the current payment benchmark.

Benchmark Results

Results on my computer with a Ryzen 7 5800XT 8-Core, 16-Thread CPU

Operations

Benchfilesystemsqlitepostgres
channel_open4.99 ms5.38 ms14.74 ms
forwarding5.13 s5.16 s5.70 s
channel_open_close5.07 s5.20 s5.08 s
payments5.69 s6.26 s11.73 s

Startup

Scenariofilesystemsqlitepostgres
channels_1_payments_210.37 ms12.04 ms57.57 ms
channels_10_payments_212.02 ms16.68 ms56.77 ms
channels_100_payments_218.37 ms50.61 ms62.03 ms
channels_100_payments_100017.77 ms70.26 ms61.86 ms

Database Hot Path

Benchfilesystemsqlitepostgres
channel_open_like0.26 ms0.46 ms7.54 ms
forwarding_25_like5.00 ms10.56 ms124.89 ms

@ldk-reviews-bot

ldk-reviews-bot commented May 31, 2026

Copy link
Copy Markdown

👋 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.

@benthecarmanbenthecarman mentioned this pull request May 31, 2026
@benthecarman
benthecarmanforce-pushed the db-bench branch 3 times, most recently from 5f75dd9 to bc97c3aCompareJune 2, 2026 22:19
@tnull

tnull commented Jun 3, 2026

Copy link
Copy Markdown
Collaborator

Feel free to rebase on top of #919 and let me know if you see any differences in the benchmarks.

@benthecarman

benthecarman commented Jun 3, 2026

Copy link
Copy Markdown
ContributorAuthor

codex summary:

Benchmark Results

Payment Benchmarks

BackendPreviousCurrentChange
filesystem6.2475 s6.2138 sslightly faster
sqlite6.7378 s6.6388 sfaster
postgres17.669 s17.489 sslightly faster; Criterion reported no significant change

Lower is better. Delta compares the async-branch rerun against the previous saved Criterion results.

BenchBackendPreviousAsync rerunDelta
single write newfilesystem1.844 ms1.847 ms+0.2%
single write newsqlite2.673 ms2.658 ms-0.6%
single write newpostgres636.0 us630.1 us-0.9%
single write existingfilesystem1.860 ms1.881 ms+1.1%
single write existingsqlite22.1 us22.5 us+2.0%
single write existingpostgres638.1 us624.9 us-2.1%
single readfilesystem14.7 us15.1 us+2.2%
single readsqlite17.0 us17.3 us+1.7%
single readpostgres145.0 us146.2 us+0.8%
single removefilesystem908.8 us920.0 us+1.2%
single removesqlite2.842 ms2.728 ms-4.0%
single removepostgres1.445 ms1.470 ms+1.8%
warm insert 100filesystem185.626 ms185.919 ms+0.2%
warm insert 100sqlite276.593 ms269.530 ms-2.6%
warm insert 100postgres63.417 ms62.791 ms-1.0%
concurrent distinct 100filesystem10.964 ms10.909 ms-0.5%
concurrent distinct 100sqlite272.638 ms261.112 ms-4.2%
concurrent distinct 100postgres9.177 ms9.230 ms+0.6%
concurrent same-key 100filesystem11.495 ms13.050 ms+13.5%
concurrent same-key 100sqlite249.663 ms191.029 ms-23.5%
concurrent same-key 100postgres58.739 ms56.400 ms-4.0%
cold insert 100filesystem185.829 ms183.843 ms-1.1%
cold insert 100sqlite266.497 ms307.448 ms+15.4%
cold insert 100postgres74.440 ms72.031 ms-3.2%
cold update 100filesystem188.561 ms186.808 ms-0.9%
cold update 100sqlite247.062 ms246.978 ms-0.0%
cold update 100postgres78.208 ms74.185 ms-5.1%
reload 100filesystem2.995 ms3.002 ms+0.2%
reload 100sqlite3.532 ms3.499 ms-0.9%
reload 100postgres3.237 ms3.152 ms-2.7%
first page from 10kfilesystem25.290 ms25.090 ms-0.8%
first page from 10ksqlite29.8 us24.8 us-17.0%
first page from 10kpostgres184.5 us182.3 us-1.2%
second page from 10kfilesystem50.665 ms50.422 ms-0.5%
second page from 10ksqlite66.5 us50.8 us-23.7%
second page from 10kpostgres376.7 us372.7 us-1.1%
lifecycle insert-update-readfilesystem6.786 ms3.736 ms-44.9%
lifecycle insert-update-readsqlite9.866 ms5.216 ms-47.1%
lifecycle insert-update-readpostgres2.533 ms1.617 ms-36.2%
pending insert 100filesystem273.515 ms183.380 ms-33.0%
pending insert 100sqlite333.859 ms262.138 ms-21.5%
pending insert 100postgres75.244 ms75.999 ms+1.0%
pending update 100filesystem197.544 ms187.656 ms-5.0%
pending update 100sqlite257.055 ms264.137 ms+2.8%
pending update 100postgres75.756 ms63.715 ms-15.9%

Takeaways

  • The async branch materially improves the lifecycle workload across all stores: filesystem improved by 44.9%, sqlite by 47.1%, and Postgres by 36.2%.
  • Postgres pending-payment updates improved meaningfully, dropping from 75.756 ms to 63.715 ms, a 15.9% reduction.
  • Most single-operation Postgres results are roughly unchanged, with small improvements on writes and small regressions/noise on reads/removes.
  • Postgres cold payment-store writes improved modestly: inserts improved by 3.2% and updates by 5.1%.
  • SQLite saw large improvements in paginated reads and same-key concurrent writes, but cold insert regressed by 15.4%, so that path is worth a closer look.
  • Payment benchmarks are still not trustworthy for comparison because the workload does not complete cleanly and times out waiting for HTLC slots.

@joostjager

Copy link
Copy Markdown
Contributor

It would have been nice if async persistence would show a clear performance win here. The store-level smoke tests are useful, but the closest thing to real node load seems to be the payment benchmark, and that does not show a meaningful improvement, putting aside that it is marked as not reliable yet.

Does this tell us much about high-load node performance in practice? If not, what would be the next useful benchmark?

@tnull

tnull commented Jun 4, 2026

Copy link
Copy Markdown
Collaborator

It would have been nice if async persistence would show a clear performance win here. The store-level smoke tests are useful, but the closest thing to real node load seems to be the payment benchmark, and that does not show a meaningful improvement, putting aside that it is marked as not reliable yet.

Hmm, note that 'async persistence' means pre and post #919 AFAIU? I.e., it does not refer to LDK's async persistence itself, which we switched to pre-#919.

@benthecarman

Copy link
Copy Markdown
ContributorAuthor

Hmm, note that 'async persistence' means pre and post #919 AFAIU? I.e., it does not refer to LDK's async persistence itself, which we switched to pre-#919.

yeah that is pre/post 919

@benthecarman

Copy link
Copy Markdown
ContributorAuthor

Does this tell us much about high-load node performance in practice? If not, what would be the next useful benchmark?

Added a few more with start up time, forwarding payments, and channel opens. But working on making these more robust

@benthecarman
benthecarmanforce-pushed the db-bench branch 3 times, most recently from fedeaf4 to c8f415fCompareJune 9, 2026 15:11
@benthecarman
benthecarman requested a review from tnullJune 9, 2026 15:25
@benthecarman
benthecarman marked this pull request as ready for review June 9, 2026 15:25
@benthecarman

benthecarman commented Jun 9, 2026

Copy link
Copy Markdown
ContributorAuthor

Since #919 is merged marking this ready for review.

Hopefully we get CI re-enabled because I am not sure how much these changes effect how long the bench CI job takes. The whole suite takes awhile so I tried trimming it down for CI. That subset takes a few minutes on my computer but unsure with the CI computers

@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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 3rd 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

🔔 4th 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

🔔 5th 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

🔔 6th 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.

@tnulltnull left a comment

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 when we add this we should also document somewhere how to run benchmarks as guidance for contributors.

Besides that, current PR is a quite sloppy. Please clean up before we can move forward here (and split out MigratebleKVStore impls, for instance).

Comment threadsrc/io/postgres_store/mod.rs Outdated

fn test_connection_string() -> String {
std::env::var("TEST_POSTGRES_URL")
dotenvy::dotenv().ok();

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.

What is dotenvy? This commit wont compile.

Comment threadCargo.toml Outdated
proptest = "1.0.0"
regex = "1.5.6"
criterion = { version = "0.7.0", features = ["async_tokio"] }
dotenvy = "0.15"

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 don't think we should take a dependency for this?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

was simple way to read .env files, made doing test loops faster instead of always doing TEST_POSTGRES_URL=blah cargo test but can remove if not wanted. Figured was fine because just dev-dep

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, we're generally more lax about adding dev-dependencies, but this seems very unnecessary as reading env variables can be done via std, and reading files should be a ~5 line helper?

Comment threadsrc/bench.rs Outdated

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.

Can all benchmark mark-related files rather live in the benches sub-folder? Not sure we want to take on this huge file that's only boilerplate/utilities in the main src dir..

Comment threadbenches/payments.rs

// Set up two nodes. Because this is slow, we reuse the same nodes for each sample.
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();
let chain_source = random_chain_source(&bitcoind, &electrsd);

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.

Please don't introduce that much churn for code you just added in the last commit.

Comment threadbenches/payments.rs
Comment threadbenches/operations.rs
panic!("Timed out waiting for forwarding path readiness");
}

async fn open_channel(

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.

This is just a duplicate of the test util? Why copy this over?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Needed a separate one because the test utils one waits for the tx to be in the mempool. Didn't want to that to leak into the benchmark. Refactored to use a shared util

Comment threadsrc/io/postgres_store/mod.rs
Comment threadbenches/operations.rs Outdated
Comment threadsrc/io/postgres_store/mod.rs Outdated
Comment threadbenches/operations.rs Outdated
Comment threadsrc/bench.rs Outdated
@joostjager

Copy link
Copy Markdown
Contributor

Small note: I’m experimenting with this PR locally and extending the benchmarks to measure single-channel payment performance with many open but unused channels, up to 5000. Since deferred writing makes channel updates scale with the total number of channels, I’m curious to see how much impact that has.

@joostjager

Copy link
Copy Markdown
Contributor

Small note: I’m experimenting with this PR locally and extending the benchmarks to measure single-channel payment performance with many open but unused channels, up to 5000. Since deferred writing makes channel updates scale with the total number of channels, I’m curious to see how much impact that has.

https://git.rust-bitcoin.org/lightningdevkit/rust-lightning/issues/4768

AI-Assisted-By: OpenAI Codex
Comment threadbenches/operations.rs
Comment threadbenches/operations.rs Outdated
Comment threadbenches/common/db_store.rs Outdated
Benchmark realistic payment and pending-payment persistence workloads across
filesystem, SQLite, and optional PostgreSQL stores. Use the async KV-store
APIs so the measured paths match the database interfaces used by async
persistence.
AI-Assisted: Codex
Run the existing payments benchmark once per configured store backend so
filesystem, SQLite, and optional PostgreSQL results are reported under the
same payment flow.
AI-assisted-by: OpenAI Codex
Add an operations bench target with a forwarding benchmark that compares
sqlite, filesystem, and postgres stores over a settled multi-hop payment.
AI-assisted-by: OpenAI Codex
Add a channel-open benchmark that measures the open_channel call while
leaving chain confirmation cleanup outside the timed section.
AI-assisted-by: OpenAI Codex
Add a startup benchmark that restarts a node whose store already
contains channel and payment data, so startup cost reflects persisted
node state.
AI-assisted-by: OpenAI Codex
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Benchmark database options

4 participants

@benthecarman@ldk-reviews-bot@tnull@joostjager