graph: fix inefficient query for IsPublicNode
fixes #10337 (partially)
Change Description
In this PR, we rewrite the query for IsPublicV1Node, which returns a boolean based on if a node has a public or private channel. This particularly fixes an issue sqlite backend as it struggles to efficiently use multiple indexes across seperate columns in one query.
Step to test
cd graph/db
go test -tags test_db_sqlite -run TestNodeIsPublic -v # ensure we still correctly distinguish between pub & priv
Summary of Changes
Hello @Abdulkbk, I'm Gemini Code Assist[^1]! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!
This pull request addresses a significant performance bottleneck in the IsPublicV1Node query, which was particularly inefficient for sqlite databases due to its struggle with complex OR conditions across multiple indexed columns. By restructuring the query to use UNION ALL, the change optimizes how the database utilizes indexes, leading to more efficient execution and resolving the identified performance issue.
Highlights
- Query Optimization: The
IsPublicV1Nodequery has been rewritten to improve its efficiency, specifically targeting performance issues with thesqlitebackend. - SQL Query Refactoring: The original query's
ORcondition across joined tables (n.id = c.node_id_1 OR n.id = c.node_id_2) has been refactored into two separateSELECTstatements combined withUNION ALL. This change allows for better utilization of database indexes. - Dependency Management: A local replace directive for
github.com/lightningnetwork/lnd/sqldbhas been added togo.mod, pointing to the localsqldbdirectory.
Using Gemini Code Assist
The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.
Invoking Gemini
You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.
| Feature | Command | Description |
|---|---|---|
| Code Review | /gemini review |
Performs a code review for the current pull request in its current state. |
| Pull Request Summary | /gemini summary |
Provides a summary of the current pull request in its current state. |
| Comment | @gemini-code-assist | Responds in comments when explicitly tagged, both in pull request comments and review comments. |
| Help | /gemini help |
Displays a list of available commands. |
Customization
To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.
Limitations & Feedback
Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with :thumbsup: and :thumbsdown: on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.
You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.
[^1]: Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.
can you create some stress tests to see whether union is actually better ?
can you create some stress tests to see whether union is actually better ?
I added a benchmark as the first commit, the following is what I found after running:
go test -tags=test_db_sqlite -bench=BenchmarkIsPublicNode > sqlite_or.txt
# then
go test -tags=test_db_sqlite -bench=BenchmarkIsPublicNode > sqlite_union.txt
# finally
benchstat sqlite_or.txt sqlite_union.txt
| Query Type | Iterations (b.N) |
Time per op (ns/op) |
|---|---|---|
| UNION ALL | 388 | 3,009,488 ns/op (~3ms) |
| OR | 289 | 4,063,903 ns/op (~4ms) |
The fact that this benchmark is run with approximately 8k nodes means that, if the number of nodes is increased to around ~80k, the difference will become more noticeable.
@abdulkbk, remember to re-request review from reviewers when ready
Thanks @MPins, for taking a look. I've addressed the feedback.
Thanks for taking a look @GustavoStingelin. From the output, we can see that indeed the union_all performs better, took less time to plan and execute.
yeah I think having a union call makes sense, can land after the cache PR if we still see major bottlenecks, however I think the cache will already solve our problems. The speed gain with a union is probably around ~20-30% given that node ids will be equally queried meaning that in the UnionCall we will likely also have to scan the table 2 times for 50% the node announcements we receive. I think planning plans are cached in PG so the planning time is not really super critical in a query.