Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Invocies: refactor InvoiceDB methods #7542

Conversation

positiveblue
Copy link
Collaborator

@positiveblue positiveblue commented Mar 27, 2023

This PR includes multiple changes. Some of them are specific to the InvoiceDB, some to the Invoice domain logic in general and others to the new DB interfaces.

Changes to the InvoiceDB:
- New set of methods that remove most of the domain logic from the db implementation. The new methods are "dumber" and they are supposed to be used to simply store/retrieve/delete data from the db.
- For each new method two versions have been added. One with a explicit tx and another one without it. When the new set of interfaces for storage (last commit) is finished only the ones without explicit tx will survive (WitTx will also be removed)

Changes to the Invoice domain logic:
- Most of the logic to check if an invoice could be updated and what changes where needed to be applied given a new event and the current invoice status have been removed from the channeldb package.
- In order to add an htlc, settle an invoice or canceling it we can use now its struct methods. Those methods implement the flow for all the invoice types (regular, hodl, amp...). We may want to add a new interface for invoices in the future. Until then most of the logic is separated in specific methods for each type while the public method simply implements acts as dispatcher.

New DB interfaces:
- The idea is to have a TxExecutor that is able to bind a tx to a db interface and injects it to the users custom logic. This is similar to what we have in other projects (but generic not only for sql/sqlc). You can check the latest commit for more.

Part of #6288

@saubyk saubyk added this to the v0.17.0 milestone Mar 27, 2023
@positiveblue positiveblue force-pushed the invoicedb-withTx branch 2 times, most recently from 3d131d9 to ea2771b Compare April 6, 2023 08:10
Copy link
Collaborator

@bitromortac bitromortac left a comment

Choose a reason for hiding this comment

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

Have done a high-level review up to invoiceregistry: refactor CancelInvoice. I like the work towards a more CRUD-based approach 🥇 , breaking the interface methods apart, thereby decoupling concerns.
I'm not sure about duplication of the interface methods, although I can understand the usability concerns, but probably would prefer to have only the tx-based ones.

database/tx.go Outdated
Rollback() error
}

