feat(stages): add RocksDB support for IndexAccountHistoryStage#21165
feat(stages): add RocksDB support for IndexAccountHistoryStage#21165
Conversation
This PR adds RocksDB support for the IndexStorageHistoryStage, following the same pattern established in #21165 for IndexAccountHistoryStage. Changes: - Add RocksDBProviderFactory and StorageSettingsCache trait bounds to the Stage impl - Use EitherWriter::new_storages_history with explicit #[cfg] blocks for RocksDB batch creation - Add helper functions for loading/flushing storage history shards: - load_storage_history_indices_with_writer - iterate collector, merge with existing shards - get_last_storage_history_shard - read from RocksDB or MDBX based on settings - write_storage_history_shards_keep_last - write full shards, keep partial in memory - flush_storage_history_shards - write all remaining shards with u64::MAX for last - Only clear MDBX table on first sync if not using RocksDB - Add rocksdb_tests module with tests for: - execute_writes_to_rocksdb_when_enabled - unwind_deletes_from_rocksdb_when_enabled - execute_incremental_sync Part of #20593 - Move secondary indices to RocksDB
40a72a8 to
1dc3c78
Compare
e2d6aea to
a462de0
Compare
| // We have reached the end of this subset of keys so | ||
| // we need to flush its last indice shard. |
There was a problem hiding this comment.
dont remove this comment
| // If it's not the first sync, there might an existing shard already, so we need to | ||
| // merge it with the one coming from the collector |
There was a problem hiding this comment.
dont remove this comment
| )?; | ||
| } | ||
|
|
||
| // There will be one remaining shard that needs to be flushed to DB. |
There was a problem hiding this comment.
dont remove this comment
| CURSOR: DbCursorRW<tables::AccountsHistory> + DbCursorRO<tables::AccountsHistory>, | ||
| { | ||
| /// Puts an account history entry. | ||
| pub fn put_account_history( |
There was a problem hiding this comment.
- Renamed
put_account_historytoupsert_account_historyto make the semantics clearer (it was already callingcursor.upsertinternally). Also added a separateappend_account_historyfor the first-sync case which is more efficient when we know keys are monotonically increasing.
|
|
||
| // Compute exclusive end bound: `last_key || 0x00` is the smallest key > last_key | ||
| // under bytewise ordering (avoids 0xFF carry issues of increment-last-byte approach) | ||
| let end_key = Self::exclusive_end_after(last_key); |
There was a problem hiding this comment.
This is necessary for first-sync performance: on first sync we clear the table and rebuild from scratch (same pattern as MDBX's tx.clear::<T>()). The implementation is defensive - returns early if table is empty, and uses the safe exclusive_end_after helper to compute the range bound.
| /// Only flushes when we have more than one shard's worth of data, keeping the last | ||
| /// (possibly partial) shard for continued accumulation. This avoids writing a shard | ||
| /// that may need to be updated when more indices arrive. | ||
| fn flush_account_history_shards_partial<N, CURSOR>( |
There was a problem hiding this comment.
why do we need partial flush and full flush functions?
-
flush_account_history_shards_partial: Called during iteration when accumulating indices. Only flushes complete shards, keeping the last (possibly partial) shard in memory for continued accumulation. -
flush_account_history_shards: Called at key boundaries (when address changes) and at the end. Flushes all remaining shards, usingu64::MAXas the highest block number for the final shard (the invariant that allowsseek_exact(address, u64::MAX)to find the last shard during incremental sync).
| /// | ||
| /// Uses `Option<Address>` instead of `Address::default()` as the sentinel to avoid | ||
| /// incorrectly treating `Address::ZERO` as "no previous address". | ||
| pub(crate) fn load_account_history_via_writer<N, CURSOR>( |
There was a problem hiding this comment.
Added this function specialized for EitherWriter. The original load_history_indices is generic over the table type and works with raw cursors, while this one works specifically with EitherWriter which abstracts MDBX/RocksDB backends. The key difference is using Option<Address> as the sentinel (to handle Address::ZERO correctly) and calling EitherWriter methods directly instead of through cursor trait bounds.
|
@joshieDo just addressed your comments :) |
Co-authored-by: Georgios Konstantopoulos <me@gakonst.com> Co-authored-by: joshieDo <93316087+joshieDo@users.noreply.github.com>
This implements RocksDB support for the IndexStorageHistoryStage following the pattern established in #21165 for IndexAccountHistoryStage: - Add RocksDBProviderFactory and StorageSettingsCache trait bounds to Stage impl - Use EitherWriter::new_storages_history with explicit #[cfg] blocks for batch creation - Add helper functions for loading/flushing storage history shards: - load_storage_history_via_writer - iterate collector, merge with existing shards - flush_storage_history_shards_partial - write full shards, keep partial in memory - flush_storage_history_shards - write all remaining shards with u64::MAX for last - Add EitherWriter methods: - append_storage_history, upsert_storage_history, get_last_storage_history_shard - unwind_storage_history_shard - handles RocksDB shard unwinding - Add RocksDBProvider::clear() and RocksDBBatch::get() methods - Route unwind to RocksDB when storages_history_in_rocksdb is enabled - Add rocksdb_tests module with tests for: - execute_writes_to_rocksdb_when_enabled - unwind_deletes_from_rocksdb_when_enabled - unwind_to_zero_keeps_block_zero - execute_incremental_sync Part of #20593 - Move secondary indices to RocksDB
Add RocksDB support to the account history index stage using the
EitherWriterabstractionChanges
RocksDBProviderFactorytrait bound to Stage implEitherWriter::new_accounts_history()to route writes to MDBX or RocksDBu64::MAXhandlingPR Stack
Closes #20593