-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
invoices: migrate KV invoices to native SQL for users of KV SQL backends #8831
base: master
Are you sure you want to change the base?
Conversation
Important Review skippedAuto reviews are limited to specific labels. 🏷️ Labels to auto review (1)
Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
6682b50
to
338e1f0
Compare
d2a329f
to
6379a8b
Compare
5fe92e2
to
a7bf598
Compare
var paymentHash lntypes.Hash | ||
if invoice.Terms.PaymentPreimage != nil { | ||
paymentHash = invoice.Terms.PaymentPreimage.Hash() | ||
} else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't correctly handle the case of an invoice generated by receiving a spontaneous AMP payment, which should have an empty payment hash IIUC. To test this, run the migration on Bob's DB resulting from the itest sendpayment_amp_invoice/external_payaddr
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice catch, thank you for pointing this out! Fix is coming soon as well as the rest of the missing parts. Migrating spontaneous AMP invoices required building an index at migration time so we're able to map add indexes to payment hashes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks like spontaneous amp payments still have a valid payment hash tho or ?
b6f0ac8
to
b983851
Compare
b983851
to
706b444
Compare
96f0cbe
to
bfe4ad5
Compare
d30d10c
to
6a6074c
Compare
Previously we intentially did not set settled_at and settle_index when inserting a new invoice as those fields are set when we settle an invoice through the usual invoice update. As migration requires that we set these nullable fields, we can safely add them.
This commit adds the migration_tracker table which we'll use to track if a custom migration has already been done.
Certain invoices may not have a deterministic payment hash. For such invoices we still store the payment hashes in our KV database, but we do not have a sufficient index to retrieve them. This PR adds such index to the SQL database that will be used during migration to retrieve payment hashes.
This commit runs the invoice migration if the user has a KV SQL backend configured.
6a6074c
to
e5b6d81
Compare
Sorry for the lack of review from me, I'll re-review late this week/early next week! I'm just now getting back to working on this project after some other higher-priority stuff landed on my plate. |
// Temporary replace until https://github.com/lightningnetwork/lnd/pull/8831 is | ||
// merged. | ||
replace github.com/lightningnetwork/lnd/sqldb => ./sqldb |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like this commit should be dropped.
@@ -235,6 +235,34 @@ func (q *Queries) GetAMPInvoiceID(ctx context.Context, setID []byte) (int64, err | |||
return invoice_id, err | |||
} | |||
|
|||
const insertAMPSubInvoice = `-- name: InsertAMPSubInvoice :execresult |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd love if you could explain how this magic string is consumed by things. I figured out that it has to do with code generation but I'd like to know what tool consumes this and creates the actual go code InsertAMPSubInvoice
-- migration_tracker is a table that keeps track of custom migrations that have | ||
-- been run on the database. This table is used to ensure that migrations are | ||
-- idempotent and are only run once. | ||
CREATE TABLE IF NOT EXISTS migration_tracker ( | ||
-- migration_id is the id of the migration. | ||
migration_id TEXT NOT NULL, | ||
|
||
-- migration_ts is the timestamp at which the migration was run. | ||
migration_ts TIMESTAMP, | ||
|
||
PRIMARY KEY (migration_id) | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How did we handle this before?
migration_id TEXT NOT NULL, | ||
|
||
-- migration_ts is the timestamp at which the migration was run. | ||
migration_ts TIMESTAMP, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If there isn't a really solid reason to abbreviate this as ts
I'd rather spell it out as timestamp
or just time
@@ -1 +1,2 @@ | |||
DROP TABLE IF EXISTS migration_tracker; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if this makes more sense to go at the beginning of the PR series. Not a big deal but it seems like this is unrelated to the core of the PR and is more around to give us atomic/idempotence guarantees on migrations.
len(k)) | ||
} | ||
|
||
copy(paymentHash[:], k) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe a dumb question, but why not just put k
directly into InsertInvoicePaymentHashAndKeyParams{...}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it was a defense mechanism before loopvar
was introduced ?
@@ -135,3 +139,222 @@ func createInvoiceHashIndex(ctx context.Context, db kvdb.Backend, | |||
}) | |||
}, func() {}) | |||
} | |||
|
|||
// MigrateSingleInvoice migrates a single invoice to the new SQL schema. Note | |||
// that prefect equality between the old and new schema is not possible as the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/prefect/perfect/g
InvoiceID: invoiceID, | ||
} | ||
|
||
if htlc.MppTotalAmt != 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we excluding the 0 case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
because non-mpp payments don't have this value so we want to keep it null in that case.
) | ||
} | ||
|
||
if !htlc.ResolveTime.IsZero() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we excluding the 0 case here too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we want to keep the resolve time null for still unresolved htlcs
// have the AMP feature bit set and contain AMP HTLCs. Note that this function | ||
// is not supposed to generate correct invoices, but rather random invoices for | ||
// testing the SQL migration. | ||
func generateTestInvoice(t *testing.T, mpp bool, amp bool) *Invoice { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is just a migration code test, and therefore its expected lifetime is small I don't think you should change this, however, it is worth noting that much of this code could have been avoided by using rapid for all of the generation. I mention this just so that next time you encounter the need for this you need not roll your own random gen stuff. We introduced it in #9032
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Really great work 👌, had a first pass.
Now trying it out on some dbs and different systems to see if we run into memory issues when migrating dbs > 1 mio invoices.
} | ||
|
||
// Skip the test if the node is already running with native SQL. | ||
if bob.Cfg.NativeSQL { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm but does this mean bob already migrated ?
if !d.cfg.DB.SkipSQLInvoiceMigration { | ||
err = invoices.MigrateInvoicesToSQL( | ||
ctx, dbs.GraphDB.Backend, dbs.GraphDB, | ||
sqlInvoiceDB, 10, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: define defaults for migration batching
|
||
// If the invoice is settled, we'll also set the timestamp and the index | ||
// at which it was settled. | ||
if invoice.State == ContractSettled { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we log an error with we encounter a settled state which has no index and no settledate ?
} | ||
|
||
if invoice.Memo != nil { | ||
params.Memo = sql.NullString{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use sqldb.SQLStr
"github.com/lightningnetwork/lnd/lntypes" | ||
"github.com/lightningnetwork/lnd/sqldb/sqlc" | ||
) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could you please describe which invoices have no payment hash in itself but still have the payment_hash index filled ?
|
||
const batchSize = 11 | ||
err := invpkg.MigrateInvoicesToSQL( | ||
ctxb, store1.Backend, store1, store2, batchSize, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
call store1
boltdb maybe ?
IndexOffset: 0, | ||
// As a sanity check, fetch more invoices than we have | ||
// to ensure that we did not add any extra invoices. | ||
NumMaxInvoices: 9999, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we know how much the db had initially would be an important information here.
result1, err := db.QueryInvoices(ht.Context(), query) | ||
require.NoError(ht, err) | ||
|
||
numInvoices := len(result1.Invoices) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why don't we do a length check here for 35 invoices ?
bob.SetExtraArgs([]string{"--db.use-native-sql"}) | ||
|
||
// Now run the migration flow three times to ensure that each run is | ||
// idempotent. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But I do understand it correctly that even though we run it 3 times, the migration only happens once when we start bob via the native sql flag at the beginning ?
// testNativeSQLNoMigration tests that nodes that have invoices would not start | ||
// up with native SQL enabled, as we don't currently support migration of KV | ||
// invoices to the new SQL schema. | ||
func testNativeSQLNoMigration(ht *lntest.HarnessTest) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but we still have a check in place that as soon as I start a boltdb version if the nativesql flag set, that it will fail with a detailed error that these two settings are not supported ?
@bhandras, remember to re-request review from reviewers when ready |
Change Description
This pull request adds the migration of old key-value (KV) invoices to the new native SQL schema when the --db.use-native-sql flag is set, unless the --db.skip-sql-invoice-migration flag is also specified.
Please note that since we currently do not support running on mixed database backends for users of
bbolt
oretcd
, an additional step is required to migrate their KV database to SQL first. For more context, please see lightninglabs/lndinit#21.