type TxExecutor interface {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This could already use a docstring to make the motivation clearer

//
// NOTE: This function will be executed in its own transaction and will
// use the default timeout.
AddInvoice(invoice *Invoice) error
Copy link
Collaborator

Choose a reason for hiding this comment

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

If those methods stay, could they be implemented by wrapping the tx-based one with WithTx?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The WithTx will be removed from the interface. Under the hood they will be implemented with the WithTx though

invoices/interface.go Outdated Show resolved Hide resolved
// AddIndex on newInvoice.
func (d *DB) AddInvoice(newInvoice *invpkg.Invoice, paymentHash lntypes.Hash) (
uint64, error) {
// WithTx is a helper method that allows callers to execute a function within a
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: commit message should start with channeldb+invoices

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is actually I doubt that I have because i am not sure if we can use the "component" that we are updating in the commit message instead of going "folder/package" based.

channeldb/invoices.go Show resolved Hide resolved

// To ensure consistency check that the already fetched
// invoice key matches the one in the settle index.
key := settleIndex.Get(settleIndexKey[:])
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can settleIndex be nil (the original code had a nil check, see also the comment above settleIndex)?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

not here. Before it could be nil because i was a point to a ref. Now we are using the value of an uint64, which will always have a value. The value 0 is considered "nil" or not-set yet

channeldb/invoice_test.go Outdated Show resolved Hide resolved
invoices/invoiceregistry.go Show resolved Hide resolved
invoices/invoiceregistry.go Outdated Show resolved Hide resolved
invoices/invoiceregistry.go Show resolved Hide resolved
@saubyk saubyk requested a review from bhandras April 6, 2023 15:24
@positiveblue positiveblue force-pushed the invoicedb-withTx branch 3 times, most recently from f092585 to edc98cd Compare April 12, 2023 04:27
The `DBTx` interface abstracts database transactions across different
backends.
The new field will be used in cases where we do not know the preimage of
an invoice, like in hodl invoices.
In the next commits we will move each one of the methods in the
temporary interface to `InvoiceDB`. This commit exists as a "declaration
of intentions" for the reviewers.

The new interface improves the API design and adds context support
For each operation we have two versions: one method that executes the
changes in a tx with only that query and a method that allows to execute
those operations in a given transaction.
The new method assumes that we can always get the payment hash from the
invoice itself, using the preimage/hash in the contract terms. That
allows us to have less parameters in the method but we need to ensure
that `Terms.PaymentHash` is not nil when `Terms.PaymentPreimage` is nil.

We do not return the addIndex anymore but directly modify the passed
parameter, as before.
This method will substitute the old `LookupInvoice`. The only difference
is that `HtlcSetOnlyModifier` is not needed anymore to signal that you
want to fetch only the htlcs related to that SetID.

Using `GetInvoice` with a SetID it will always return only the
htlcs related to that SetID. For fetching an AMP invoice with all its
htlcs, an InvRef with the relevant `PayAddr` needs to be used.

Another change is that if an `InvRef` refears a SetID that does not
exist the `GetInvoice` function will not fail automatically but will try
to fetch the invoice using other params like `PayAddr`. The rational
behind this is that when we are adding an htlc to an AMP invoice we want
to fetch the invoice and the relevant SetID. If this is the first htlc
for that set, we want the invoice with no htlcs.
The new method uses the normal `InvoiceRef` to know what invoice needs
to be deleted.
Many times adding a new htlc to an invoice does not trigger an update in
the invoice other than adding the htlc to the htlc set. In other cases,
adding the HTLC settles/cancels the invoice.

This new method helps domain logic to make this a two step process: add
a new htlc and later on if the invocie is settle/canceled update the
invoice and all the relevant htlcs.

Because right now the htlcs are stored serialized with the invocie this
method duplicates some logic that will live in the domain layer (ex:
`invoice.AddHTLC`)
The methods has been renamed to `UpdateInvoiceV2` to avoid breaking
specific unit tests for the db part (store/retrieve).
In this case the implementation can be a noop method. That's because
everytime that we update the htlcs of an invoice we will also call
the `UpdateInvoice`. Because of how we were serializing/storing invocies
in channeldb, updating an invoice will also store the htlcs.
Put all the domain logic to perform different action to an invoice in
invoice methods.
Use the new invoicedb interface.
Use the new invoicedb interface.
Use the new invoicedb interface.
//
// TODO(positiveblue): delete this method from this interface and use
// something like the executor patter that we use in other projects.
WithTx(ctx context.Context, funcs ...func(tx database.DBTx) error) error
Copy link
Member

Choose a reason for hiding this comment

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

I think this can be removed now?

htlc *InvoiceHTLC) error

// AddHtlcWithTx adds new htlc to an existing invoice.
AddHtlcWithTx(ctx context.Context, dbTx database.DBTx, ref InvoiceRef,
Copy link
Member

Choose a reason for hiding this comment

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

Also we no longer need any of the WithTx suffix methods, right?

Copy link
Collaborator

@bhandras bhandras left a comment

Choose a reason for hiding this comment

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

My main comment is compatibility with existing backends and also that currently the diff is rather large and it's hard to tell which lines are new which are just copied over. Could the PR be redone such that the diff is focused on the changed parts while still remains compatible? Is the plan to use existing test coverage to cover all of this PR or will there be new test coverage added too?

@@ -262,6 +262,11 @@ type ContractTerm struct {
// extended. Set to nil if the preimage isn't known yet.
PaymentPreimage *lntypes.Preimage

// PaymentHash is the hash of the preimage associated to this invoice.
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: associated to => with

t.Fatalf("unable to find invoice: %v", err)
}
err = db.AddInvoice(testInvoice)
require.NoError(t, err)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe worth adding a commit once the PR is close to change those too.

// AddInvoice inserts the targeted invoice into the database.
//
// NOTE: If the invoice is added to the database this will set the next
// avaialbe value for the AddIndex field.
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: avaialbe => available

}
} else {
invoiceNum = byteOrder.Uint32(invoiceCounter)
case invoice.Terms.PaymentHash != nil:
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: I think this case should come first so we don't recompute the hash from the preimage all the time if the preimage is set.

// NOTE: This function will be executed in its own transaction and will
// use the default timeout.
func (d *DB) AddInvoice(invoice *invpkg.Invoice) error {
tx, err := d.BeginReadWriteTx()
Copy link
Collaborator

Choose a reason for hiding this comment

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

This way we will break etcd compatibility. A better way would be to use a closure like kvdb interace's Update and View in order to allow the ...WithTx function to repeat if commit fails. (the etcd "driver" uses an STM to ensure compatibility with the bolt/kvdb interface).

}

payAddrIndex := tx.ReadBucket(payAddrIndexBucket)
setIDIndex := tx.ReadWriteBucket(setIDIndexBucket)
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: does this need to be an rw bucket?

if invoiceNumBySetID == nil {
return nil, invpkg.ErrInvoiceNotFound
if invoiceNumBySetID != nil {
return invoiceNumBySetID, nil
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why the change?

@@ -187,14 +187,14 @@ func testInvoiceWorkflow(t *testing.T, test invWorkflowTest) {
// Attempt to retrieve the invoice which was just added to the
// database. It should be found, and the invoice returned should be
// identical to the one created above.
dbInvoice, err := db.LookupInvoice(ref)
dbInvoice, err := db.GetInvoice(ref)
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: can you please explain in more detail why remove LookUpInvoice makes sense in the commit message?

}

// DeleteInvoicesWithTx updates the invoice identified by the passed InvoiceRef.
func (d *DB) DeleteInvoicesWithTx(ctx context.Context, dbTx database.DBTx,
Copy link
Collaborator

Choose a reason for hiding this comment

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

For all these ...WithTx functions maybe it'd make sense to first rename them in place and do the changes there to reduce the diff size. Right now it's not easy to see what changed in the function bodies.

@positiveblue positiveblue added advanced Issues suitable for very experienced developers epic Issues created to track large feature development labels Apr 20, 2023
@Roasbeef Roasbeef added the sql label Jun 5, 2023
@saubyk saubyk removed this from the v0.17.0 milestone Jun 9, 2023
@positiveblue
Copy link
Collaborator Author

Closed in favor of #7354 #7343 #7357

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
advanced Issues suitable for very experienced developers epic Issues created to track large feature development sql
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants