mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-04-24 06:39:58 -07:00
global: speed improvement
This commit is contained in:
@@ -14,6 +14,7 @@ mod month1;
|
||||
mod month3;
|
||||
mod month6;
|
||||
pub mod timestamp;
|
||||
mod tx_heights;
|
||||
mod tx_index;
|
||||
mod txin_index;
|
||||
mod txout_index;
|
||||
@@ -50,6 +51,7 @@ pub use month1::Vecs as Month1Vecs;
|
||||
pub use month3::Vecs as Month3Vecs;
|
||||
pub use month6::Vecs as Month6Vecs;
|
||||
pub use timestamp::Timestamps;
|
||||
pub use tx_heights::TxHeights;
|
||||
pub use tx_index::Vecs as TxIndexVecs;
|
||||
pub use txin_index::Vecs as TxInIndexVecs;
|
||||
pub use txout_index::Vecs as TxOutIndexVecs;
|
||||
@@ -64,6 +66,8 @@ pub struct Vecs<M: StorageMode = Rw> {
|
||||
db: Database,
|
||||
#[traversable(skip)]
|
||||
pub cached_mappings: CachedMappings,
|
||||
#[traversable(skip)]
|
||||
pub tx_heights: TxHeights,
|
||||
pub addr: AddrVecs,
|
||||
pub height: HeightVecs<M>,
|
||||
pub epoch: EpochVecs<M>,
|
||||
@@ -143,6 +147,7 @@ impl Vecs {
|
||||
|
||||
let this = Self {
|
||||
cached_mappings,
|
||||
tx_heights: TxHeights::init(indexer),
|
||||
addr,
|
||||
height,
|
||||
epoch,
|
||||
@@ -179,6 +184,8 @@ impl Vecs {
|
||||
) -> Result<Indexes> {
|
||||
self.db.sync_bg_tasks()?;
|
||||
|
||||
self.tx_heights.update(indexer, starting_indexes.height);
|
||||
|
||||
// timestamp_monotonic must be computed first — other mappings read it
|
||||
self.timestamp
|
||||
.compute_monotonic(indexer, starting_indexes.height, exit)?;
|
||||
|
||||
61
crates/brk_computer/src/indexes/tx_heights.rs
Normal file
61
crates/brk_computer/src/indexes/tx_heights.rs
Normal file
@@ -0,0 +1,61 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use brk_indexer::Indexer;
|
||||
use brk_types::{Height, RangeMap, TxIndex};
|
||||
use parking_lot::RwLock;
|
||||
use vecdb::{AnyVec, ReadableVec, VecIndex};
|
||||
|
||||
/// Reverse mapping from `TxIndex` → `Height` via binary search on block boundaries.
|
||||
///
|
||||
/// Built from `first_tx_index` (the first TxIndex in each block). A floor lookup
|
||||
/// on any TxIndex gives the block height that contains it.
|
||||
///
|
||||
/// Wrapped in `Arc<RwLock<>>` so the compute thread can extend it while
|
||||
/// query threads read concurrently — the inner `RangeMap` is purely in-memory
|
||||
/// and wouldn't stay current through mmap like PcoVec/BytesVec do.
|
||||
#[derive(Clone)]
|
||||
pub struct TxHeights(Arc<RwLock<RangeMap<TxIndex, Height>>>);
|
||||
|
||||
impl TxHeights {
|
||||
/// Build from the full `first_tx_index` vec at startup.
|
||||
pub fn init(indexer: &Indexer) -> Self {
|
||||
let len = indexer.vecs.transactions.first_tx_index.len();
|
||||
let entries: Vec<TxIndex> = if len > 0 {
|
||||
indexer
|
||||
.vecs
|
||||
.transactions
|
||||
.first_tx_index
|
||||
.collect_range_at(0, len)
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
Self(Arc::new(RwLock::new(RangeMap::from(entries))))
|
||||
}
|
||||
|
||||
/// Extend with new blocks since last call. Truncates on reorg.
|
||||
pub fn update(&self, indexer: &Indexer, reorg_height: Height) {
|
||||
let mut inner = self.0.write();
|
||||
let reorg_len = reorg_height.to_usize();
|
||||
if inner.len() > reorg_len {
|
||||
inner.truncate(reorg_len);
|
||||
}
|
||||
let target_len = indexer.vecs.transactions.first_tx_index.len();
|
||||
let current_len = inner.len();
|
||||
if current_len < target_len {
|
||||
let new_entries: Vec<TxIndex> = indexer
|
||||
.vecs
|
||||
.transactions
|
||||
.first_tx_index
|
||||
.collect_range_at(current_len, target_len);
|
||||
for entry in new_entries {
|
||||
inner.push(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Look up the block height for a given tx_index.
|
||||
#[inline]
|
||||
pub fn get_shared(&self, tx_index: TxIndex) -> Option<Height> {
|
||||
self.0.read().get_shared(tx_index)
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,9 @@ use vecdb::{
|
||||
|
||||
pub mod major;
|
||||
pub mod minor;
|
||||
mod pool_heights;
|
||||
|
||||
pub use pool_heights::PoolHeights;
|
||||
|
||||
use crate::{
|
||||
blocks, indexes,
|
||||
@@ -30,6 +33,8 @@ pub struct Vecs<M: StorageMode = Rw> {
|
||||
pools: &'static Pools,
|
||||
|
||||
pub pool: M::Stored<BytesVec<Height, PoolSlug>>,
|
||||
#[traversable(skip)]
|
||||
pub pool_heights: PoolHeights,
|
||||
pub major: BTreeMap<PoolSlug, major::Vecs<M>>,
|
||||
pub minor: BTreeMap<PoolSlug, minor::Vecs<M>>,
|
||||
}
|
||||
@@ -63,8 +68,12 @@ impl Vecs {
|
||||
}
|
||||
}
|
||||
|
||||
let pool = BytesVec::forced_import(&db, "pool", version)?;
|
||||
let pool_heights = PoolHeights::build(&pool);
|
||||
|
||||
let this = Self {
|
||||
pool: BytesVec::forced_import(&db, "pool", version)?,
|
||||
pool,
|
||||
pool_heights,
|
||||
major: major_map,
|
||||
minor: minor_map,
|
||||
pools,
|
||||
@@ -149,8 +158,10 @@ impl Vecs {
|
||||
let mut output_count_cursor = indexes.tx_index.output_count.cursor();
|
||||
|
||||
self.pool.truncate_if_needed_at(min)?;
|
||||
self.pool_heights.truncate(min);
|
||||
|
||||
let len = indexer.vecs.blocks.coinbase_tag.len();
|
||||
let mut next_height = min;
|
||||
|
||||
indexer.vecs.blocks.coinbase_tag.try_for_each_range_at(
|
||||
min,
|
||||
@@ -186,6 +197,9 @@ impl Vecs {
|
||||
.unwrap_or(unknown);
|
||||
|
||||
self.pool.push(pool.slug);
|
||||
self.pool_heights.push(pool.slug, Height::from(next_height));
|
||||
next_height += 1;
|
||||
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
39
crates/brk_computer/src/pools/pool_heights.rs
Normal file
39
crates/brk_computer/src/pools/pool_heights.rs
Normal file
@@ -0,0 +1,39 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use brk_types::{Height, PoolSlug};
|
||||
use parking_lot::RwLock;
|
||||
use rustc_hash::FxHashMap;
|
||||
use vecdb::{AnyVec, BytesVec, VecIndex};
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct PoolHeights(Arc<RwLock<FxHashMap<PoolSlug, Vec<Height>>>>);
|
||||
|
||||
impl PoolHeights {
|
||||
pub fn build(pool: &BytesVec<Height, PoolSlug>) -> Self {
|
||||
let len = pool.len();
|
||||
let mut map: FxHashMap<PoolSlug, Vec<Height>> = FxHashMap::default();
|
||||
let reader = pool.reader();
|
||||
for h in 0..len {
|
||||
map.entry(reader.get(h))
|
||||
.or_default()
|
||||
.push(Height::from(h));
|
||||
}
|
||||
Self(Arc::new(RwLock::new(map)))
|
||||
}
|
||||
|
||||
pub fn truncate(&self, min: usize) {
|
||||
let mut cache = self.0.write();
|
||||
for heights in cache.values_mut() {
|
||||
let cut = heights.partition_point(|h| h.to_usize() < min);
|
||||
heights.truncate(cut);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push(&self, slug: PoolSlug, height: Height) {
|
||||
self.0.write().entry(slug).or_default().push(height);
|
||||
}
|
||||
|
||||
pub fn read(&self) -> parking_lot::RwLockReadGuard<'_, FxHashMap<PoolSlug, Vec<Height>>> {
|
||||
self.0.read()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user