payments/db: remap legacy zero attempt IDs#10799
Conversation
Summary of ChangesHello, 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 addresses a critical issue where legacy KV payments containing HTLC attempts with an ID of zero cause unique constraint violations when migrating to the SQL schema. By implementing a synthetic ID allocation strategy that leverages the existing switch payment ID sequencer, the migration process can now safely assign unique identifiers to these legacy records. Additionally, the PR improves diagnostic logging and error reporting for the migration process to better identify and resolve potential data conflicts. Highlights
New Features🧠 You can now enable Memory (public preview) 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. Using Gemini Code AssistThe 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
Customization To customize the 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 Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. 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. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request updates the payment migration logic to handle legacy HTLC attempts with zero IDs by allocating unique identifiers from the switch sequencer's horizon, preventing SQL unique constraint collisions. The changes introduce an attemptIDAllocator and a collectMigrationState function to manage this process. Feedback was provided to ensure the new implementation adheres to the repository style guide, specifically regarding the requirement for function documentation to begin with the function name and the use of in-body comments to explain the intention of logical code blocks.
5c1be14 to
c89f2ac
Compare
|
🟡 PR Severity: MEDIUM Automated classification | 1 file (non-test) | 212 lines changed Medium (1 file): - payments/db/migration1/sql_migration.go - payments package SQL migration Excluded from scoring (1 file): - payments/db/migration1/sql_migration_test.go - test file Analysis: This PR modifies a SQL migration under payments/db/migration1/. The payments/* path maps to MEDIUM severity. The always-CRITICAL migration override covers channeldb/migration*, sqldb/, and wtdb/ -- this path does not match. Bump thresholds: 1 non-test file (threshold: >20) and 212 lines changed (threshold: >500) -- no bump applied. To override, add a severity-override-{critical,high,medium,low} label. <!-- pr-severity-bot --> |
43c48bc to
5458424
Compare
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces a mechanism to handle legacy KV payments with attempt ID zero during SQL migration by using an attemptIDAllocator to generate unique synthetic IDs from the switch sequencer horizon. It also includes logic to update the sequencer post-migration and adds validation helpers. Feedback focuses on a critical bug where the allocator starts at the last used sequence ID instead of the next available one, potentially causing collisions. Additionally, the reviewer noted several locations where missing nil checks on SessionKey() could lead to panics during migration and validation.
|
Regarding the review suggestion to start the synthetic attempt ID allocator at The switch sequencer does not use the generic bbolt So if the stored value is |
7f0c728 to
3dfdd4f
Compare
dbd2261 to
eb7ce77
Compare
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
This PR updates the payments KV→SQL migration to remap legacy HTLC attempts that have attempt ID 0 by allocating synthetic, globally-unique SQL attempt indexes derived from the switch payment ID sequencer horizon, preventing UNIQUE constraint collisions during migration.
Changes:
- Add attempt-index remapping for legacy zero attempt IDs and advance the switch sequencer once migration succeeds.
- Improve migration error context for HTLC attempt inserts (include attempted index + payment hash).
- Add migration validation/test normalization so KV vs SQL comparisons account for expected remapped attempt IDs.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| payments/db/migration1/sql_migration.go | Introduces legacy attempt ID allocator, uses it during attempt migration, and persists updated switch sequencer horizon after successful migration. |
| payments/db/migration1/migration_validation.go | Normalizes legacy zero attempt IDs (KV side) during deep-compare so expected remaps don’t cause false mismatches. |
| payments/db/migration1/sql_migration_test.go | Adds test coverage for legacy zero attempt IDs and refactors test helpers to allow fixed attempt IDs. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
eb7ce77 to
128995a
Compare
| // attempt ID. | ||
| nextID uint64 | ||
|
|
||
| // persistNextID is the final next-unused ID to persist after |
There was a problem hiding this comment.
Do we actually need both of these variables?
IIUC the main thing we need to know is if we had to allocate any values at all from this range.
There was a problem hiding this comment.
good point, changing it because we can be sure that the migration runs exclusively.
128995a to
d370b98
Compare
| // It uses the in-memory counter initialized from the switch payment ID | ||
| // sequencer horizon. | ||
| func (a *attemptIDAllocator) allocateLegacyAttemptID() (uint64, error) { | ||
| if a.nextID == 0 { |
There was a problem hiding this comment.
This still fails the migration when legacy zero-ID HTLCs exist but the switch sequencer is at its initial horizon. collectMigrationState leaves nextSwitchPaymentID as 0 when the bucket is absent or when it exists but has never allocated a batch, and then this path returns cannot allocate legacy attempt ID: switch payment ID sequence is zero. A node with old zero-ID attempts and no later outgoing payment after the sequencer was introduced would still be unable to migrate. The runtime sequencer handles this initial state by skipping ID 0 and issuing 1, so the migration should do the same and create/advance the sequencer bucket as needed. I confirmed this locally with a temporary regression test that creates the sequencer bucket at sequence 0 plus a zero-ID payment; the migration fails here.
There was a problem hiding this comment.
ok done, now starting with 1 if it was zero in the first place
There was a problem hiding this comment.
Good catch. The migration should mirror the runtime sequencer here instead of failing on the initial horizon.
The adjusted approach is to keep seeding the allocator from the switch sequence bucket, but when the allocator is actually asked to issue a replacement ID and the cursor is 0, it skips 0 and starts at 1, matching htlcswitch/sequencer.go. After migration, the switch sequence is advanced to the final next-unused value, creating/advancing the sequencer bucket as needed.
I also added coverage for both cases:
- initial sequence
0: legacy attempts get1,2, final sequence3 - advanced sequence
10: legacy attempts get10,11, final sequence12
| sessionKeyBytes := sessionKey.Serialize() | ||
|
|
||
| attemptID := htlc.AttemptID | ||
| if attemptID == 0 { |
There was a problem hiding this comment.
This remaps every zero-ID attempt, including attempts that still have no settle/fail resolution. If such an attempt is genuinely in flight, the switch/circuit/result state may still know it as attempt ID 0, while SQL will resume it using the synthetic ID assigned here. That would make startup unable to match the live attempt/result. If legacy zero-ID attempts are guaranteed to be terminal historical records, then this is fine, but otherwise the migration should either preserve/handle live zero-ID attempts specially or explicitly guard/fail with a clear error before remapping them.
I think we should just mark these payments as failed if they are still pending now - they must have been failed already given how old they are.
There was a problem hiding this comment.
ok moving the payment and attempt to failed in case we find something like this. Increased the scope a bit.
d370b98 to
76584b5
Compare
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request implements a strategy for migrating legacy HTLC attempts with zero IDs from KV to SQL. It introduces a synthetic ID allocator that utilizes the switch sequencer's horizon to generate unique attempt indices and updates the migration process to fail unresolved legacy attempts. The changes also include logic to synchronize these IDs during post-migration validation and provide new unit tests for these edge cases. Reviewer feedback focused on adhering to the repository style guide by using newlines to separate logical stanzas for better readability.
76584b5 to
3a21b75
Compare
Legacy KV payments can contain HTLC attempts with attempt ID zero. The SQL payments schema requires payment_htlc_attempts.attempt_index to be globally unique, so migrating multiple such attempts can fail with a UNIQUE constraint violation. Allocate synthetic attempt indexes for legacy zero-ID attempts from the switch payment ID sequencer horizon. Keep nonzero attempt IDs unchanged and advance the switch sequence once after migration validation succeeds. This preserves the SQL uniqueness invariant and prevents future switch IDs from colliding with migrated attempts. It also wraps HTLC insert errors with the attempted index and payment hash so future migration failures identify the problematic row.
3a21b75 to
c11a292
Compare
Legacy KV payments can contain HTLC attempts with attempt ID zero. The SQL payments schema requires payment_htlc_attempts.attempt_index to be globally unique, so migrating multiple such attempts can fail with a UNIQUE constraint violation.
This PR allocates synthetic attempt indexes for legacy zero-ID attempts from the switch payment ID sequencer horizon, keeps nonzero attempt IDs unchanged, and advances the switch sequence once after migration validation succeeds.
It also wraps HTLC insert errors with the attempted index and payment hash so future migration failures identify the problematic row.
Fixes #10797