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

Force rollback of tables with foreign keys / cascading deletes on automatic db migrations #1290

Merged
merged 2 commits into from
Jan 21, 2020

Conversation

KtorZ
Copy link
Member

@KtorZ KtorZ commented Jan 20, 2020

Issue Number

#1279

Overview

  • 74c232e
    force rollback to genesis after automated migrations
    persistent clear foreign tables on automatic migration due to
    cascading delete. One possible approach to solve this: forcing a
    manual rollback to genesis whenever there are migrations applied
    to the database, regardless of the migration. Only the foreign
    tables are affected, so static metadata and wallet credentials
    are preserved. This mean that we would force resync of wallets
    on every updates that have database changes, but that's a fair
    price to pay for now.

  • de3825d
    remove foreign keys to 'Wallet' table and cascade deletes manually
    This to prevent 'persistent' from doing it during automated migration! We don't do
    this for checkpoints because the cascading updates are truly helpful there to manage
    the database state and, can be more easily rolled back at any time (since the data
    regarding checkpoints entirely comes from the chain).

Comments

⚠️ Currently manually testing db migration from various previous versions.

Automated migration passes! And on old version, we have a nice log showing what's going on, including manual migration, and handling of automated ones!

 *** Migrating from v2019-12-13 ***

[cardano-wallet.application:Info:22] [2020-01-20 17:58:12.47 UTC] Found existing wallet: 99ef09bdab5f62579f2d6db02ba7df5e88b7e865
[cardano-wallet.wallet-db:Notice:28] [2020-01-20 17:58:12.47 UTC] checkpoint table does not contain required field 'active_slot_coeff'. Adding this field with a default value of 1.0.
[cardano-wallet.wallet-db:Notice:28] [2020-01-20 17:58:12.52 UTC] 48 migrations were applied to the database.
[cardano-wallet.wallet-db:Notice:28] [2020-01-20 17:58:12.52 UTC] Some automated database migrations happened. Forced to rollback to genesis.
[cardano-wallet.main:Info:22] [2020-01-20 17:58:12.53 UTC] Wallet backend server listening on 127.0.0.1:8090
[cardano-wallet.wallet-engine:Info:28] [2020-01-20 17:58:12.53 UTC] 99ef09bd: Applying blocks [1167051.7 ... 1167051.7]
[cardano-wallet.wallet-engine:Info:28] [2020-01-20 17:58:12.53 UTC] 99ef09bd: Creating checkpoint at 3b2894d8-[1167051.7#1]

So, to summarize what's happening:

  1. An existing wallet database is found.
  2. Before running any automatic migration, the database is inspected and an extra column is found. So we manually alter the table to add a column with a default value.
  3. Then, persistent runs its own set of automated migrations on top of the database, running 48 of them.
  4. This is then detected and induces immediately a rollback to genesis of all the on-chain data.
  5. The wallet starts applying blocks and creating checkpoints, starting with a checkpoint at height #1 (right after genesis)

🎉

@KtorZ KtorZ self-assigned this Jan 20, 2020
@KtorZ
Copy link
Member Author

KtorZ commented Jan 20, 2020

bors try

iohk-bors bot added a commit that referenced this pull request Jan 20, 2020
@KtorZ KtorZ force-pushed the KtorZ/force-rollback-automated-migrations branch from 7a18339 to de3825d Compare January 20, 2020 18:20
@KtorZ
Copy link
Member Author

KtorZ commented Jan 20, 2020

bors r-

@KtorZ KtorZ force-pushed the KtorZ/force-rollback-automated-migrations branch from de3825d to ce7db9a Compare January 20, 2020 18:29
@iohk-bors
Copy link
Contributor

iohk-bors bot commented Jan 20, 2020

try

Build failed

@KtorZ
Copy link
Member Author

KtorZ commented Jan 20, 2020

bors try

iohk-bors bot added a commit that referenced this pull request Jan 20, 2020
@iohk-bors
Copy link
Contributor

iohk-bors bot commented Jan 20, 2020

try

Build failed

Copy link
Member

@jonathanknowles jonathanknowles left a comment

Choose a reason for hiding this comment

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

Hi @KtorZ

I've had a chance to test your PR, and I was able to successfully start with a wallet that was created with the v2019-12-13 branch. So far so good! 🍾

I do have a couple of questions / concerns though:

  1. The trigger condition for performing a forceful rollback is that we see a nonzero migration count. However, this count currently only includes automatic migrations. Should we not also include manual migrations in this count? It's conceivable that a successful manual migration will bring the database into a state where no automatic migrations are necessary. But it might be wiser to err on the side of caution and still perform a forceful rollback in this case. (We can always remove this in future, if we're more confident, and have more testing.)

  2. The removal of automatic cascading deletes. The nice thing about having cascading deletes is that we can rely on the database implementation to ensure referential integrity (no dangling references). Even in the case where Persistent's automatic migration mechanism (erroneously) causes rows to be deleted in tables that reference the checkpoint table, we can still rely on SQLite to preserve referential integrity. However, if we remove this automatic mechanism for ensuring referential integrity, do we not increase our testing burden? Haven't we now made it easier for future code to break referential integrity?

