Skip to content

Conversation

@ziggie1984
Copy link
Collaborator

@ziggie1984 ziggie1984 commented Oct 13, 2025

Depends on #10287

This PR implements most of the Insert and Delete methods we need for the unit tests in the following PR.

@ziggie1984 ziggie1984 changed the base branch from master to elle-payment-sql-series-new October 13, 2025 19:13
@ziggie1984 ziggie1984 self-assigned this Oct 13, 2025
@ziggie1984 ziggie1984 added payments Related to invoices/payments sql labels Oct 13, 2025
@ziggie1984 ziggie1984 added this to v0.21 Oct 13, 2025
@ziggie1984 ziggie1984 added this to the v0.21.0 milestone Oct 13, 2025
@saubyk saubyk moved this to In progress in v0.21 Oct 14, 2025
@ziggie1984 ziggie1984 changed the title [Part 3|*] Implement FetchPayment for SQL Backend [Part 3|*] Implement Second Part for SQL Backend functions Oct 15, 2025
@ziggie1984 ziggie1984 force-pushed the introduce-sql-schema-payments-part-3 branch 2 times, most recently from 80de0dd to c0134ed Compare October 15, 2025 15:12
@ziggie1984 ziggie1984 marked this pull request as ready for review October 15, 2025 15:13
@ziggie1984 ziggie1984 force-pushed the introduce-sql-schema-payments-part-3 branch 3 times, most recently from f0e3302 to 99ddf07 Compare October 17, 2025 07:53
@ziggie1984
Copy link
Collaborator Author

/gemini review

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 implements a significant portion of the insert and delete methods for the new SQL backend for the payments database. The changes are extensive and include new SQL queries, data conversion functions, and the implementation of several methods on the SQLStore.

The use of ExecuteBatchQuery and ExecutePaginatedQuery is great for performance. The new PaymentAndIntent interface is a good abstraction.

I've found a few areas for improvement:

  • Adherence to the function commenting style guide for exported functions.
  • Consistent use of context.Context propagation instead of context.TODO().
  • Opportunities for performance improvements by using batch inserts and optimizing queries to avoid extra fetches.
  • A potential code smell related to handling of nil slices from the database.

Overall, this is a solid step forward in the SQL backend implementation. Addressing these points will improve the code's maintainability, robustness, and performance.

Comment on lines 281 to 286
// TODO(ziggie): Can we get rid of this?
// This has to be in place otherwise the
// comparison will not match.
if record.Value == nil {
record.Value = []byte{}
}

Choose a reason for hiding this comment

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

medium

The TODO here highlights a potential code smell. This workaround of converting a nil slice to an empty slice might be hiding an issue or could be simplified. It would be beneficial to investigate whether the downstream code that consumes these records can be made to handle nil slices gracefully. This would remove the need for this conversion and make the data handling more consistent.

