Skip to content

[sql-67] multi: tombstone bbolt databases on successful SQL migration#1272

Merged
ViktorT-11 merged 3 commits into
lightninglabs:sql-migration-basefrom
ViktorT-11:2026-03-deprecate-kvdb-on-mig
May 8, 2026
Merged

[sql-67] multi: tombstone bbolt databases on successful SQL migration#1272
ViktorT-11 merged 3 commits into
lightninglabs:sql-migration-basefrom
ViktorT-11:2026-03-deprecate-kvdb-on-mig

Conversation

@ViktorT-11
Copy link
Copy Markdown
Contributor

Based on #1266

This PR tombstones/deprecates the different bbolt db files once a litd node has successfully migrated from kvdb -> SQL.

The files are intentionally kept with the same name, but are updated by adding an extra bucket in the different files, so that startup of litd will fail if the user decides to change back their databasebackend to bbolt after the migration.

The motivation for this is to ensure that the user only has one source of data, and so that the user can't continue to operate with a bbolt database and add new data to it after the migration, as that data cannot be migrated over to the users new SQL database if we did allow the user to continue to run with the bbolt backend.

The tombstone is intentionally designed so that the accounts.db will cause a reading failure if the user decides to downgrade their litd node to a version prior to the tombstone logic existing. If the user runs a litd version which includes the tombstone logic, the startup will fail when reading the session.db & rules.db files as well, in the scenario that for example the user deletes their accounts.db file, but keeps their tombstoned session.db & rules.db files.

Additionally, the logic is designed so that in case the user deletes their SQL database file after migration, they can still re-run a new kvdb -> SQL migration with their old tombstoned bbolt database files.

@ViktorT-11 ViktorT-11 added the no-changelog This PR is does not require a release notes entry label Apr 1, 2026
@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello, 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 introduces a safety mechanism to ensure that legacy bbolt database files are properly deprecated after a successful migration to a SQL backend. By adding a tombstone marker to these files, the system prevents users from inadvertently continuing to use the old bbolt backend, which could lead to data inconsistency. The changes also ensure that if a user removes their SQL database, they can still re-run the migration process using the existing tombstoned files, maintaining flexibility while enforcing data integrity.

Highlights

  • KVDB Tombstoning: Implemented a mechanism to mark legacy bbolt database files (accounts, sessions, and firewall rules) as deprecated after a successful migration to SQL, preventing accidental usage of the old backend.
  • Migration Safety: Added logic to allow re-running migrations if the SQL database is deleted or downgraded, while still blocking normal startup if the tombstone is present.
  • Refactoring: Renamed migration-related packages from 'migstreams' to 'migsets' and updated related function names to reflect the transition to migration sets.
  • Testing: Expanded integration tests to verify that deprecated files correctly block startup and that migrations can be re-run when necessary.

🧠 New Feature in Public Preview: You can now enable Memory 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 Assist

The 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 /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize 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 .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. 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

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown

@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 deprecation mechanism for legacy bbolt databases after SQL migration, ensuring that stale files are tombstoned and blocked from normal startup. It also renames migration structures from 'streams' to 'sets' and updates integration tests to verify the new deprecation flow. Review feedback identifies opportunities to optimize startup by avoiding redundant database opens and suggests adding file existence checks to prevent creating unnecessary empty files during deprecation.

Comment thread accounts/kvdb_deprecation.go Outdated
Comment thread accounts/store_kvdb.go
Comment thread firewalldb/kvdb_deprecation.go Outdated
Comment thread firewalldb/kvdb_store.go Outdated
Comment thread session/kvdb_deprecation.go Outdated
Comment thread session/kvdb_store.go Outdated
@lightninglabs-deploy
Copy link
Copy Markdown

@ellemouton: review reminder
@bitromortac: review reminder
@ViktorT-11, remember to re-request review from reviewers when ready

Copy link
Copy Markdown
Contributor

@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.

Looks great conceptually 🙏, only had some minor comments.

Comment thread accounts/kvdb_deprecation.go Outdated
Comment thread db/migsets/post_migration_callbacks_dev.go Outdated
Comment thread accounts/kvdb_deprecation_test.go Outdated
Comment thread itest/litd_migration_test.go Outdated
Comment thread itest/litd_migration_test.go
@ViktorT-11 ViktorT-11 force-pushed the 2026-03-deprecate-kvdb-on-mig branch from ca6685c to bfb0dd8 Compare April 30, 2026 14:15
@ViktorT-11 ViktorT-11 requested a review from bitromortac May 4, 2026 08:46
Copy link
Copy Markdown
Contributor

@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.

LGTM 🎉 (also saw that the backward compatibility test ran).

// committed so a failed SQL migration cannot strand the user with an unusable
// kvdb backend.
func deprecateKVDBStores(dbDir string) error {
err := accounts.DeprecateKVDB(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We could use errors.Join in the end to try all deprecations?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

agreed. atm it could land in a state where first one succeeds, second fails and then third isnt attempted.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the suggestion, addressed!

Comment thread itest/litd_migration_test.go Outdated
Comment thread db/migsets/post_migration_callbacks_dev.go
@ViktorT-11
Copy link
Copy Markdown
Contributor Author

Thanks for the review @bitromortac! I'll address your latest feedback after the next review round (from @ellemouton) 🎉

Copy link
Copy Markdown
Member

@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.

exciting!

left some comments

Comment thread itest/litd_node.go
Comment on lines 793 to +800

err := hn.cmd.Wait()
if err != nil {
litdError <- fmt.Errorf("%v\n%v\n", err, errb.String())
}

// Signal any onlookers that this process has exited.
close(hn.processExit)

// Make sure log file is closed and renamed if necessary.
finalizeLogfile()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

the code should document why the error check is so much later on else it looks like a bug & also at a glance just looks like an unchecked error

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Updated with a doc, also buffered the channel.

Comment thread session/kvdb_store.go Outdated
// committed so a failed SQL migration cannot strand the user with an unusable
// kvdb backend.
func deprecateKVDBStores(dbDir string) error {
err := accounts.DeprecateKVDB(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

agreed. atm it could land in a state where first one succeeds, second fails and then third isnt attempted.

Comment thread accounts/kvdb_deprecation.go Outdated
@@ -0,0 +1,100 @@
package accounts
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

i dont think it is clear why the tomb-stoning is done differently for accounts vs session/firewall packages. i think this could be made clearer (if there is a reason).

Ideally there is one Deprecation helper/set of helpers and all three packages just call that (can just pass in a top-level bucket?). or is the idea that each file's keys are kept in that package? if so , can let the helper take in a "tombstone key prefix" perhaps.

See how LND does tombstoneing (invoices package for example).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good idea! Updated to add a new tombstone package, which is used across the packages instead.

Also updated to use the same approach lnd uses for tombstoneing

Comment thread accounts/kvdb_deprecation.go Outdated
Comment thread session/kvdb_deprecation.go Outdated
Comment thread session/kvdb_store.go Outdated
Comment thread itest/litd_migration_test.go
Comment thread itest/litd_node.go Outdated
// still be opened unexpectedly.
err = deprecateKVDBStores(filepath.Dir(macPath))
if err != nil {
log.Errorf("CRITICAL: kvdb -> SQL migration "+
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

is there a way for us to retry later on? how does lnd do it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I did not update this.

IMO, the way you'd need to implement it is that you'd need to add a flag in the sql db that signals if the kvdb has been tombstoned or not, which is initialized as false. We'd then retry the tombstoneing until the flag is set to true, which we'd do after a successful tombstoneing, or if the kdvb files do not exist.

That would requiring adding a separate table for this in the sql db, which I think might been seen as overengineering for an issue that potentially never occur for any users, and if it does, you could argue that the outcome not that bad.

If you think it makes sense to add that tracking table in sql, I'm open to doing so though, unless you have a better idea of how to solve it!

The current implementation in lnd is even less complete than the above:

// Set the invoice bucket tombstone to indicate
// that the migration has been completed.
//
// TODO(ziggie): The tombstone is currently
// set inside the SQL transaction callback,
// which is fragile: if the SQL transaction
// is retried (e.g. on a serialization
// error), the KV tombstone is written before
// the SQL commit is confirmed. Move this to
// run after ApplyAllMigrations returns so
// the tombstone is only set once the
// migration is durably committed.
d.logger.Debugf("Setting invoice bucket " +
"tombstone")

//nolint:ll
return dbs.ChanStateDB.SetInvoiceBucketTombstone()

@ViktorT-11 ViktorT-11 force-pushed the 2026-03-deprecate-kvdb-on-mig branch from bfb0dd8 to 7a6710a Compare May 7, 2026 13:25
@ViktorT-11
Copy link
Copy Markdown
Contributor Author

Thank you so much for the detailed review @ellemouton! Addressed your feedback 🔥

I'll fix the linter issue & #1272 (comment) on the next review round.

@ViktorT-11 ViktorT-11 requested a review from ellemouton May 7, 2026 13:39
Copy link
Copy Markdown
Member

@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.

lgtm!

ViktorT-11 added 3 commits May 8, 2026 12:22
Mark the legacy kvdb stores as deprecated once the kvdb -> SQL
migration commits successfully. This prevents normal bbolt startup
from reopening accounts.db, session.db, or rules.db after their data
has already been migrated.

Add explicit deprecation checks to the three kvdb store open paths and
provide migration-only constructors that can still reopen deprecated
files when the SQL database is deleted or downgraded and the migration
must be rerun.

Use store-specific tombstones for the deprecation markers and add
tests that verify deprecated stores are rejected while migration
reruns continue to work.
Close the process exit signal before forwarding litd startup errors from
the wait goroutine. This lets the harness observe that the process has
already exited even when no receiver is currently ready on the error
channel.

Use a non-blocking send for the captured process error so failed startup
paths do not hang the goroutine while holding back log finalization or
process exit handling.

This will be needed for the upcoming commit which adds itest coverage
of deprecated kvdb databases.
Expand the kvdb -> SQL migration itest to cover the full post-migration
startup behavior, not just the initial data copy and SQL assertions.

Verify that bbolt startup is blocked once kvdb files are deprecated,
that deleting the SQL database reruns the migration successfully, and
that older litd binaries still fail to start against deprecated kvdb
files.

Also add ordered blocker checks for the deprecated kvdb files so the
test shows startup fails first on accounts.db, then session.db, and
finally rules.db as earlier files are removed.
@ViktorT-11 ViktorT-11 force-pushed the 2026-03-deprecate-kvdb-on-mig branch from 7a6710a to d519953 Compare May 8, 2026 10:22
@ViktorT-11
Copy link
Copy Markdown
Contributor Author

Thanks for the reviews 🎉!

@ViktorT-11 ViktorT-11 merged commit 3182d8d into lightninglabs:sql-migration-base May 8, 2026
23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-changelog This PR is does not require a release notes entry

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants