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

Optimize balance-related queries with a cache #2383

Open
wants to merge 94 commits into
base: master
Choose a base branch
from

Conversation

rafal-ch
Copy link
Contributor

@rafal-ch rafal-ch commented Oct 22, 2024

Linked Issues/PRs

Closes #1965

Description

  1. The client uses the off-chain database metadata to tell if it can use the optimized queries or fall-back to the legacy ones.
  2. When client starts the metadata is either:
    1. Kept intact in-case genesis exists (meaning - no optimized queries are available)
      • this stems from the fact that the pre-existing DB contains some coins already and we'll not be re-indexing them in order to create the lookup indexes
      • in this mode of operation the "old" way of getting balances is used as confirmed in logs:
        • worker_service: 607: Balances cache available: false
        • query::balance: 100: Querying balances without balances cache owner=53a9c6a...
    2. Initialized with V2 if genesis is missing
      • this means that while re-syncing the DB the balance indexes are going to be created and could be used to satisfy the requests
        • worker_service: 607: Balances cache available: true
        • query::balance: 151: Querying balances using balances cache owner=53a9c6a...

Technical considerations:

  • Metadata is extended with V2 which now contains indexation_availability. This is a set that holds the available indexations (currently only Balances)
  • New databases are added: CoinBalances and MessageBalances that keep the coin and message balances respectively
  • In case balances indexation is available (see "Description" above):
    • these databases are updated in the GrapQL API worker service when processing one of these events: MessageImported, MessageConsumed, CoinCreated, CoinConsumed
    • balance() and balances() queries use these databases to satisfy the query

Checklist

  • New behavior is reflected in tests

Before requesting review

  • I have reviewed the code myself

match event {
Event::MessageImported(message) => message
.update_balances(block_st_transaction, |balance: Cow<u64>, amount| {
balance.saturating_add(amount)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think I'm gonna change these operations to "checked". My understanding is that we'll not be producing events that could lead to overflow, but if something unexpected happen it's still worth to see an explicit error instead of the balance being calculated incorrectly.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Good catch. Hmm, it can happen if anyone decide to create their own token with huge balances. I think the Amount should be u128.

Copy link
Contributor Author

@rafal-ch rafal-ch Oct 31, 2024

Choose a reason for hiding this comment

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

After I implemented this check, some of the integration tests started to fail, because they used some arbitrary transactions with arbitrary amounts and wallets.

I'll be fixing them to use the wallets available in the test environment. One such test is fixed here. @MitchTurner, can you have a look at this and see if this fix makes sense? If so, I'll apply this to other tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It turned out that the required changes to tests are too far reaching. I reverted the commits with modified test (474e207 de11071 895d9da) and added a note here (9848f64).

With balances type changed to u128 we are a bit more safe, but still this is an issue worth discussing.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I changed the asset balance to be u128, leaving the contract balance as is.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Also, the amount of each asset is still u64, only the total balance has been promoted to u128. If we want to also upgrade the amount I'll do it in a follow-up PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Follow-up issue created: #2428

@@ -110,7 +110,7 @@ impl ImportTable for Handler<OwnedMessageIds, Messages> {
let events = group
.into_iter()
.map(|TableEntry { value, .. }| Cow::Owned(Event::MessageImported(value)));
worker_service::process_executor_events(events, tx)?;
worker_service::process_executor_events(events, tx, true)?;
Copy link
Collaborator

Choose a reason for hiding this comment

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

[nit]: Would be nice to use some named variable or have a comment describing why it is true(the same for another true below)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated in 0b39319

&DatabaseMetadata::V2 {
version: <OffChain as DatabaseDescription>::version(),
height: Default::default(),
indexation_availability: [(IndexationKind::Balances)]
Copy link
Collaborator

Choose a reason for hiding this comment

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

It would be nice to have a way to force us(via compiler) to handle new variant of the IndexationKind enum here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated here: 8f3e817

Comment on lines +117 to +120
/// Coin balances per user and asset.
CoinBalances = 23,
/// Message balances per user.
MessageBalances = 24,
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think having 2 tables for balance it is too much=D I think w can have just one table with some:

enum Balance {
    V1 {
        coins: Amount,
        messages: Amount,
    }
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The suggestion would require the "asset id" to be a part of the key, even if it is technically not needed for messages. That's why I decided to structure it in two separate columns.

Also, current solution would allow us to add more types of "amounts" in the future, without requiring V2.

match event {
Event::MessageImported(message) => message
.update_balances(block_st_transaction, |balance: Cow<u64>, amount| {
balance.saturating_add(amount)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Good catch. Hmm, it can happen if anyone decide to create their own token with huge balances. I think the Amount should be u128.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
no changelog Skip the CI check of the changelog modification
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants