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

invoices: migrate KV invoices to native SQL for users of KV SQL backends #8831

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 23 additions & 28 deletions config_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -1079,41 +1079,36 @@ func (d *DefaultDatabaseBuilder) BuildDatabase(

// Instantiate a native SQL invoice store if the flag is set.
if d.cfg.DB.UseNativeSQL {
// KV invoice db resides in the same database as the graph and
// channel state DB. Let's query the database to see if we have
// any invoices there. If we do, we won't allow the user to
// start lnd with native SQL enabled, as we don't currently
// migrate the invoices to the new database schema.
invoiceSlice, err := dbs.GraphDB.QueryInvoices(
ctx, invoices.InvoiceQuery{
NumMaxInvoices: 1,
},
)
if err != nil {
cleanUp()
d.logger.Errorf("Unable to query KV invoice DB: %v",
err)

return nil, nil, err
}

if len(invoiceSlice.Invoices) > 0 {
cleanUp()
err := fmt.Errorf("found invoices in the KV invoice " +
"DB, migration to native SQL is not yet " +
"supported")
d.logger.Error(err)

return nil, nil, err
}

executor := sqldb.NewTransactionExecutor(
dbs.NativeSQLStore,
func(tx *sql.Tx) invoices.SQLInvoiceQueries {
return dbs.NativeSQLStore.WithTx(tx)
},
)

sqlInvoiceDB := invoices.NewSQLStore(
executor, clock.NewDefaultClock(),
)

// If the user has not explicitly requested to avoid the
// migration, we will attempt to migrate the invoices to the
// native SQL store.
if !d.cfg.DB.SkipSQLInvoiceMigration {
err = invoices.MigrateInvoicesToSQL(
ctx, dbs.GraphDB.Backend, dbs.GraphDB,
sqlInvoiceDB, 10,
Copy link
Collaborator

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 err != nil {
cleanUp()

err = fmt.Errorf("unable to migrate invoices "+
"to native SQL store: %w", err)
d.logger.Error(err)

return nil, nil, err
}
}

dbs.InvoiceDB = invoices.NewSQLStore(
executor, clock.NewDefaultClock(),
)
Expand Down
5 changes: 5 additions & 0 deletions docs/release-notes/release-notes-0.19.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@
## Testing
## Database

* [Migrate KV invoices to
SQL](https://github.com/lightningnetwork/lnd/pull/8831) as part of a larger
effort to support SQL databases natively in LND.

## Code Health

## Tooling and Documentation
Expand All @@ -68,6 +72,7 @@

# Contributors (Alphabetical Order)

* Andras Banki-Horvath
* CharlieZKSmith
* Pins
* Ziggie
6 changes: 5 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ require (
github.com/opencontainers/image-spec v1.0.2 // indirect
github.com/opencontainers/runc v1.1.12 // indirect
github.com/ory/dockertest/v3 v3.10.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/pmezard/go-difflib v1.0.0
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.26.0 // indirect
github.com/prometheus/procfs v0.6.0 // indirect
Expand Down Expand Up @@ -205,6 +205,10 @@ replace github.com/gogo/protobuf => github.com/gogo/protobuf v1.3.2
// allows us to specify that as an option.
replace google.golang.org/protobuf => github.com/lightninglabs/protobuf-go-hex-display v1.30.0-hex-display

// Temporary replace until https://github.com/lightningnetwork/lnd/pull/8831 is
// merged.
replace github.com/lightningnetwork/lnd/sqldb => ./sqldb
Comment on lines +208 to +210
Copy link
Collaborator

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.


// If you change this please also update .github/pull_request_template.md,
// docs/INSTALL.md and GO_IMAGE in lnrpc/gen_protos_docker.sh.
go 1.22.6
Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -461,8 +461,6 @@ github.com/lightningnetwork/lnd/kvdb v1.4.10 h1:vK89IVv1oVH9ubQWU+EmoCQFeVRaC8kf
github.com/lightningnetwork/lnd/kvdb v1.4.10/go.mod h1:J2diNABOoII9UrMnxXS5w7vZwP7CA1CStrl8MnIrb3A=
github.com/lightningnetwork/lnd/queue v1.1.1 h1:99ovBlpM9B0FRCGYJo6RSFDlt8/vOkQQZznVb18iNMI=
github.com/lightningnetwork/lnd/queue v1.1.1/go.mod h1:7A6nC1Qrm32FHuhx/mi1cieAiBZo5O6l8IBIoQxvkz4=
github.com/lightningnetwork/lnd/sqldb v1.0.4 h1:9cMwPxcrLQG8UmyZO4q8SpR7NmxSwBMbj3AispdcwHg=
github.com/lightningnetwork/lnd/sqldb v1.0.4/go.mod h1:4cQOkdymlZ1znnjuRNvMoatQGJkRneTj2CoPSPaQhWo=
github.com/lightningnetwork/lnd/ticker v1.1.1 h1:J/b6N2hibFtC7JLV77ULQp++QLtCwT6ijJlbdiZFbSM=
github.com/lightningnetwork/lnd/ticker v1.1.1/go.mod h1:waPTRAAcwtu7Ji3+3k+u/xH5GHovTsCoSVpho0KDvdA=
github.com/lightningnetwork/lnd/tlv v1.2.6 h1:icvQG2yDr6k3ZuZzfRdG3EJp6pHurcuh3R6dg0gv/Mw=
Expand Down
124 changes: 124 additions & 0 deletions invoices/kv_sql_migration_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package invoices_test

import (
"context"
"database/sql"
"testing"
"time"

"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/clock"
invpkg "github.com/lightningnetwork/lnd/invoices"
"github.com/lightningnetwork/lnd/sqldb"
"github.com/stretchr/testify/require"
)

// TestMigrationWithChannelDB tests the migration of invoices from a bolt backed
// channel.db to a SQL database. Note that this test does not attempt to be a
// complete migration test for all invoice types but rather is added as a tool
// for developers and users to debug invoice migration issues with an actual
// channel.db file.
func TestMigrationWithChannelDB(t *testing.T) {
// First create a shared Postgres instance so we don't spawn a new
// docker container for each test.
pgFixture := sqldb.NewTestPgFixture(
t, sqldb.DefaultPostgresFixtureLifetime,
)
t.Cleanup(func() {
pgFixture.TearDown(t)
})

makeSQLDB := func(t *testing.T, sqlite bool) *invpkg.SQLStore {
var db *sqldb.BaseDB
if sqlite {
db = sqldb.NewTestSqliteDB(t).BaseDB
} else {
db = sqldb.NewTestPostgresDB(t, pgFixture).BaseDB
}

executor := sqldb.NewTransactionExecutor(
db, func(tx *sql.Tx) invpkg.SQLInvoiceQueries {
return db.WithTx(tx)
},
)

testClock := clock.NewTestClock(time.Unix(1, 0))

return invpkg.NewSQLStore(executor, testClock)
}

migrationTest := func(t *testing.T, store1 *channeldb.DB, sqlite bool) {
store2 := makeSQLDB(t, sqlite)
ctxb := context.Background()

const batchSize = 11
err := invpkg.MigrateInvoicesToSQL(
ctxb, store1.Backend, store1, store2, batchSize,
Copy link
Collaborator

Choose a reason for hiding this comment

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

call store1 boltdb maybe ?

)
require.NoError(t, err)

// MigrateInvoices will check if the inserted invoice equals to
// the migrated one, but as a sanity check, we'll also fetch the
// invoices from the store and compare them to the original
// invoices.
query := invpkg.InvoiceQuery{
IndexOffset: 0,
// As a sanity check, fetch more invoices than we have
// to ensure that we did not add any extra invoices.
NumMaxInvoices: 9999,
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 know how much the db had initially would be an important information here.

}
result1, err := store1.QueryInvoices(ctxb, query)
require.NoError(t, err)
numInvoices := len(result1.Invoices)

result2, err := store2.QueryInvoices(ctxb, query)
require.NoError(t, err)
require.Equal(t, numInvoices, len(result2.Invoices))

// Simply zero out the add index so we don't fail on that when
// comparing.
for i := 0; i < numInvoices; i++ {
result1.Invoices[i].AddIndex = 0
result2.Invoices[i].AddIndex = 0

// We need to override the timezone of the invoices as
// the provided DB vs the test runners local time zone
// might be different.
invpkg.OverrideInvoiceTimeZone(&result1.Invoices[i])

require.Equal(
t, result1.Invoices[i], result2.Invoices[i],
)
}
}

tests := []struct {
name string
dbPath string
}{
{
"empty",
t.TempDir(),
},
{
"testdata",
"testdata",
},
}

for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
store, err := channeldb.Open(test.dbPath)
require.NoError(t, err)

t.Run("Postgres", func(t *testing.T) {
migrationTest(t, store, false)
})

t.Run("SQLite", func(t *testing.T) {
migrationTest(t, store, true)
})
})
}
}
Loading