Apart from these concerns, this PR looks good to me. I have a couple of comments regarding Haddock comments and names of types.

@@ -211,7 +213,7 @@ startSqliteBackend
-> Migration
-> Tracer IO DBLog
-> Maybe FilePath
-> IO (Either MigrationError SqliteContext)
-> IO (Either MigrationError (Quantity "migration" Int, SqliteContext))
Copy link
Member

Choose a reason for hiding this comment

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

Suggestion: use migrations (plural) or migrationCount here, to really emphasize that it is a count of the number of migrations applied.

Copy link
Member Author

Choose a reason for hiding this comment

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

I disagree here ^.^
The Symbol attached with a Quantity is used as a unit. We have several occurrences of this in the source code, like Quantity "lovelace" ... or Quantity "block" ..., always singular. Same applies here IMO.

Copy link
Contributor

Choose a reason for hiding this comment

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

yeah, Quantity "migration" is complied with convention adopted in other places

@@ -231,7 +233,8 @@ startSqliteBackend manualMigration migrateAll trace fp = do
Left e -> do
destroyDBLayer ctx
pure $ Left e
Right _ -> pure $ Right ctx
Right ms -> do
pure $ Right (Quantity (length ms), ctx)
Copy link
Member

@jonathanknowles jonathanknowles Jan 21, 2020

Choose a reason for hiding this comment

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

Question: this count (ms) appears to only include automatic migrations.

Should we not also include any manual migrations in this total count?

So: manual migrations + automatic migrations?

I'm assuming it's possible for there to be cases where there are manual migrations but no automatic migrations. (This would happen if our manual migrations successfully produce the exact schema that Sqlite is expecting.)

Copy link
Member Author

Choose a reason for hiding this comment

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

Your assumption is correct: there can be manual migrations but no automatic ones.
Still, the migration count here (and this particular fix) is solely about automatic ones. It comes as a work-around for persistent's behavior of deleting tables combined with the cascading deletion of foreign tables.
For manual migration, we are in total control of what's being done and, it is very unlikely we would make a manual migration that requires rolling back the chain. If we ever do, then it'd be probably time to revisit this whole migration logic instead of patching up things on top of each other.
The migration logic has become quite convoluted already. Manual migrations shouldn't be included because they aren't causing implicit uncontrolled behavior here.

Copy link
Member

Choose a reason for hiding this comment

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

@KtorZ wrote:

For manual migration, we are in total control of what's being done and, it is very unlikely we would make a manual migration that requires rolling back the chain. If we ever do, then it'd be probably time to revisit this whole migration logic instead of patching up things on top of each other.

Fair enough! This is a convincing reason.

lib/core/src/Cardano/DB/Sqlite.hs Outdated Show resolved Hide resolved
lib/core/src/Cardano/Wallet/DB/Sqlite.hs Outdated Show resolved Hide resolved
lib/core/src/Cardano/Wallet/DB/Sqlite.hs Outdated Show resolved Hide resolved
lib/core/src/Cardano/Wallet/DB/Sqlite.hs Outdated Show resolved Hide resolved
  `persistent` clear foreign tables on automatic migration due to
  cascading delete. One possible approach to solve this: forcing a
  manual rollback to genesis whenever there are migrations applied
  to the database, regardless of the migration. Only the foreign
  tables are affected, so static metadata and wallet credentials
  are preserved. This mean that we would force resync of wallets
  on every updates that have database changes, but that's a fair
  price to pay for now.
This to prevent 'persistent' from doing it during automated migration! We don't do
this for checkpoints because the cascading updates are truly helpful there to manage
the database state and, can be more easily rolled back at any time (since the data
regarding checkpoints entirely comes from the chain).
@KtorZ KtorZ force-pushed the KtorZ/force-rollback-automated-migrations branch from 6b70b75 to 0583144 Compare January 21, 2020 09:37
Comment on lines +371 to +372
[ "Some automated database migrations happened. Forced to roll back "
, "to genesis."
Copy link
Contributor

@paweljakubas paweljakubas Jan 21, 2020

Choose a reason for hiding this comment

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

minor suggestion

Some automated database migrations happened. Rollback to the genesis was forced.

it is somewhat more natural to me

Copy link
Member Author

Choose a reason for hiding this comment

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

Why the however 🤔 ?

Copy link
Contributor

Choose a reason for hiding this comment

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

yeah, probably without the. so no to the genesis but to genesis

let start = startSqliteBackend (ManualMigration mempty) migrateAll trace fp
(nMigrations, ctx) <- handlingPersistError trace fp start
let dbLayer = mkDBLayer ctx
when (nMigrations > Quantity 0) $ do
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

-- tables, and re-creating them from scratch. When paired with automatic cascade
-- deletion, this has dramatic consequences on the wallet data integrity (when a
-- source table is deleted due to a migration, all foreign tables are also
-- deleted, but only the rows in the source table are replaced!).
Copy link
Contributor

@paweljakubas paweljakubas Jan 21, 2020

Choose a reason for hiding this comment

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

💯 for the comment above

@@ -424,7 +424,15 @@ newDBLayer trace defaultFieldValues mDatabaseFile = do
selectWallet wid >>= \case
Nothing -> pure $ Left $ ErrNoSuchWallet wid
Just _ -> Right <$> do
deleteCascadeWhere [WalId ==. wid]
deleteWhere [WalId ==. wid]
Copy link
Contributor

@paweljakubas paweljakubas Jan 21, 2020

Choose a reason for hiding this comment

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

if so then ON DELETE CASCADE in TH.hs are to be deleted

Copy link
Contributor

Choose a reason for hiding this comment

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

and they are

@KtorZ
Copy link
Member Author

KtorZ commented Jan 21, 2020

bors r+

iohk-bors bot added a commit that referenced this pull request Jan 21, 2020
1290: Force rollback of tables with foreign keys / cascading deletes on automatic db migrations r=KtorZ a=KtorZ

# Issue Number

<!-- Put here a reference to the issue this PR relates to and which requirements it tackles -->

#1279 

# Overview

<!-- Detail in a few bullet points the work accomplished in this PR -->

- 74c232e
  force rollback to genesis after automated migrations
    `persistent` clear foreign tables on automatic migration due to
  cascading delete. One possible approach to solve this: forcing a
  manual rollback to genesis whenever there are migrations applied
  to the database, regardless of the migration. Only the foreign
  tables are affected, so static metadata and wallet credentials
  are preserved. This mean that we would force resync of wallets
  on every updates that have database changes, but that's a fair
  price to pay for now.

- de3825d
  remove foreign keys to 'Wallet' table and cascade deletes manually
  This to prevent 'persistent' from doing it during automated migration! We don't do
this for checkpoints because the cascading updates are truly helpful there to manage
the database state and, can be more easily rolled back at any time (since the data
regarding checkpoints entirely comes from the chain).


# Comments

<!-- Additional comments or screenshots to attach if any -->

~~:warning: Currently manually testing db migration from various previous versions.~~

Automated migration passes! And on old version, we have a nice log showing what's going on, including manual migration, and handling of automated ones!

```
 *** Migrating from v2019-12-13 ***

[cardano-wallet.application:Info:22] [2020-01-20 17:58:12.47 UTC] Found existing wallet: 99ef09bdab5f62579f2d6db02ba7df5e88b7e865
[cardano-wallet.wallet-db:Notice:28] [2020-01-20 17:58:12.47 UTC] checkpoint table does not contain required field 'active_slot_coeff'. Adding this field with a default value of 1.0.
[cardano-wallet.wallet-db:Notice:28] [2020-01-20 17:58:12.52 UTC] 48 migrations were applied to the database.
[cardano-wallet.wallet-db:Notice:28] [2020-01-20 17:58:12.52 UTC] Some automated database migrations happened. Forced to rollback to genesis.
[cardano-wallet.main:Info:22] [2020-01-20 17:58:12.53 UTC] Wallet backend server listening on 127.0.0.1:8090
[cardano-wallet.wallet-engine:Info:28] [2020-01-20 17:58:12.53 UTC] 99ef09bd: Applying blocks [1167051.7 ... 1167051.7]
[cardano-wallet.wallet-engine:Info:28] [2020-01-20 17:58:12.53 UTC] 99ef09bd: Creating checkpoint at 3b2894d8-[1167051.7#1]
```

So, to summarize what's happening:

1. An existing wallet database is found. 
2. Before running any automatic migration, the database is inspected and an extra column is found. So we manually alter the table to add a column with a default value.
3. Then, persistent runs its own set of automated migrations on top of the database, running 48 of them.
4. This is then detected and induces immediately a rollback to genesis of all the on-chain data.
5. The wallet starts applying blocks and creating checkpoints, starting with a checkpoint at height `#1` (right after genesis)

:tada: 


<!-- 
Don't forget to:

 ✓ Self-review your changes to make sure nothing unexpected slipped through
 ✓ Assign yourself to the PR
 ✓ Assign one or several reviewer(s)
 ✓ Once created, link this PR to its corresponding ticket
 ✓ Assign the PR to a corresponding milestone
 ✓ Acknowledge any changes required to the Wiki
-->


Co-authored-by: KtorZ <matthias.benkort@gmail.com>
@iohk-bors
Copy link
Contributor

iohk-bors bot commented Jan 21, 2020

Build succeeded

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants