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

SQLite migration code #10551

Merged
merged 19 commits into from May 3, 2023

Conversation

kiminuo
Copy link
Collaborator

@kiminuo kiminuo commented Apr 24, 2023

Follow-up to #10272
The important commit is 85f1e4a.

fixes #10589

Takes MatureIndex.dat and converts it to IndexStore.sqlite. Unfortunately, the process is not very fast. It takes 43 seconds on my machine for testnet. So this PR would benefit from giving a user a visual notification that the migration is in progress. WDYT? We agreed that we should not add any UX.

Note: This PR does not remove the original MatureIndex.dat as agreed in a previous meeting. That is a next logical step to avoid issues for developers.

Testing

Remove C:\Users\<USER>\AppData\Roaming\WalletWasabi\Client\BitcoinStore\<NETWORK>\IndexStore\IndexStore.sqlite and IndexStore.sqlite should be created during app's launch.

Measurements

On my machine:

  • For main network: It takes about 21 seconds.
  • For testnet network: It takes about 8 seconds.

…ith-sqlite' into feature/2023-04-24-Sqlite-migration

# Conflicts:
#	WalletWasabi/Stores/IndexStore.cs
…ith-sqlite' into feature/2023-04-24-Sqlite-migration
@molnard
Copy link
Collaborator

molnard commented Apr 25, 2023

Sorry @kiminuo I misunderstood something in yesterday's meetings. I thought it takes 10 minutes to migrate but I see it is less than 1 minute. That is acceptable UX. Where will the user wait for the migration?

@molnard
Copy link
Collaborator

molnard commented Apr 25, 2023

giving a user a visual notification that the migration is in progress. WDYT?

Do not go down that road, it is long and complicated and not worth it for just only 1 minute (or less). It is enough if wallet loading will take longer.

MaxHillebrand
MaxHillebrand previously approved these changes Apr 25, 2023
Copy link
Member

@MaxHillebrand MaxHillebrand left a comment

Choose a reason for hiding this comment

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

tACK 2395e63

On the first run:

INFO	IndexStore.MigrateToSqliteNoLock (124)	Filters to add is 1601354. Elapsed seconds 6.
INFO	IndexStore.MigrateToSqliteNoLock (127)	Migration to SQLite was finished in 13 seconds.
INFO	IndexStore.Dispose (77)	InitializeAsync finished in 13.12 seconds.

@kiminuo
Copy link
Collaborator Author

kiminuo commented Apr 25, 2023

Where will the user wait for the migration?

It happens on the initial page which is frozen at the moment. You can actually try it. I feel like the UX is not great at the moment but maybe it's acceptable given it's one time thing. Maybe we should just mention it in the next release notes so that people are not suprised.

Using https://www.meziantou.net/split-a-string-into-lines-without-allocation.htm would shorten the time required for the operation by about half, I suspect, because we allocate many and many strings by reading the MatureIndex.dat file. But I have not tried it because it starts to feel a bit over the top.

At the moment, I don't have another good idea how to make it faster without non-trivial time investment. Maybe someone else has an idea how to make it faster?

@kiminuo
Copy link
Collaborator Author

kiminuo commented Apr 25, 2023

@MaxHillebrand It should be slightly faster now.

MaxHillebrand
MaxHillebrand previously approved these changes Apr 26, 2023
Copy link
Member

@MaxHillebrand MaxHillebrand left a comment

Choose a reason for hiding this comment

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

TACK c586c42

30 seconds now.

I see the log before the UI opens, so loading time is seriously not an issue.

@kiminuo kiminuo marked this pull request as ready for review April 26, 2023 07:33
@@ -44,6 +44,9 @@ public class BitcoinStore

public async Task InitializeAsync(CancellationToken cancel = default)
{
// Switch to a different thread so that we can confinue with other initialization tasks and UI can show up.
await Task.Yield();
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 entirely necessary but without it, I can see white screen instead of the initial Avalonia page.

{
Header = header;
_filter = filter;
FilterData = filterData;
_filter = new(() => new GolombRiceFilter(filterData, 20, 1 << 20), LazyThreadSafetyMode.ExecutionAndPublication);
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Accessing filter bytes through GolombRiceFilter is slow. It's better to touch GolombRiceFilter only when really necessary.

Comment on lines +127 to +131
if (i % 100_000 == 0)
{
IndexStorage.BulkAppend(filters);
filters.Clear();
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It seems it's slightly faster when "chunked". Not by much.

@molnard
Copy link
Collaborator

molnard commented Apr 26, 2023

The period of the migration is OK. Regarding the UX - can this migration take place exactly the same way as it was before with processing the filters? There was no UI freeze when the filters were not ready.

@adamPetho
Copy link
Collaborator

adamPetho commented Apr 26, 2023

TestNet ⚡

2023-04-26 17:29:40.380 [1] WARNING     IndexStore.MigrateToSqliteNoLock (99)   Migration of block filters to SQLite format is about to begin. Please wait a moment.
2023-04-26 17:29:45.121 [1] INFO        IndexStore.MigrateToSqliteNoLock (137)  Migration of 1602357 filters to SQLite was finished in 00:00:04.7387223 seconds.

MainNet

2023-04-26 17:50:16.475 [1] WARNING     IndexStore.MigrateToSqliteNoLock (99)   Migration of block filters to SQLite format is about to begin. Please wait a moment.
2023-04-26 17:50:31.992 [1] INFO        IndexStore.MigrateToSqliteNoLock (137)  Migration of 305180 filters to SQLite was finished in 00:00:15.5153843 seconds.

My only nitpick is that the UI is totally frozen while the migration is happening.

@molnard
Copy link
Collaborator

molnard commented Apr 26, 2023

Make sure when testing this, that wallet from the latest release will be loaded as fast as with this PR. There should not be a noticeable change.

@kiminuo
Copy link
Collaborator Author

kiminuo commented Apr 26, 2023

I have attempted to modify the implementation to avoid the frozen UI issue. However, now it's a bit more involved and a bit more risky. So it requires more careful review.

It's also a bit hackish. The idea is that when we get first filters batch, then we just run migration and fix SmartHeaderChain. It's not optimal. It's not very nice. However, it does not freeze the UI.

If anyone has a better idea how to approach this, I'm all ears.

WalletWasabi/Stores/IndexStore.cs Outdated Show resolved Hide resolved
Copy link
Collaborator

@molnard molnard left a comment

Choose a reason for hiding this comment

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

NACK for the current solution as you said is a hack - from the worst kind. I am sure you can find it better, please elaborate more.

WalletWasabi/Stores/IndexStore.cs Show resolved Hide resolved
WalletWasabi/Stores/IndexStore.cs Show resolved Hide resolved
@kiminuo
Copy link
Collaborator Author

kiminuo commented Apr 29, 2023

NACK for the current solution as you said is a hack - from the worst kind. I am sure you can find it better, please elaborate more.

I'm not sure what constitutes the worst kind of hack. What are the properties you don't like?

Thinking about it a bit more, I believe we can put the code here

Logger.LogTrace("> Wasabi synchronizer thread starts.");
. That seems like that might behave slightly better (run the migration a bit sooner). But it will still be hacky. The thing is that one has to argue real well that the migration won't break anything and that's not entirely easy given that SmartHeaderChain is used on many and many places. So I believe the original approach was actually better (maybe try to make it even faster so that it does not mind too much that GUI is not responsive?).

@molnard
Copy link
Collaborator

molnard commented May 1, 2023

I'm not sure what constitutes the worst kind of hack

The AddNewFiltersAsync method can start the migration in case.

@molnard
Copy link
Collaborator

molnard commented May 1, 2023

I am not sure I understand. I put this 10 sec delay here and I did not get a UI freeze.

image

…Sqlite-migration

# Conflicts:
#	WalletWasabi/Stores/IndexStore.cs
This reverts commit d1fa430.

# Conflicts:
#	WalletWasabi/Stores/IndexStore.cs
@kiminuo
Copy link
Collaborator Author

kiminuo commented May 1, 2023

I am not sure I understand. I put this 10 sec delay here and I did not get a UI freeze.

I reverted to my original approach and added your X second delay trick (I'm not entirely sure why it works though).

@MaxHillebrand
Copy link
Member

What's needed to push this over the finish line?
(other than me cheering you on)

@kiminuo
Copy link
Collaborator Author

kiminuo commented May 2, 2023

David's ACK/NACK on the approach. Review&testing by others will help too.

Copy link
Collaborator

@molnard molnard left a comment

Choose a reason for hiding this comment

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

tested it works perfectly. One concern to address.

int i = 0;

// Helps to avoid UI freezing.
await Task.Delay(5_000, cancellationToken).ConfigureAwait(false);
Copy link
Collaborator

@molnard molnard May 2, 2023

Choose a reason for hiding this comment

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

This help only because it is giving back the execution to the caller thread. When it returns here it might be running on a separate thread after (.ConfigureAwait(false)) , might not - it is not guaranteed. If ThreadPoll will assign the task to the UI thread (random) it will still freeze the UI.

Because you are not having an async method for MigrateToSqliteNoLockAsync (perf reasons cACK) I suggest removing the Delay and changing it back to the sync method.

Then call it with

await Task.Run(MigrateToSqliteNoLockAsync);

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

There is already using (await IndexLock.LockAsync(cancellationToken).ConfigureAwait(false)), so I added:

await Task.Run(MigrateToSqliteNoLock, cancellationToken).ConfigureAwait(false);

Or is there any reason why it should be done differently?

Copy link
Collaborator

@MarnixCroes MarnixCroes left a comment

Choose a reason for hiding this comment

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

FWIW (maybe edge case, maybe useful):
My client was busy doing full rescan/filter download.
I stopped the process halfway, then checked out and ran this PR.
The log immediately says it downloaded the latest block filter (so no more filter download), however, the wallet load screen is still counting down where it left off: many hours remaining. While in logs nothing is changing.

@turbolay
Copy link
Collaborator

turbolay commented May 2, 2023

FWIW (maybe edge case, maybe useful):
My client was busy doing full rescan/filter download.
I stopped the process halfway, then checked out and ran this PR.
The log immediately says it downloaded the latest block filter (so no more filter download), however, the wallet load screen is still counting down where it left off: many hours remaining. While in logs nothing is changing.

This is expected, see second part of #10589 (comment)

Copy link
Collaborator

@molnard molnard left a comment

Choose a reason for hiding this comment

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

tACK. 🥇

@molnard molnard merged commit 9f2b1c4 into zkSNACKs:master May 3, 2023
6 of 7 checks passed
@molnard
Copy link
Collaborator

molnard commented May 3, 2023

The next step is to delete the old files. Two weeks from now that PR can be merged. 17th of May. Added to the calendar.

@kiminuo kiminuo deleted the feature/2023-04-24-Sqlite-migration branch May 3, 2023 12:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Something The past few days Triggers Rescan of Wallets
8 participants