mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-07-23 08:58:11 -07:00
computer: stateful snapshot
This commit is contained in:
@@ -17,25 +17,31 @@ use brk_types::{DateIndex, Height, OutputType, Sats, TypeIndex};
|
|||||||
use log::info;
|
use log::info;
|
||||||
use vecdb::{Exit, GenericStoredVec, IterableVec, TypedVecIterator, VecIndex};
|
use vecdb::{Exit, GenericStoredVec, IterableVec, TypedVecIterator, VecIndex};
|
||||||
|
|
||||||
use crate::stateful::compute::flush::flush;
|
use crate::{
|
||||||
use crate::states::{BlockState, Transacted};
|
chain, indexes, price,
|
||||||
use crate::utils::OptionExt;
|
stateful::{
|
||||||
use crate::{chain, indexes, price};
|
address::AddressTypeToAddressCount,
|
||||||
|
compute::flush::{flush, process_address_updates},
|
||||||
|
process::{
|
||||||
|
AddressLookup, EmptyAddressDataWithSource, InputsResult, LoadedAddressDataWithSource,
|
||||||
|
build_txoutindex_to_height_map, process_inputs, process_outputs, process_received,
|
||||||
|
process_sent, update_tx_counts,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
states::{BlockState, Transacted},
|
||||||
|
utils::OptionExt,
|
||||||
|
};
|
||||||
|
|
||||||
use super::super::address::AddressTypeToTypeIndexMap;
|
|
||||||
use super::super::cohorts::{AddressCohorts, DynCohortVecs, UTXOCohorts};
|
|
||||||
use super::super::vecs::Vecs;
|
|
||||||
use super::{
|
use super::{
|
||||||
|
super::{
|
||||||
|
address::AddressTypeToTypeIndexMap,
|
||||||
|
cohorts::{AddressCohorts, DynCohortVecs, UTXOCohorts},
|
||||||
|
vecs::Vecs,
|
||||||
|
},
|
||||||
BIP30_DUPLICATE_HEIGHT_1, BIP30_DUPLICATE_HEIGHT_2, BIP30_ORIGINAL_HEIGHT_1,
|
BIP30_DUPLICATE_HEIGHT_1, BIP30_DUPLICATE_HEIGHT_2, BIP30_ORIGINAL_HEIGHT_1,
|
||||||
BIP30_ORIGINAL_HEIGHT_2, ComputeContext, FLUSH_INTERVAL, IndexerReaders, TxInIterators,
|
BIP30_ORIGINAL_HEIGHT_2, ComputeContext, FLUSH_INTERVAL, IndexerReaders, TxInIterators,
|
||||||
TxOutIterators, VecsReaders, build_txinindex_to_txindex, build_txoutindex_to_txindex,
|
TxOutIterators, VecsReaders, build_txinindex_to_txindex, build_txoutindex_to_txindex,
|
||||||
};
|
};
|
||||||
use crate::stateful::address::AddressTypeToAddressCount;
|
|
||||||
use crate::stateful::process::{
|
|
||||||
AddressLookup, EmptyAddressDataWithSource, InputsResult, LoadedAddressDataWithSource,
|
|
||||||
build_txoutindex_to_height_map, process_inputs, process_outputs, process_received,
|
|
||||||
process_sent, update_tx_counts,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Process all blocks from starting_height to last_height.
|
/// Process all blocks from starting_height to last_height.
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
@@ -488,15 +494,17 @@ pub fn process_blocks(
|
|||||||
// Drop readers before flush to release mmap handles
|
// Drop readers before flush to release mmap handles
|
||||||
drop(vr);
|
drop(vr);
|
||||||
|
|
||||||
flush(
|
// Process address updates (mutations)
|
||||||
vecs,
|
process_address_updates(
|
||||||
height,
|
&mut vecs.addresses_data,
|
||||||
chain_state,
|
&mut vecs.any_address_indexes,
|
||||||
&mut loaded_cache,
|
std::mem::take(&mut empty_cache),
|
||||||
&mut empty_cache,
|
std::mem::take(&mut loaded_cache),
|
||||||
exit,
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
// Flush to disk (pure I/O)
|
||||||
|
flush(vecs, height, chain_state, exit)?;
|
||||||
|
|
||||||
// Recreate readers after flush to pick up new data
|
// Recreate readers after flush to pick up new data
|
||||||
vr = VecsReaders::new(&vecs.any_address_indexes, &vecs.addresses_data);
|
vr = VecsReaders::new(&vecs.any_address_indexes, &vecs.addresses_data);
|
||||||
}
|
}
|
||||||
@@ -505,15 +513,18 @@ pub fn process_blocks(
|
|||||||
// Final flush
|
// Final flush
|
||||||
let _lock = exit.lock();
|
let _lock = exit.lock();
|
||||||
drop(vr);
|
drop(vr);
|
||||||
flush(
|
|
||||||
vecs,
|
// Process address updates (mutations)
|
||||||
last_height,
|
process_address_updates(
|
||||||
chain_state,
|
&mut vecs.addresses_data,
|
||||||
&mut loaded_cache,
|
&mut vecs.any_address_indexes,
|
||||||
&mut empty_cache,
|
std::mem::take(&mut empty_cache),
|
||||||
exit,
|
std::mem::take(&mut loaded_cache),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
// Flush to disk (pure I/O)
|
||||||
|
flush(vecs, last_height, chain_state, exit)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
//! State flushing logic for checkpoints.
|
//! State flushing logic for checkpoints.
|
||||||
//!
|
//!
|
||||||
//! Handles periodic flushing of all stateful data to disk,
|
//! Separates processing (mutations) from flushing (I/O):
|
||||||
//! including address data and chain state.
|
//! - `process_address_updates`: applies cached address changes to storage
|
||||||
|
//! - `flush`: writes all data to disk
|
||||||
|
|
||||||
use std::mem;
|
use std::time::Instant;
|
||||||
|
|
||||||
use brk_error::Result;
|
use brk_error::Result;
|
||||||
use brk_types::{AnyAddressIndex, Height};
|
use brk_types::Height;
|
||||||
use log::info;
|
use log::info;
|
||||||
use vecdb::{AnyStoredVec, Exit, GenericStoredVec, Stamp};
|
use vecdb::{AnyStoredVec, Exit, GenericStoredVec, Stamp};
|
||||||
|
|
||||||
@@ -23,38 +24,67 @@ use crate::{
|
|||||||
|
|
||||||
use super::super::address::{AddressTypeToTypeIndexMap, AddressesDataVecs, AnyAddressIndexesVecs};
|
use super::super::address::{AddressTypeToTypeIndexMap, AddressesDataVecs, AnyAddressIndexesVecs};
|
||||||
|
|
||||||
/// Apply address index updates to the index storage.
|
/// Process address updates from caches.
|
||||||
fn apply_address_index_updates(
|
///
|
||||||
|
/// Applies all accumulated address changes to storage structures:
|
||||||
|
/// - Processes empty address transitions
|
||||||
|
/// - Processes loaded address transitions
|
||||||
|
/// - Updates address indexes
|
||||||
|
///
|
||||||
|
/// Call this before `flush()` to prepare data for writing.
|
||||||
|
pub fn process_address_updates(
|
||||||
|
addresses_data: &mut AddressesDataVecs,
|
||||||
address_indexes: &mut AnyAddressIndexesVecs,
|
address_indexes: &mut AnyAddressIndexesVecs,
|
||||||
updates: AddressTypeToTypeIndexMap<AnyAddressIndex>,
|
empty_updates: AddressTypeToTypeIndexMap<EmptyAddressDataWithSource>,
|
||||||
|
loaded_updates: AddressTypeToTypeIndexMap<LoadedAddressDataWithSource>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
for (address_type, sorted) in updates.into_sorted_iter() {
|
let t0 = Instant::now();
|
||||||
|
|
||||||
|
// Process address data transitions
|
||||||
|
let empty_result = process_empty_addresses(addresses_data, empty_updates)?;
|
||||||
|
let t1 = Instant::now();
|
||||||
|
|
||||||
|
let loaded_result = process_loaded_addresses(addresses_data, loaded_updates)?;
|
||||||
|
let t2 = Instant::now();
|
||||||
|
|
||||||
|
let all_updates = empty_result.merge(loaded_result);
|
||||||
|
let t3 = Instant::now();
|
||||||
|
|
||||||
|
// Apply index updates
|
||||||
|
for (address_type, sorted) in all_updates.into_sorted_iter() {
|
||||||
for (typeindex, any_index) in sorted {
|
for (typeindex, any_index) in sorted {
|
||||||
address_indexes.update_or_push(address_type, typeindex, any_index)?;
|
address_indexes.update_or_push(address_type, typeindex, any_index)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let t4 = Instant::now();
|
||||||
|
|
||||||
|
info!(
|
||||||
|
"process_address_updates: empty={:?} loaded={:?} merge={:?} indexes={:?} total={:?}",
|
||||||
|
t1 - t0,
|
||||||
|
t2 - t1,
|
||||||
|
t3 - t2,
|
||||||
|
t4 - t3,
|
||||||
|
t4 - t0
|
||||||
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Flush checkpoint to disk.
|
/// Flush checkpoint to disk (pure I/O, no processing).
|
||||||
///
|
///
|
||||||
/// Flushes all accumulated data including:
|
/// Writes all accumulated data:
|
||||||
/// - Cohort stateful vectors
|
/// - Cohort stateful vectors
|
||||||
/// - Height-indexed vectors
|
/// - Height-indexed vectors
|
||||||
/// - Address data caches (loaded and empty)
|
/// - Address indexes and data
|
||||||
/// - Chain state (synced from in-memory to persisted)
|
/// - Transaction output index mappings
|
||||||
#[allow(clippy::too_many_arguments)]
|
/// - Chain state
|
||||||
pub fn flush(
|
pub fn flush(
|
||||||
vecs: &mut Vecs,
|
vecs: &mut Vecs,
|
||||||
height: Height,
|
height: Height,
|
||||||
chain_state: &[BlockState],
|
chain_state: &[BlockState],
|
||||||
loaded_cache: &mut AddressTypeToTypeIndexMap<LoadedAddressDataWithSource>,
|
|
||||||
empty_cache: &mut AddressTypeToTypeIndexMap<EmptyAddressDataWithSource>,
|
|
||||||
exit: &Exit,
|
exit: &Exit,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
info!("Flushing checkpoint at height {}...", height);
|
info!("Flushing at height {}...", height);
|
||||||
|
|
||||||
let _lock = exit.lock();
|
|
||||||
|
|
||||||
// Flush cohort states (separate + aggregate)
|
// Flush cohort states (separate + aggregate)
|
||||||
vecs.utxo_cohorts.safe_flush_stateful_vecs(height, exit)?;
|
vecs.utxo_cohorts.safe_flush_stateful_vecs(height, exit)?;
|
||||||
@@ -68,19 +98,12 @@ pub fn flush(
|
|||||||
vecs.addresstype_to_height_to_empty_addr_count
|
vecs.addresstype_to_height_to_empty_addr_count
|
||||||
.safe_flush(exit)?;
|
.safe_flush(exit)?;
|
||||||
|
|
||||||
// Process and flush address data updates
|
// Flush address data
|
||||||
let empty_updates = mem::take(empty_cache);
|
let stamp = Stamp::from(height);
|
||||||
let loaded_updates = mem::take(loaded_cache);
|
vecs.any_address_indexes.flush(stamp, true)?;
|
||||||
flush_address_data(
|
vecs.addresses_data.flush(stamp, true)?;
|
||||||
height,
|
|
||||||
&mut vecs.any_address_indexes,
|
|
||||||
&mut vecs.addresses_data,
|
|
||||||
empty_updates,
|
|
||||||
loaded_updates,
|
|
||||||
true,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Flush txoutindex_to_txinindex with stamp
|
// Flush txoutindex_to_txinindex
|
||||||
vecs.txoutindex_to_txinindex
|
vecs.txoutindex_to_txinindex
|
||||||
.stamped_flush_with_changes(height.into())?;
|
.stamped_flush_with_changes(height.into())?;
|
||||||
|
|
||||||
@@ -93,36 +116,3 @@ pub fn flush(
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Flush address data at a checkpoint.
|
|
||||||
///
|
|
||||||
/// Note: Cohort states are flushed separately before this is called.
|
|
||||||
///
|
|
||||||
/// 1. Process address data updates (empty and loaded)
|
|
||||||
/// 2. Update address indexes
|
|
||||||
/// 3. Stamped flush address indexes and data
|
|
||||||
fn flush_address_data(
|
|
||||||
height: Height,
|
|
||||||
address_indexes: &mut AnyAddressIndexesVecs,
|
|
||||||
addresses_data: &mut AddressesDataVecs,
|
|
||||||
empty_updates: AddressTypeToTypeIndexMap<EmptyAddressDataWithSource>,
|
|
||||||
loaded_updates: AddressTypeToTypeIndexMap<LoadedAddressDataWithSource>,
|
|
||||||
with_changes: bool,
|
|
||||||
) -> Result<()> {
|
|
||||||
info!("Flushing address data at height {}...", height);
|
|
||||||
|
|
||||||
// 1. Process address updates - empty first, then loaded
|
|
||||||
let empty_result = process_empty_addresses(addresses_data, empty_updates)?;
|
|
||||||
let loaded_result = process_loaded_addresses(addresses_data, loaded_updates)?;
|
|
||||||
let all_updates = empty_result.merge(loaded_result);
|
|
||||||
|
|
||||||
// 2. Apply index updates
|
|
||||||
apply_address_index_updates(address_indexes, all_updates)?;
|
|
||||||
|
|
||||||
// 3. Stamped flush
|
|
||||||
let stamp = Stamp::from(height);
|
|
||||||
address_indexes.flush(stamp, with_changes)?;
|
|
||||||
addresses_data.flush(stamp, with_changes)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user