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

jsonrpc: introduce coin_index_2 with coins sorted by balance #19822

Merged
merged 8 commits into from
Oct 16, 2024

Conversation

bmwill
Copy link
Contributor

@bmwill bmwill commented Oct 11, 2024

Description

Introduce a new coin_index_2 index with coins sorted by balance in decreasing value (sorted high to low).

Test plan

How did you test the new or updated feature?


Release notes

Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required.

For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates.

  • Protocol:
  • Nodes (Validators and Full nodes):
  • Indexer:
  • JSON-RPC:

Introduces a new coin index used in returning paginated coins for the sui_getCoins JSON-RPC method. This changes the pagination order of coins to be returned in order of decreasing value (coins with a larger balance are returned first). In order to accomplish this a new column family (coin_index_2) was added to the jsonrpc indexes rocksdb database, deprecating and obsolescing the old coin_index column family.

Note

On startup a Fullnode will need to initialize this new index by iterating through, and processing the live
object set. This will result in a small period of downtime on node startup.

Once the new index has been initialized the old one will be removed in order to reclaim disk space.

  • GraphQL:
  • CLI:
  • Rust SDK:
  • REST API:

Copy link

vercel bot commented Oct 11, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
sui-docs ✅ Ready (Inspect) Visit Preview 💬 Add feedback Oct 15, 2024 7:13pm
3 Skipped Deployments
Name Status Preview Comments Updated (UTC)
multisig-toolkit ⬜️ Ignored (Inspect) Visit Preview Oct 15, 2024 7:13pm
sui-kiosk ⬜️ Ignored (Inspect) Visit Preview Oct 15, 2024 7:13pm
sui-typescript-docs ⬜️ Ignored (Inspect) Visit Preview Oct 15, 2024 7:13pm

fn make_live_object_indexer(&self) -> Self::ObjectIndexer;
}

/// Represents an instance of a indexer that operates on a subset of the live object set
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
/// Represents an instance of a indexer that operates on a subset of the live object set
/// Represents an instance of an indexer that operates on a subset of the live object set

});

// 1. Remove old coins from the DB by looking at the set of input coin objects
let coin_delete_keys = input_coins
Copy link
Contributor

Choose a reason for hiding this comment

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

@bmwill is there a motivation to change this piece (i.e. not using object_index_changes but using input_coins instead)? It's not immediately clear to me whether this change is correct or not, and we'd better not touch this piece unless absolutely needed

same for using written_coins below

Copy link
Contributor Author

@bmwill bmwill Oct 12, 2024

Choose a reason for hiding this comment

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

Yes, the way it is written now is actually going to be more correct and less error prone and imo easier to reason about.

Copy link
Contributor

Choose a reason for hiding this comment

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

I can't tell whether it's more correct or not with my current cooked weekend brain. Did you find errors in the old implementation which makes the new way "more correct"? If not, i'd vote for not touching this very brittle thing. If yes or you feel very confident about the correctness then i won't insist either.

either way i think @sadhansood may want to take a look at this piece as well

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Taking another look this seems like it can be simplified even more, and i should cleanup the comments on the written coins side

Copy link
Contributor

Choose a reason for hiding this comment

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

Looking at the new logic, it seems reasonable. Although it is hard to be 100% sure and my hope is our tests will catch any bugs here. One way we could be absolutely sure is to compare end of epoch account balance of every account between old and new indexing logic on two FNs which are bootstrapped from db snapshots of the same epoch (obviously disconnect network or change sui config to prevent them from processing new checkpoints). This is subjective on how much confidence we have on our tests

Comment on lines +97 to +105
Some(c) => {
let obj = self.internal.get_object(&c).await?.ok_or_else(|| {
SuiRpcInputError::GenericInvalid("cursor not found".to_string())
})?;
let coin = obj.as_coin_maybe().ok_or_else(|| {
SuiRpcInputError::GenericInvalid("cursor is not a coin".to_string())
})?;
(coin_type_tag.to_string(), !coin.balance.value(), c)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

this has the risk of getting inconsistent results across pages, but honestly this is not the only api with this issue and i don't have a better idea how to deal with it

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah :(

I had two options:

  1. break the cursor to now take the balance
  2. do this

This seemed the least bad option.

It does highlight that cursors like this should be less defined so that they can change if need be in the future

Copy link
Contributor

Choose a reason for hiding this comment

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

Remark: Rocksdb actually has a way to support consistent cursor here which is called snapshot (not to be confused with db checkpoint). It's a low overhead point in time view of the db (and millions of such snapshots could be simultaneously kept) which could be used to retrieve consistent data across pages (obviously we cannot hold a snapshot forever and it would need to be timed out)

crates/sui-core/src/jsonrpc_index.rs Show resolved Hide resolved
crates/sui-core/src/jsonrpc_index.rs Outdated Show resolved Hide resolved
crates/sui-core/src/jsonrpc_index.rs Show resolved Hide resolved
});

// 1. Remove old coins from the DB by looking at the set of input coin objects
let coin_delete_keys = input_coins
Copy link
Contributor

Choose a reason for hiding this comment

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

Looking at the new logic, it seems reasonable. Although it is hard to be 100% sure and my hope is our tests will catch any bugs here. One way we could be absolutely sure is to compare end of epoch account balance of every account between old and new indexing logic on two FNs which are bootstrapped from db snapshots of the same epoch (obviously disconnect network or change sui config to prevent them from processing new checkpoints). This is subjective on how much confidence we have on our tests

crates/sui-core/src/rest_index.rs Show resolved Hide resolved
Comment on lines +97 to +105
Some(c) => {
let obj = self.internal.get_object(&c).await?.ok_or_else(|| {
SuiRpcInputError::GenericInvalid("cursor not found".to_string())
})?;
let coin = obj.as_coin_maybe().ok_or_else(|| {
SuiRpcInputError::GenericInvalid("cursor is not a coin".to_string())
})?;
(coin_type_tag.to_string(), !coin.balance.value(), c)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Remark: Rocksdb actually has a way to support consistent cursor here which is called snapshot (not to be confused with db checkpoint). It's a low overhead point in time view of the db (and millions of such snapshots could be simultaneously kept) which could be used to retrieve consistent data across pages (obviously we cannot hold a snapshot forever and it would need to be timed out)

Copy link
Contributor

@sadhansood sadhansood left a comment

Choose a reason for hiding this comment

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

code lgtm, and hopefully with some a/b testing of fetching coins/balance we can see if this works as expected

crates/sui-core/src/jsonrpc_index.rs Outdated Show resolved Hide resolved
Factor out the parallel live object indexing, used for initializing the
rest indexes, into more general purpose and reusable logic.
Introduce a new coin_index_2 index with coins sorted by balance in
decreasing value (sorted high to low).
Initialize the new coin_index_2 index and clear the old coin_index
column family.
Reduce the minimum batch size to write out during indexing the live
object set from 256MB to 128MB, these smaller batch sizes resulted in a
small improvement to index initialization.
…tion

Introduce a meta column family that is used to track initialization of
the Index DB itself as well as the status of the various column families
themsevles.
@bmwill bmwill force-pushed the jsonrpc-coin-index branch from e543142 to a487e8c Compare October 15, 2024 19:12
@bmwill bmwill merged commit 71f9203 into MystenLabs:main Oct 16, 2024
46 checks passed
@bmwill bmwill deleted the jsonrpc-coin-index branch October 16, 2024 01:31
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.

3 participants