Thanks to visit codestin.com
Credit goes to github.com

Skip to content

invoices/sql: fix full table scans on HTLC settlement hot path#10619

Open
ziggie1984 wants to merge 5 commits intolightningnetwork:masterfrom
ziggie1984:increase-settling-speed
Open

invoices/sql: fix full table scans on HTLC settlement hot path#10619
ziggie1984 wants to merge 5 commits intolightningnetwork:masterfrom
ziggie1984:increase-settling-speed

Conversation

@ziggie1984
Copy link
Collaborator

@ziggie1984 ziggie1984 commented Mar 2, 2026

Summary

Resulted from #10535, where we observed SQLITE slowdowns during performance testing.

Every invoice lookup and update on the HTLC settlement hot path was
causing a full table scan of the invoices table. The root cause was the
catch-all GetInvoice query, which used an OR $1 IS NULL pattern for
each filter parameter:

WHERE (i.hash = $1 OR $1 IS NULL)
AND (i.payment_addr = $2 OR $2 IS NULL)

SQLite's query planner resolves execution plans at prepare time, before
seeing actual parameter values. Because either condition can be trivially
true when its parameter is NULL, the planner conservatively falls back to
a full table scan rather than using the unique indexes on hash and
payment_addr.

This PR fixes the issue in two steps:

  1. Replace the single catch-all query with three dedicated queries, each
    filtering on a uniquely-constrained column:

    • GetInvoiceByHash: WHERE hash = $1
    • GetInvoiceByAddr: WHERE payment_addr = $1 (new, covers AMP path)
    • GetInvoiceBySetID: existing, unchanged
  2. Drop four redundant or unused indexes that were adding write overhead
    and B-tree bloat without benefiting any query:

    • invoices_hash_idx and invoices_payment_addr_idx: duplicated by
      the UNIQUE constraints on those columns
    • invoices_preimage_idx and invoices_settled_at_idx: never used
      as query filters

Remove four indexes from the invoices table that either duplicate existing
UNIQUE constraint indexes or are never used in WHERE clauses:

- invoices_hash_idx: redundant, UNIQUE constraint on hash already creates
  an implicit index
- invoices_payment_addr_idx: redundant, UNIQUE constraint on payment_addr
  already creates an implicit index
- invoices_preimage_idx: unused, preimage is NULL on all new invoices and
  is never used as a query filter
- invoices_settled_at_idx: unused, settled_at is NULL on all pending
  invoices and is never used as a query filter (settle_index is used
  instead)

Dropping these reduces B-tree working set size, which improves page cache
utilization as the invoice table grows.
@gemini-code-assist
Copy link

Summary of Changes

Hello, I'm Gemini Code Assist1! 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 significantly improves the performance of invoice lookups and updates, particularly in the HTLC settlement hot path. By moving away from a single, inefficient query that caused full table scans, and instead implementing targeted queries that utilize unique indexes, the system's database interaction for invoices becomes much more efficient. Additionally, the removal of unnecessary indexes further streamlines database operations, contributing to overall system health and speed.

Highlights

  • Performance Optimization: Eliminated full table scans on the invoices table during HTLC settlement by replacing a generic GetInvoice query with dedicated, indexed lookups.
  • Query Refactoring: Replaced the single catch-all GetInvoice query with three specialized queries: GetInvoiceByHash, GetInvoiceByAddr (new), and GetInvoiceBySetID, leveraging unique indexes for efficiency.
  • Database Index Cleanup: Dropped four redundant or unused indexes (invoices_hash_idx, invoices_payment_addr_idx, invoices_preimage_idx, invoices_settled_at_idx) to reduce write overhead and B-tree bloat.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • invoices/sql_store.go
    • Added bytes package import.
    • Removed lnutils package import.
    • Updated the SQLInvoiceQueries interface to remove the generic GetInvoice method and add GetInvoiceByAddr.
    • Refactored the getInvoiceByRef function to use specific GetInvoiceByHash, GetInvoiceByAddr, and GetInvoiceBySetID calls, replacing the previous generic GetInvoice logic.
    • Introduced a check for ErrInvRefEquivocation when both payment hash and address are provided and do not match.
  • sqldb/migrations_dev.go
    • Added a new migration entry for 000012_drop_redundant_invoice_indexes.
  • sqldb/sqlc/invoices.sql.go
    • Implemented the GetInvoiceByAddr function, which executes a SQL query to retrieve an invoice by its payment address.
  • sqldb/sqlc/migrations/000012_drop_redundant_invoice_indexes.down.sql
    • Added a new migration file to re-create the dropped indexes for rollback purposes.
  • sqldb/sqlc/migrations/000012_drop_redundant_invoice_indexes.up.sql
    • Added a new migration file to drop four redundant or unused invoice indexes: invoices_hash_idx, invoices_payment_addr_idx, invoices_preimage_idx, and invoices_settled_at_idx.
  • sqldb/sqlc/querier.go
    • Added GetInvoiceByAddr to the Querier interface.
  • sqldb/sqlc/queries/invoices.sql
    • Added the SQL query definition for GetInvoiceByAddr to fetch an invoice by its payment address.