@ziggie1984 ziggie1984 force-pushed the introduce-sql-schema-payments-part-3 branch from 99ddf07 to 6145682 Compare October 17, 2025 08:43
@ziggie1984 ziggie1984 moved this from In progress to In review in v0.21 Oct 18, 2025
Comment on lines 540 to 776
completePayment, err := s.fetchPaymentWithCompleteData(
ctx, db, fetchPayment,
)
if err != nil {
Copy link
Collaborator

Choose a reason for hiding this comment

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

i dont think we need to fetch the complete payment here?

Copy link
Member

Choose a reason for hiding this comment

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

yeah looks like overkill to get all the data, I think we only need completePayment.Status?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

unfortunately we have to because it depends on the state of the HTLCs, so we need to always fetch the whole payment

se also func (m *MPPayment) setState() error {

Copy link
Member

@yyforyongyu yyforyongyu left a comment

Choose a reason for hiding this comment

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

Cool and would appreciate if the CIs can pass first before requesting reviews, especially the linter.

Comment on lines 540 to 776
completePayment, err := s.fetchPaymentWithCompleteData(
ctx, db, fetchPayment,
)
if err != nil {
Copy link
Member

Choose a reason for hiding this comment

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

yeah looks like overkill to get all the data, I think we only need completePayment.Status?

@lightninglabs-deploy
Copy link

@ziggie1984, remember to re-request review from reviewers when ready

@ziggie1984 ziggie1984 force-pushed the introduce-sql-schema-payments-part-3 branch from 6145682 to 047e180 Compare November 10, 2025 17:56
@ziggie1984 ziggie1984 moved this from In review to In progress in v0.21 Nov 10, 2025
@ziggie1984 ziggie1984 force-pushed the introduce-sql-schema-payments-part-3 branch from 047e180 to 5f2b14a Compare November 11, 2025 10:29
Copy link
Collaborator Author

@ziggie1984 ziggie1984 left a comment

Choose a reason for hiding this comment

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

Addressed all the feedback, I think the batch inserting I will do in a followup PR.

Comment on lines 540 to 776
completePayment, err := s.fetchPaymentWithCompleteData(
ctx, db, fetchPayment,
)
if err != nil {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

unfortunately we have to because it depends on the state of the HTLCs, so we need to always fetch the whole payment

se also func (m *MPPayment) setState() error {

Comment on lines 645 to 908
completePayment, err := s.fetchPaymentWithCompleteData(
ctx, db, existingPayment,
)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

same as above we need to make sure no HTLCs are INFLIGHT and none exist or all are failed basically.

@ziggie1984
Copy link
Collaborator Author

The itest for native_sql backends will not pass in this PR as well, they will start to pass from the next PR one (Part 4)

Copy link
Collaborator

@ellemouton ellemouton left a comment

Choose a reason for hiding this comment

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

looking good!

return nil
}

ctx := context.TODO()
Copy link
Collaborator

Choose a reason for hiding this comment

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

keep at top of file?

Copy link
Member

Choose a reason for hiding this comment

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

nit: guess I don't understand this change - why moving this ctx definition?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I am rebasing all these PRs on top of the complete serious and sometimes I think because later in the series these context get dropped and from some reason some merge conflict probably led to this.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

fixed

//
// TODO(ziggie): Refactor to not mix application logic with database
// logic. This decision should be made in the application layer.
if s.keepFailedPaymentAttempts {
Copy link
Collaborator

Choose a reason for hiding this comment

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

i think what YY is saying it that this is changing the behaviour as it currently stands. For KVStore: if DeletePayment is called from (r *rpcServer) DeletePayment, there is no check on keepFailedPaymentAttempts since it calls (p *KVStore) DeletePayment which does not check that. So if we want to keep the behaviour the same for (s *SQLStore) DeletePayment, then we should not check it there either.

FirstHopCustomRecords

for key, value := range firstHopCustomRecords {
err = db.InsertPaymentFirstHopCustomRecord(
Copy link
Collaborator

Choose a reason for hiding this comment

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

yeah i think there isnt right now an easy way to do bulk inserts

Comment on lines +44 to 45
CONSTRAINT idx_payments_payment_identifier_unique
UNIQUE (payment_identifier)
Copy link
Collaborator

Choose a reason for hiding this comment

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

do we not want to instead name these?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I think they are named and the name in this case is idx_payments_payment_identifier_unique , Can you confirm @gemini-code-assist

Choose a reason for hiding this comment

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

Yes, you are correct. The UNIQUE constraint is explicitly named idx_payments_payment_identifier_unique using the CONSTRAINT keyword. This is the standard way to provide a name for a constraint in SQL.

Copy link
Member

@yyforyongyu yyforyongyu left a comment

Choose a reason for hiding this comment

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

Getting close!

-- ─────────────────────────────────────────────

-- ─────────────────────────────────────────────
-- Payment Intents Table
Copy link
Member

Choose a reason for hiding this comment

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

nit: for future commits, it'd be easier for the reviewers if we don't move the code yet add some changes in one commit, which can be challenging for the reviewers to do a line-by-line comparison.

return nil
}

ctx := context.TODO()
Copy link
Member

Choose a reason for hiding this comment

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

nit: guess I don't understand this change - why moving this ctx definition?

//
// TODO(ziggie): Refactor to not mix application logic with database
// logic. This decision should be made in the application layer.
if s.keepFailedPaymentAttempts {
Copy link
Member

Choose a reason for hiding this comment

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

Yeah keepFailedPaymentAttempts is a config value, which means the users want to delete the failed attempts automatically after a payment is settled, this shouldn't affect how the DeletePayment works. However with the current change it will introduce a bug.

return fmt.Errorf("failed to fetch payment: %w", err)
}

mpPayment, err = s.fetchPaymentWithCompleteData(
Copy link
Member

Choose a reason for hiding this comment

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

Are there upcoming performance tests?


// Add the attempt to the payment without fetching it from the
// DB again.
mpPayment.HTLCs = append(mpPayment.HTLCs, HTLCAttempt{
Copy link
Member

Choose a reason for hiding this comment

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

Designing a safe DB layer is what we are trying to do here, yet we delegate that responsibility to the callers. The most proper way to handle this is via SELECT FOR UPDATE, but unfortunately due to our multi-sql schema support, this cannot be easily done at the moment. We sort of rely on the RegisterAttempt implemented in ControlTower, which holds a lock before calling this method, more like a mediation than solution. And here we already know this in-memory append will create a split brain issue with the data on disk, so I don't understand why we stick to this approach? @gemini-code-assist

If we are to go with this approach, we should document in details about the possible race, and how the mpPayment.HTLCs can be different from what's stored in db.

@ziggie1984 ziggie1984 moved this from In review to In progress in v0.21 Nov 19, 2025
@ziggie1984 ziggie1984 force-pushed the introduce-sql-schema-payments-part-3 branch from 9fd7eba to dbcd822 Compare November 19, 2025 22:39
Copy link
Collaborator Author

@ziggie1984 ziggie1984 left a comment

Choose a reason for hiding this comment

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

Thank you both for the thorough reviews 🙏

Comment on lines +44 to 45
CONSTRAINT idx_payments_payment_identifier_unique
UNIQUE (payment_identifier)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I think they are named and the name in this case is idx_payments_payment_identifier_unique , Can you confirm @gemini-code-assist

return nil
}

ctx := context.TODO()
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I am rebasing all these PRs on top of the complete serious and sometimes I think because later in the series these context get dropped and from some reason some merge conflict probably led to this.

return nil
}

ctx := context.TODO()
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

fixed

//
// TODO(ziggie): Refactor to not mix application logic with database
// logic. This decision should be made in the application layer.
if s.keepFailedPaymentAttempts {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

now I see it, weird that I had it in. Fixed. Nice catch!

return fmt.Errorf("failed to fetch payment: %w", err)
}

mpPayment, err = s.fetchPaymentWithCompleteData(
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

yes after migration we will focus on proper performance testing


// Add the attempt to the payment without fetching it from the
// DB again.
mpPayment.HTLCs = append(mpPayment.HTLCs, HTLCAttempt{
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

let's fetch the whole payment instead of doing the HTLC append in memory logic.


// Add the attempt to the payment without fetching it from the
// DB again.
mpPayment.HTLCs = append(mpPayment.HTLCs, HTLCAttempt{
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

nice catch and inputs 🙏

@ziggie1984 ziggie1984 moved this from In progress to In review in v0.21 Nov 19, 2025
Copy link
Member

@yyforyongyu yyforyongyu left a comment

Choose a reason for hiding this comment

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

LGTM💟


// Insert blinded route data if present. Every hop in the
// blinded path must have an encrypted data record.
if hop.EncryptedData != nil {
Copy link
Member

Choose a reason for hiding this comment

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

weird...I remembered this was changed before, but now changed back?

@ziggie1984 ziggie1984 force-pushed the introduce-sql-schema-payments-part-3 branch from dbcd822 to dc99a77 Compare November 20, 2025 08:53
@ziggie1984 ziggie1984 force-pushed the introduce-sql-schema-payments-part-3 branch from dc99a77 to 1101cc3 Compare November 20, 2025 09:00
Copy link
Collaborator

@ellemouton ellemouton left a comment

Choose a reason for hiding this comment

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

@ziggie1984 ziggie1984 merged commit a4756cd into lightningnetwork:elle-payment-sql-series-new Nov 20, 2025
33 of 38 checks passed
@github-project-automation github-project-automation bot moved this from In review to Done in v0.21 Nov 20, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

payments Related to invoices/payments sql

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

4 participants