global: speed improvement part2

This commit is contained in:
nym21
2026-04-09 14:02:26 +02:00
parent 21a0226a19
commit 5a3e1b4e6e
24 changed files with 137 additions and 160 deletions

View File

@@ -3464,7 +3464,6 @@ impl SeriesTree_Transactions {
/// Series tree node.
pub struct SeriesTree_Transactions_Raw {
pub first_tx_index: SeriesPattern18<TxIndex>,
pub height: SeriesPattern19<Height>,
pub txid: SeriesPattern19<Txid>,
pub tx_version: SeriesPattern19<TxVersion>,
pub raw_locktime: SeriesPattern19<RawLockTime>,
@@ -3479,7 +3478,6 @@ impl SeriesTree_Transactions_Raw {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
first_tx_index: SeriesPattern18::new(client.clone(), "first_tx_index".to_string()),
height: SeriesPattern19::new(client.clone(), "height".to_string()),
txid: SeriesPattern19::new(client.clone(), "txid".to_string()),
tx_version: SeriesPattern19::new(client.clone(), "tx_version".to_string()),
raw_locktime: SeriesPattern19::new(client.clone(), "raw_locktime".to_string()),
@@ -3495,14 +3493,12 @@ impl SeriesTree_Transactions_Raw {
/// Series tree node.
pub struct SeriesTree_Transactions_Count {
pub total: AverageBlockCumulativeMaxMedianMinPct10Pct25Pct75Pct90SumPattern,
pub is_coinbase: SeriesPattern19<StoredBool>,
}
impl SeriesTree_Transactions_Count {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
total: AverageBlockCumulativeMaxMedianMinPct10Pct25Pct75Pct90SumPattern::new(client.clone(), "tx_count".to_string()),
is_coinbase: SeriesPattern19::new(client.clone(), "is_coinbase".to_string()),
}
}
}

View File

@@ -157,7 +157,7 @@ impl Vecs {
indexer: &Indexer<M>,
range: Range<usize>,
) -> Vec<f64> {
let total_txs = indexer.vecs.transactions.height.len();
let total_txs = indexer.vecs.transactions.txid.len();
let total_outputs = indexer.vecs.outputs.value.len();
// Pre-collect height-indexed data for the range (plus one extra for next-block lookups)

View File

@@ -1,7 +1,6 @@
use brk_error::Result;
use brk_indexer::Indexer;
use brk_types::{StoredBool, TxIndex, Version};
use vecdb::{Database, LazyVecFrom2, ReadableCloneableVec};
use brk_types::Version;
use vecdb::Database;
use super::Vecs;
use crate::{
@@ -13,25 +12,11 @@ impl Vecs {
pub(crate) fn forced_import(
db: &Database,
version: Version,
indexer: &Indexer,
indexes: &indexes::Vecs,
cached_starts: &CachedWindowStarts,
) -> Result<Self> {
let tx_index_to_is_coinbase = LazyVecFrom2::init(
"is_coinbase",
version,
indexer.vecs.transactions.height.read_only_boxed_clone(),
indexer
.vecs
.transactions
.first_tx_index
.read_only_boxed_clone(),
|index: TxIndex, _height, first_tx_index| StoredBool::from(index == first_tx_index),
);
Ok(Self {
total: PerBlockFull::forced_import(db, "tx_count", version, indexes, cached_starts)?,
is_coinbase: tx_index_to_is_coinbase,
})
}
}

View File

@@ -1,11 +1,10 @@
use brk_traversable::Traversable;
use brk_types::{Height, StoredBool, StoredU64, TxIndex};
use vecdb::{LazyVecFrom2, Rw, StorageMode};
use brk_types::StoredU64;
use vecdb::{Rw, StorageMode};
use crate::internal::PerBlockFull;
#[derive(Traversable)]
pub struct Vecs<M: StorageMode = Rw> {
pub total: PerBlockFull<StoredU64, M>,
pub is_coinbase: LazyVecFrom2<TxIndex, StoredBool, TxIndex, Height, Height, TxIndex>,
}

View File

@@ -1,6 +1,6 @@
use brk_error::Result;
use brk_indexer::Indexer;
use brk_types::{FeeRate, Indexes, OutPoint, Sats, TxInIndex, VSize};
use brk_types::{FeeRate, Indexes, OutPoint, Sats, TxInIndex, TxIndex, VSize};
use vecdb::{AnyStoredVec, AnyVec, Exit, ReadableVec, VecIndex, WritableVec, unlikely};
use super::super::size;
@@ -125,11 +125,9 @@ impl Vecs {
let start_height = if start_tx == 0 {
0
} else {
indexer
.vecs
.transactions
.height
.collect_one_at(start_tx)
indexes
.tx_heights
.get_shared(TxIndex::from(start_tx))
.unwrap()
.to_usize()
};

View File

@@ -25,7 +25,7 @@ impl Vecs {
let db = open_db(parent_path, super::DB_NAME, 10_000_000)?;
let version = parent_version;
let count = CountVecs::forced_import(&db, version, indexer, indexes, cached_starts)?;
let count = CountVecs::forced_import(&db, version, indexes, cached_starts)?;
let size = SizeVecs::forced_import(&db, version, indexer, indexes)?;
let fees = FeesVecs::forced_import(&db, version, indexes)?;
let versions = VersionsVecs::forced_import(&db, version, indexes, cached_starts)?;

View File

@@ -43,7 +43,6 @@ impl BlockProcessor<'_> {
already_added: &mut ByAddrType<FxHashMap<AddrHash, TypeIndex>>,
same_block_info: &mut FxHashMap<OutPoint, SameBlockOutputInfo>,
) -> Result<()> {
let height = self.height;
let indexes = &mut *self.indexes;
// Split transactions vecs: finalize needs first_txout_index/first_txin_index, metadata needs the rest
@@ -85,7 +84,7 @@ impl BlockProcessor<'_> {
same_block_info,
)
},
|| tx::store_tx_metadata(height, txs, txid_prefix_store, &mut tx_metadata),
|| tx::store_tx_metadata(txs, txid_prefix_store, &mut tx_metadata),
);
finalize_result?;

View File

@@ -1,6 +1,6 @@
use brk_error::{Error, Result};
use brk_store::Store;
use brk_types::{Height, StoredBool, TxIndex, Txid, TxidPrefix};
use brk_types::{StoredBool, TxIndex, Txid, TxidPrefix};
use rayon::prelude::*;
use tracing::error;
use vecdb::{AnyVec, WritableVec, likely};
@@ -89,7 +89,6 @@ impl<'a> BlockProcessor<'a> {
}
pub(super) fn store_tx_metadata(
height: Height,
txs: Vec<ComputedTx>,
store: &mut Store<TxidPrefix, TxIndex>,
md: &mut TxMetadataVecs<'_>,
@@ -98,7 +97,6 @@ pub(super) fn store_tx_metadata(
if ct.prev_tx_index_opt.is_none() {
store.insert(ct.txid_prefix, ct.tx_index);
}
md.height.checked_push(ct.tx_index, height)?;
md.tx_version
.checked_push(ct.tx_index, ct.tx.version.into())?;
md.txid.checked_push(ct.tx_index, ct.txid)?;

View File

@@ -6,7 +6,8 @@ use brk_types::{
};
use rayon::prelude::*;
use vecdb::{
AnyStoredVec, BytesVec, Database, ImportableVec, PcoVec, Rw, Stamp, StorageMode, WritableVec,
AnyStoredVec, BytesVec, CachedVec, Database, ImportableVec, PcoVec, Rw, Stamp, StorageMode,
WritableVec,
};
use crate::parallel_import;
@@ -14,12 +15,16 @@ use crate::parallel_import;
#[derive(Traversable)]
pub struct BlocksVecs<M: StorageMode = Rw> {
pub blockhash: M::Stored<BytesVec<Height, BlockHash>>,
#[traversable(skip)]
pub cached_blockhash: CachedVec<Height, BlockHash>,
pub coinbase_tag: M::Stored<BytesVec<Height, CoinbaseTag>>,
#[traversable(wrap = "difficulty", rename = "value")]
pub difficulty: M::Stored<PcoVec<Height, StoredF64>>,
/// Doesn't guarantee continuity due to possible reorgs and more generally the nature of mining
#[traversable(wrap = "time")]
pub timestamp: M::Stored<PcoVec<Height, Timestamp>>,
#[traversable(skip)]
pub cached_timestamp: CachedVec<Height, Timestamp>,
#[traversable(wrap = "size", rename = "base")]
pub total: M::Stored<PcoVec<Height, StoredU64>>,
#[traversable(wrap = "weight", rename = "base")]
@@ -56,11 +61,16 @@ impl BlocksVecs {
segwit_size = PcoVec::forced_import(db, "segwit_size", version),
segwit_weight = PcoVec::forced_import(db, "segwit_weight", version),
};
let cached_blockhash = CachedVec::new(&blockhash);
let cached_timestamp = CachedVec::new(&timestamp);
Ok(Self {
blockhash,
cached_blockhash,
coinbase_tag,
difficulty,
timestamp,
cached_timestamp,
total,
weight,
position,

View File

@@ -14,7 +14,6 @@ use crate::parallel_import;
#[derive(Traversable)]
pub struct TransactionsVecs<M: StorageMode = Rw> {
pub first_tx_index: M::Stored<PcoVec<Height, TxIndex>>,
pub height: M::Stored<PcoVec<TxIndex, Height>>,
pub txid: M::Stored<BytesVec<TxIndex, Txid>>,
pub tx_version: M::Stored<PcoVec<TxIndex, TxVersion>>,
pub raw_locktime: M::Stored<PcoVec<TxIndex, RawLockTime>>,
@@ -28,7 +27,6 @@ pub struct TransactionsVecs<M: StorageMode = Rw> {
}
pub struct TxMetadataVecs<'a> {
pub height: &'a mut PcoVec<TxIndex, Height>,
pub tx_version: &'a mut PcoVec<TxIndex, TxVersion>,
pub txid: &'a mut BytesVec<TxIndex, Txid>,
pub raw_locktime: &'a mut PcoVec<TxIndex, RawLockTime>,
@@ -49,7 +47,6 @@ impl TransactionsVecs {
&mut self.first_txout_index,
&mut self.first_txin_index,
TxMetadataVecs {
height: &mut self.height,
tx_version: &mut self.tx_version,
txid: &mut self.txid,
raw_locktime: &mut self.raw_locktime,
@@ -63,7 +60,6 @@ impl TransactionsVecs {
pub fn forced_import(db: &Database, version: Version) -> Result<Self> {
let (
first_tx_index,
height,
txid,
tx_version,
raw_locktime,
@@ -75,7 +71,6 @@ impl TransactionsVecs {
position,
) = parallel_import! {
first_tx_index = PcoVec::forced_import(db, "first_tx_index", version),
height = PcoVec::forced_import(db, "height", version),
txid = BytesVec::forced_import(db, "txid", version),
tx_version = PcoVec::forced_import(db, "tx_version", version),
raw_locktime = PcoVec::forced_import(db, "raw_locktime", version),
@@ -88,7 +83,6 @@ impl TransactionsVecs {
};
Ok(Self {
first_tx_index,
height,
txid,
tx_version,
raw_locktime,
@@ -104,7 +98,6 @@ impl TransactionsVecs {
pub fn truncate(&mut self, height: Height, tx_index: TxIndex, stamp: Stamp) -> Result<()> {
self.first_tx_index
.truncate_if_needed_with_stamp(height, stamp)?;
self.height.truncate_if_needed_with_stamp(tx_index, stamp)?;
self.txid.truncate_if_needed_with_stamp(tx_index, stamp)?;
self.tx_version
.truncate_if_needed_with_stamp(tx_index, stamp)?;
@@ -128,7 +121,6 @@ impl TransactionsVecs {
pub fn par_iter_mut_any(&mut self) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
[
&mut self.first_tx_index as &mut dyn AnyStoredVec,
&mut self.height,
&mut self.txid,
&mut self.tx_version,
&mut self.raw_locktime,

View File

@@ -150,7 +150,7 @@ fn main() {
let idx = |m: usize, s: usize| -> usize { m * start_heights.len() + s };
let total_txs = indexer.vecs.transactions.height.len();
let total_txs = indexer.vecs.transactions.txid.len();
let total_outputs = indexer.vecs.outputs.value.len();
let first_tx_index: Vec<TxIndex> = indexer.vecs.transactions.first_tx_index.collect();

View File

@@ -46,7 +46,7 @@ fn main() {
let config = Config::default();
let window_size = config.window_size;
let total_txs = indexer.vecs.transactions.height.len();
let total_txs = indexer.vecs.transactions.txid.len();
let total_outputs = indexer.vecs.outputs.value.len();
let first_tx_index: Vec<TxIndex> = indexer.vecs.transactions.first_tx_index.collect();

View File

@@ -91,7 +91,7 @@ fn main() {
// Build per-block RAW histograms from the lowest start height.
eprintln!("Building histograms from height {}...", lowest);
let total_txs = indexer.vecs.transactions.height.len();
let total_txs = indexer.vecs.transactions.txid.len();
let total_outputs = indexer.vecs.outputs.value.len();
let first_txout_index_reader = indexer.vecs.transactions.first_txout_index.reader();

View File

@@ -181,7 +181,7 @@ fn main() {
let config = Config::default();
let mut oracle = Oracle::new(cents_to_bin(start_price * 100.0), config);
let total_txs = indexer.vecs.transactions.height.len();
let total_txs = indexer.vecs.transactions.txid.len();
let total_outputs = indexer.vecs.outputs.value.len();
// Pre-collect height-indexed vecs (small). Transaction-indexed vecs are too large.

View File

@@ -163,7 +163,7 @@ fn main() {
// Phase 1: precompute per-block data in a single pass over the indexer.
eprintln!("Phase 1: precomputing block data...");
let total_txs = indexer.vecs.transactions.height.len();
let total_txs = indexer.vecs.transactions.txid.len();
let total_outputs = indexer.vecs.outputs.value.len();
let first_tx_index: Vec<TxIndex> = indexer.vecs.transactions.first_tx_index.collect();

View File

@@ -161,7 +161,7 @@ fn main() {
// filter at different tolerance thresholds in Phase 2.
eprintln!("Phase 1: precomputing block data...");
let total_txs = indexer.vecs.transactions.height.len();
let total_txs = indexer.vecs.transactions.txid.len();
let total_outputs = indexer.vecs.outputs.value.len();
let first_tx_index: Vec<TxIndex> = indexer.vecs.transactions.first_tx_index.collect();

View File

@@ -148,7 +148,7 @@ fn main() {
];
// Build per-block filtered histograms from the indexer, feeding all oracles in one pass.
let total_txs = indexer.vecs.transactions.height.len();
let total_txs = indexer.vecs.transactions.txid.len();
let total_outputs = indexer.vecs.outputs.value.len();
// Pre-collect height-indexed vecs (small). Transaction-indexed vecs are too large.

View File

@@ -4,7 +4,7 @@ use bitcoin::{Network, PublicKey, ScriptBuf};
use brk_error::{Error, OptionData, Result};
use brk_types::{
Addr, AddrBytes, AddrChainStats, AddrHash, AddrIndexOutPoint, AddrIndexTxIndex, AddrStats,
AnyAddrDataIndexEnum, BlockHash, Dollars, Height, OutputType, Timestamp, Transaction, TxIndex,
AnyAddrDataIndexEnum, Dollars, Height, OutputType, Transaction, TxIndex,
TxStatus, Txid, TypeIndex, Unit, Utxo, Vout,
};
use vecdb::{ReadableVec, VecIndex};
@@ -189,11 +189,8 @@ impl Query {
let txid_reader = vecs.transactions.txid.reader();
let first_txout_index_reader = vecs.transactions.first_txout_index.reader();
let value_reader = vecs.outputs.value.reader();
let blockhash_reader = vecs.blocks.blockhash.reader();
let tx_heights = &self.computer().indexes.tx_heights;
let mut block_ts_cursor = vecs.blocks.timestamp.cursor();
let mut cached_block: Option<(Height, BlockHash, Timestamp)> = None;
let mut utxos = Vec::with_capacity(outpoints.len());
for (tx_index, vout) in outpoints {
@@ -202,16 +199,8 @@ impl Query {
let first_txout_index = first_txout_index_reader.get(tx_index.to_usize());
let value = value_reader.get(usize::from(first_txout_index + vout));
let (block_hash, block_time) = if let Some((h, ref bh, bt)) = cached_block
&& h == height
{
(bh.clone(), bt)
} else {
let bh = blockhash_reader.get(height.to_usize());
let bt = block_ts_cursor.get(height.to_usize()).data()?;
cached_block = Some((height, bh.clone(), bt));
(bh, bt)
};
let block_hash = vecs.blocks.cached_blockhash.collect_one(height).data()?;
let block_time = vecs.blocks.cached_timestamp.collect_one(height).data()?;
utxos.push(Utxo {
txid,
@@ -268,11 +257,10 @@ impl Query {
.next_back()
.map(|(key, _): (AddrIndexTxIndex, Unit)| key.tx_index())
.ok_or(Error::UnknownAddr)?;
self.indexer()
.vecs
.transactions
.height
.collect_one(last_tx_index)
self.computer()
.indexes
.tx_heights
.get_shared(last_tx_index)
.ok_or(Error::UnknownAddr)
}

View File

@@ -60,9 +60,9 @@ impl Query {
let blockhash = indexer
.vecs
.blocks
.blockhash
.reader()
.get(usize::from(height));
.cached_blockhash
.collect_one(height)
.data()?;
// Convert timestamp to ISO 8601 format
let ts_secs: i64 = (*best_ts).into();

View File

@@ -4,7 +4,7 @@ use bitcoin::{consensus::Decodable, hex::DisplayHex};
use brk_error::{Error, OptionData, Result};
use brk_types::{
BlkPosition, BlockHash, Height, OutPoint, OutputType, RawLockTime, Sats, StoredU32, Timestamp,
Transaction, TxIn, TxInIndex, TxIndex, TxOut, TxOutIndex, TxStatus, Txid, Vout, Weight,
Transaction, TxIn, TxInIndex, TxIndex, TxOut, TxStatus, Txid, TypeIndex, Vout, Weight,
};
use rustc_hash::FxHashMap;
use vecdb::{AnyVec, ReadableVec, VecIndex};
@@ -75,6 +75,7 @@ impl Query {
return Ok(Vec::new());
}
let t0 = std::time::Instant::now();
let len = indices.len();
// Sort positions ascending for sequential I/O (O(n) when already sorted)
@@ -92,9 +93,6 @@ impl Query {
let mut total_size_cursor = indexer.vecs.transactions.total_size.cursor();
let mut first_txin_cursor = indexer.vecs.transactions.first_txin_index.cursor();
let mut position_cursor = indexer.vecs.transactions.position.cursor();
let blockhash_reader = indexer.vecs.blocks.blockhash.reader();
let mut block_ts_cursor = indexer.vecs.blocks.timestamp.cursor();
struct DecodedTx {
pos: usize,
tx_index: TxIndex,
@@ -109,7 +107,6 @@ impl Query {
outpoints: Vec<OutPoint>,
}
let mut cached_block: Option<(Height, BlockHash, Timestamp)> = None;
let mut decoded_txs: Vec<DecodedTx> = Vec::with_capacity(len);
let mut total_inputs: usize = 0;
@@ -125,16 +122,18 @@ impl Query {
let first_txin_index: TxInIndex = first_txin_cursor.get(idx).data()?;
let position: BlkPosition = position_cursor.get(idx).data()?;
let (block_hash, block_time) = if let Some((h, ref bh, bt)) = cached_block
&& h == height
{
(bh.clone(), bt)
} else {
let bh = blockhash_reader.get(height.to_usize());
let bt = block_ts_cursor.get(height.to_usize()).data()?;
cached_block = Some((height, bh.clone(), bt));
(bh, bt)
};
let block_hash = indexer
.vecs
.blocks
.cached_blockhash
.collect_one(height)
.data()?;
let block_time = indexer
.vecs
.blocks
.cached_timestamp
.collect_one(height)
.data()?;
let buffer = reader.read_raw_bytes(position, *total_size as usize)?;
let decoded = bitcoin::Transaction::consensus_decode(&mut Cursor::new(buffer))
@@ -157,19 +156,39 @@ impl Query {
});
}
// Phase 1b: Batch-read outpoints via cursor (PcoVec — sequential
// cursor avoids re-decompressing the same pages)
let t_phase1a = t0.elapsed();
// Phase 1b: Batch-read outpoints + prevout data via cursors (PcoVec —
// sequential cursor avoids re-decompressing the same pages).
// Reading output_type/type_index/value HERE from inputs vecs (sequential)
// avoids random-reading them from outputs vecs in Phase 2.
let mut outpoint_cursor = indexer.vecs.inputs.outpoint.cursor();
let mut input_output_type_cursor = indexer.vecs.inputs.output_type.cursor();
let mut input_type_index_cursor = indexer.vecs.inputs.type_index.cursor();
let mut input_value_cursor = self.computer().inputs.spent.value.cursor();
let mut prevout_input_data: FxHashMap<OutPoint, (OutputType, TypeIndex, Sats)> =
FxHashMap::with_capacity_and_hasher(total_inputs, Default::default());
for dtx in &mut decoded_txs {
let start = usize::from(dtx.first_txin_index);
let count = dtx.decoded.input.len();
let mut outpoints = Vec::with_capacity(count);
for i in 0..count {
outpoints.push(outpoint_cursor.get(start + i).data()?);
let op: OutPoint = outpoint_cursor.get(start + i).data()?;
if op.is_not_coinbase() {
let ot: OutputType = input_output_type_cursor.get(start + i).data()?;
let ti: TypeIndex = input_type_index_cursor.get(start + i).data()?;
let val: Sats = input_value_cursor.get(start + i).data()?;
prevout_input_data.insert(op, (ot, ti, val));
}
outpoints.push(op);
}
dtx.outpoints = outpoints;
}
let t_phase1b = t0.elapsed();
// ── Phase 2: Batch-read prevout data in sorted order ────────────
// Collect all non-coinbase outpoints, deduplicate, sort by tx_index
@@ -184,53 +203,25 @@ impl Query {
prevout_keys.sort_unstable();
prevout_keys.dedup();
// Batch-read txid + first_txout_index sorted by prev_tx_index
// Batch-read txid sorted by prev_tx_index (only remaining random read)
let txid_reader = indexer.vecs.transactions.txid.reader();
let first_txout_index_reader = indexer.vecs.transactions.first_txout_index.reader();
struct PrevoutIntermediate {
outpoint: OutPoint,
txid: Txid,
txout_index: TxOutIndex,
}
let mut intermediates: Vec<PrevoutIntermediate> = Vec::with_capacity(prevout_keys.len());
for &op in &prevout_keys {
let prev_tx_idx = op.tx_index().to_usize();
let txid = txid_reader.get(prev_tx_idx);
let first_txout = first_txout_index_reader.get(prev_tx_idx);
let txout_index = first_txout + op.vout();
intermediates.push(PrevoutIntermediate {
outpoint: op,
txid,
txout_index,
});
}
// Re-sort by txout_index for sequential output data reads
intermediates.sort_unstable_by_key(|i| i.txout_index);
let value_reader = indexer.vecs.outputs.value.reader();
let output_type_reader = indexer.vecs.outputs.output_type.reader();
let type_index_reader = indexer.vecs.outputs.type_index.reader();
let addr_readers = indexer.vecs.addrs.addr_readers();
let mut prevout_map: FxHashMap<OutPoint, (Txid, TxOut)> =
FxHashMap::with_capacity_and_hasher(intermediates.len(), Default::default());
FxHashMap::with_capacity_and_hasher(prevout_keys.len(), Default::default());
for inter in &intermediates {
let txout_idx = usize::from(inter.txout_index);
let value: Sats = value_reader.get(txout_idx);
let output_type: OutputType = output_type_reader.get(txout_idx);
let type_index = type_index_reader.get(txout_idx);
for &op in &prevout_keys {
let txid = txid_reader.get(op.tx_index().to_usize());
// output_type, type_index, value pre-read from inputs vecs (sequential)
let &(output_type, type_index, value) =
prevout_input_data.get(&op).unwrap();
let script_pubkey = addr_readers.script_pubkey(output_type, type_index);
prevout_map.insert(
inter.outpoint,
(inter.txid.clone(), TxOut::from((script_pubkey, value))),
);
prevout_map.insert(op, (txid, TxOut::from((script_pubkey, value))));
}
let t_phase2 = t0.elapsed();
// ── Phase 3: Assemble Transaction objects ───────────────────────
let mut txs: Vec<Option<Transaction>> = (0..len).map(|_| None).collect();
@@ -320,6 +311,22 @@ impl Query {
txs[dtx.pos] = Some(transaction);
}
let t_phase3 = t0.elapsed();
if t_phase3.as_millis() > 50 {
eprintln!(
"[perf:txs] n={} vin={} prevouts={} | 1a={:.1?} 1b={:.1?} | 2={:.1?} | 3={:.1?} | total={:.1?}",
len,
total_inputs,
prevout_keys.len(),
t_phase1a,
t_phase1b - t_phase1a,
t_phase2 - t_phase1b,
t_phase3 - t_phase2,
t_phase3,
);
}
Ok(txs.into_iter().map(Option::unwrap).collect())
}

View File

@@ -1,7 +1,7 @@
use bitcoin::hex::{DisplayHex, FromHex};
use brk_error::{Error, OptionData, Result};
use brk_types::{
BlockHash, Height, MerkleProof, Timestamp, Transaction, TxInIndex, TxIndex, TxOutIndex,
Height, MerkleProof, Transaction, TxInIndex, TxIndex, TxOutIndex,
TxOutspend, TxStatus, Txid, TxidPrefix, Vin, Vout,
};
use vecdb::{ReadableVec, VecIndex};
@@ -59,13 +59,8 @@ impl Query {
.tx_heights
.get_shared(tx_index)
.data()?;
let block_hash = indexer
.vecs
.blocks
.blockhash
.reader()
.get(height.to_usize());
let block_time = indexer.vecs.blocks.timestamp.collect_one(height).data()?;
let block_hash = indexer.vecs.blocks.cached_blockhash.collect_one(height).data()?;
let block_time = indexer.vecs.blocks.cached_timestamp.collect_one(height).data()?;
Ok(TxStatus {
confirmed: true,
@@ -144,14 +139,10 @@ impl Query {
let indexer = self.indexer();
let txin_index_reader = self.computer().outputs.spent.txin_index.reader();
let txid_reader = indexer.vecs.transactions.txid.reader();
let blockhash_reader = indexer.vecs.blocks.blockhash.reader();
let tx_heights = &self.computer().indexes.tx_heights;
let mut input_tx_cursor = indexer.vecs.inputs.tx_index.cursor();
let mut first_txin_cursor = indexer.vecs.transactions.first_txin_index.cursor();
let mut block_ts_cursor = indexer.vecs.blocks.timestamp.cursor();
let mut cached_block: Option<(Height, BlockHash, Timestamp)> = None;
let mut outspends = Vec::with_capacity(output_count);
for i in 0..output_count {
@@ -168,16 +159,18 @@ impl Query {
let spending_txid = txid_reader.get(spending_tx_index.to_usize());
let spending_height = tx_heights.get_shared(spending_tx_index).data()?;
let (block_hash, block_time) = if let Some((h, ref bh, bt)) = cached_block
&& h == spending_height
{
(bh.clone(), bt)
} else {
let bh = blockhash_reader.get(spending_height.to_usize());
let bt = block_ts_cursor.get(spending_height.to_usize()).data()?;
cached_block = Some((spending_height, bh.clone(), bt));
(bh, bt)
};
let block_hash = indexer
.vecs
.blocks
.cached_blockhash
.collect_one(spending_height)
.data()?;
let block_time = indexer
.vecs
.blocks
.cached_timestamp
.collect_one(spending_height)
.data()?;
outspends.push(TxOutspend {
spent: true,

View File

@@ -189,8 +189,26 @@ impl AppState {
F: FnOnce(&brk_query::Query) -> brk_error::Result<T> + Send + 'static,
{
self.cached(headers, strategy, uri, "application/json", move |q, enc| {
let t0 = std::time::Instant::now();
let value = f(q)?;
Ok(enc.compress(Bytes::from(serde_json::to_vec(&value).unwrap())))
let t_query = t0.elapsed();
let json = serde_json::to_vec(&value).unwrap();
let t_json = t0.elapsed();
let json_len = json.len();
let compressed = enc.compress(Bytes::from(json));
let t_total = t0.elapsed();
if t_total.as_millis() > 100 {
eprintln!(
"[perf] query={:.1?} json={:.1?}({:.1}MB) compress={:.1?}({}) total={:.1?}",
t_query,
t_json - t_query,
json_len as f64 / 1_048_576.0,
t_total - t_json,
enc.as_str(),
t_total,
);
}
Ok(compressed)
})
.await
}