Activity
  • No specific activity (comments, reviews, progress) has been recorded for this pull request yet.
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 👍 and 👎 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.

Footnotes

  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.

@lightninglabs-deploy lightninglabs-deploy added the severity-critical Requires expert review - security/consensus critical label Mar 2, 2026
@lightninglabs-deploy
Copy link
Collaborator

🔴 PR Severity: CRITICAL

Automated classification | 6 files (excl. auto-generated) | 139 lines changed

🔴 Critical (5 files)
  • sqldb/migrations_dev.go - sqldb/* is always CRITICAL per database migration rule
  • sqldb/sqlc/migrations/000012_drop_redundant_invoice_indexes.down.sql - database migration (rollback) in sqldb/*
  • sqldb/sqlc/migrations/000012_drop_redundant_invoice_indexes.up.sql - database migration (apply) in sqldb/*
  • sqldb/sqlc/querier.go - sqldb/* package, always CRITICAL
  • sqldb/sqlc/queries/invoices.sql - sqldb/* package, always CRITICAL
🟠 High (1 file)
  • invoices/sql_store.go - Invoice management and settlement (invoices/*)
Excluded (auto-generated) (1 file)
  • sqldb/sqlc/invoices.sql.go - *.sql.go auto-generated file, excluded from counting

Analysis

This PR adds a new SQL migration (000012_drop_redundant_invoice_indexes) that drops redundant indexes from the invoice tables, along with corresponding changes to the SQL store implementation and generated query code. The presence of database migration files in sqldb/* automatically triggers CRITICAL severity per classification rules — database migrations are always CRITICAL regardless of their apparent simplicity, because schema changes can affect data integrity and are difficult to reverse in production environments.

The migration drops indexes, which could have performance implications for queries against the invoice table. Reviewers should verify:

  1. All queries that relied on the dropped indexes still have adequate index coverage
  2. The migration is safely reversible (the .down.sql file looks correct)
  3. The new query added to invoices.sql.go correctly leverages remaining indexes

Severity bump check: 6 non-excluded files (< 20 threshold), 139 non-excluded lines changed (< 500 threshold) — no bump applied.


To override, add a severity-override-{critical,high,medium,low} label.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request effectively addresses a performance bottleneck by replacing a generic, slow database query with more specific, indexed queries for fetching invoices. The removal of redundant and unused database indexes is also a good cleanup. The overall approach is sound. My review includes a couple of suggestions to improve error handling consistency and to restore a safety check for ambiguous invoice references that was present in the previous implementation.

@saubyk saubyk added this to v0.21 Mar 2, 2026
@saubyk saubyk added this to the v0.21.0 milestone Mar 2, 2026
@saubyk saubyk moved this to In progress in v0.21 Mar 2, 2026
The GetInvoice query used an OR IS NULL pattern for each filter
parameter:

  WHERE (i.hash = $1 OR $1 IS NULL)
    AND (i.payment_addr = $2 OR $2 IS NULL)

SQLite's query planner decides on an execution plan at prepare time,
before seeing any parameter values. Because either condition can be
trivially true when its parameter is NULL, the planner conservatively
falls back to a full table scan rather than using the unique indexes on
hash and payment_addr. This caused every invoice lookup and update on
the hot path (HTLC settlement) to scan the entire invoices table.

Replace the single catch-all query with three dedicated queries, each
using a direct equality on a uniquely constrained column:

- GetInvoiceByHash:  WHERE hash = $1
- GetInvoiceByAddr:  WHERE payment_addr = $1  (new, covers AMP path)
- GetInvoiceBySetID: existing, unchanged

Update getInvoiceByRef to route to the appropriate query based on which
fields are present in the InvoiceRef. When both hash and payment address
are provided, we look up by hash and then verify the returned invoice's
payment address matches.

GetInvoice is now unused and removed from the SQLInvoiceQueries
interface.
@ziggie1984 ziggie1984 force-pushed the increase-settling-speed branch from 4ad19a9 to 3ace0b4 Compare March 2, 2026 18:18
After upgrading Bob or Dave, the test only waited for the upgraded
node's own channels to become active. However, for alice to route a
payment to dave she needs her local channel to Bob to be active from
her own perspective, which is only the case once the p2p connection
is re-established.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

invoices severity-critical Requires expert review - security/consensus critical sql

Projects

Status: In progress

Development

Successfully merging this pull request may close these issues.

3 participants