Skip to content

Commit b1545ee

Browse files
committed
perf: reuse sqlite connection in get() impl for readonly marf instances
1 parent ff97989 commit b1545ee

File tree

5 files changed

+136
-17
lines changed

5 files changed

+136
-17
lines changed

stackslib/src/chainstate/burn/db/sortdb.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2071,15 +2071,15 @@ impl<'a> SortitionHandleConn<'a> {
20712071
connection: &'a SortitionDBConn<'a>,
20722072
chain_tip: &SortitionId,
20732073
) -> Result<SortitionHandleConn<'a>, db_error> {
2074-
Ok(SortitionHandleConn {
2075-
context: SortitionHandleContext {
2074+
Ok(SortitionHandleConn::new(
2075+
&connection.index,
2076+
SortitionHandleContext {
20762077
chain_tip: chain_tip.clone(),
20772078
first_block_height: connection.context.first_block_height,
20782079
pox_constants: connection.context.pox_constants.clone(),
20792080
dryrun: connection.context.dryrun,
20802081
},
2081-
index: connection.index,
2082-
})
2082+
))
20832083
}
20842084

20852085
fn get_tip_indexed(&self, key: &str) -> Result<Option<String>, db_error> {
@@ -3766,15 +3766,15 @@ impl SortitionDBTx<'_> {
37663766

37673767
impl SortitionDBConn<'_> {
37683768
pub fn as_handle<'b>(&'b self, chain_tip: &SortitionId) -> SortitionHandleConn<'b> {
3769-
SortitionHandleConn {
3770-
index: self.index,
3771-
context: SortitionHandleContext {
3769+
SortitionHandleConn::new(
3770+
&self.index,
3771+
SortitionHandleContext {
37723772
first_block_height: self.context.first_block_height.clone(),
37733773
chain_tip: chain_tip.clone(),
37743774
pox_constants: self.context.pox_constants.clone(),
37753775
dryrun: self.context.dryrun,
37763776
},
3777-
}
3777+
)
37783778
}
37793779

37803780
/// Given a burnchain consensus hash,

stackslib/src/chainstate/stacks/index/marf.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ use stacks_common::types::chainstate::{BlockHeaderHash, TrieHash, TRIEHASH_ENCOD
2525
use stacks_common::util::hash::Sha512Trunc256Sum;
2626
use stacks_common::util::log;
2727

28+
use super::storage::ReopenedTrieStorageConnection;
2829
use crate::chainstate::stacks::index::bits::{get_leaf_hash, get_node_hash, read_root_hash};
2930
use crate::chainstate::stacks::index::node::{
3031
clear_backptr, is_backptr, set_backptr, CursorError, TrieCursor, TrieNode, TrieNode16,
@@ -251,6 +252,20 @@ impl<T: MarfTrieId> MarfConnection<T> for MarfTransaction<'_, T> {
251252
}
252253
}
253254

255+
impl<T: MarfTrieId> MarfConnection<T> for ReopenedTrieStorageConnection<'_, T> {
256+
fn with_conn<F, R>(&mut self, exec: F) -> R
257+
where
258+
F: FnOnce(&mut TrieStorageConnection<T>) -> R,
259+
{
260+
let mut conn = self.connection();
261+
exec(&mut conn)
262+
}
263+
264+
fn sqlite_conn(&self) -> &Connection {
265+
self.db_conn()
266+
}
267+
}
268+
254269
impl<T: MarfTrieId> MarfConnection<T> for MARF<T> {
255270
fn with_conn<F, R>(&mut self, exec: F) -> R
256271
where
@@ -1620,6 +1635,21 @@ impl<T: MarfTrieId> MARF<T> {
16201635
})
16211636
}
16221637

1638+
/// Build a read-only storage connection which can be used for reads without modifying the
1639+
/// calling MARF struct (i.e., the tip pointer is only changed in the connection)
1640+
/// but reusing self's existing SQLite Connection (avoiding the overhead of
1641+
/// `reopen_readonly`).
1642+
pub fn reopen_connection(&self) -> Result<ReopenedTrieStorageConnection<'_, T>, Error> {
1643+
if self.open_chain_tip.is_some() {
1644+
error!(
1645+
"MARF at {} is already in the process of writing",
1646+
&self.storage.db_path
1647+
);
1648+
return Err(Error::InProgressError);
1649+
}
1650+
self.storage.reopen_connection()
1651+
}
1652+
16231653
/// Get the root trie hash at a particular block
16241654
pub fn get_root_hash_at(&mut self, block_hash: &T) -> Result<TrieHash, Error> {
16251655
self.storage.connection().get_root_hash_at(block_hash)

stackslib/src/chainstate/stacks/index/storage.rs

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1310,6 +1310,50 @@ impl<T: MarfTrieId> TrieStorageTransientData<T> {
13101310
}
13111311
}
13121312

1313+
pub struct ReopenedTrieStorageConnection<'a, T: MarfTrieId> {
1314+
pub db_path: &'a str,
1315+
db: &'a Connection,
1316+
blobs: Option<TrieFile>,
1317+
data: TrieStorageTransientData<T>,
1318+
cache: TrieCache<T>,
1319+
bench: TrieBenchmark,
1320+
pub hash_calculation_mode: TrieHashCalculationMode,
1321+
1322+
/// row ID of a trie that represents unconfirmed state (i.e. trie state that will never become
1323+
/// part of the MARF, but nevertheless represents a persistent scratch space). If this field
1324+
/// is Some(..), then the storage connection here was used to (re-)open an unconfirmed trie
1325+
/// (via `open_unconfirmed()` or `open_block()` when `self.unconfirmed()` is `true`), or used
1326+
/// to create an unconfirmed trie (via `extend_to_unconfirmed_block()`).
1327+
unconfirmed_block_id: Option<u32>,
1328+
1329+
// used in testing in order to short-circuit block-height lookups
1330+
// when the trie struct is tested outside of marf.rs usage
1331+
#[cfg(test)]
1332+
pub test_genesis_block: Option<T>,
1333+
}
1334+
1335+
impl<'a, T: MarfTrieId> ReopenedTrieStorageConnection<'a, T> {
1336+
pub fn db_conn(&self) -> &Connection {
1337+
self.db
1338+
}
1339+
1340+
pub fn connection(&mut self) -> TrieStorageConnection<'_, T> {
1341+
TrieStorageConnection {
1342+
db: SqliteConnection::ConnRef(&self.db),
1343+
db_path: self.db_path,
1344+
data: &mut self.data,
1345+
blobs: self.blobs.as_mut(),
1346+
cache: &mut self.cache,
1347+
bench: &mut self.bench,
1348+
hash_calculation_mode: self.hash_calculation_mode,
1349+
unconfirmed_block_id: None,
1350+
1351+
#[cfg(test)]
1352+
test_genesis_block: &mut self.test_genesis_block,
1353+
}
1354+
}
1355+
}
1356+
13131357
impl<T: MarfTrieId> TrieFileStorage<T> {
13141358
pub fn connection(&mut self) -> TrieStorageConnection<'_, T> {
13151359
TrieStorageConnection {
@@ -1327,6 +1371,54 @@ impl<T: MarfTrieId> TrieFileStorage<T> {
13271371
}
13281372
}
13291373

1374+
/// Build a read-only storage connection which can be used for reads without modifying the
1375+
/// calling TrieFileStorage struct (i.e., the tip pointer is only changed in the connection)
1376+
/// but reusing the TrieFileStorage's existing SQLite Connection (avoiding the overhead of
1377+
/// `reopen_readonly`).
1378+
pub fn reopen_connection(&self) -> Result<ReopenedTrieStorageConnection<'_, T>, Error> {
1379+
let data = TrieStorageTransientData {
1380+
uncommitted_writes: self.data.uncommitted_writes.clone(),
1381+
cur_block: self.data.cur_block.clone(),
1382+
cur_block_id: self.data.cur_block_id.clone(),
1383+
1384+
read_count: 0,
1385+
read_backptr_count: 0,
1386+
read_node_count: 0,
1387+
read_leaf_count: 0,
1388+
1389+
write_count: 0,
1390+
write_node_count: 0,
1391+
write_leaf_count: 0,
1392+
1393+
trie_ancestor_hash_bytes_cache: None,
1394+
1395+
readonly: true,
1396+
unconfirmed: self.unconfirmed(),
1397+
};
1398+
// perf note: should we attempt to clone the cache
1399+
let cache = TrieCache::default();
1400+
let blobs = if self.blobs.is_some() {
1401+
Some(TrieFile::from_db_path(&self.db_path, true)?)
1402+
} else {
1403+
None
1404+
};
1405+
let bench = TrieBenchmark::new();
1406+
let hash_calculation_mode = self.hash_calculation_mode;
1407+
let unconfirmed_block_id = None;
1408+
Ok(ReopenedTrieStorageConnection {
1409+
db_path: &self.db_path,
1410+
db: &self.db,
1411+
blobs,
1412+
data,
1413+
cache,
1414+
bench,
1415+
hash_calculation_mode,
1416+
unconfirmed_block_id,
1417+
#[cfg(test)]
1418+
test_genesis_block: self.test_genesis_block.clone(),
1419+
})
1420+
}
1421+
13301422
pub fn transaction(&mut self) -> Result<TrieStorageTransaction<'_, T>, Error> {
13311423
if self.readonly() {
13321424
return Err(Error::ReadOnlyError);

stackslib/src/clarity_vm/database/marf.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -263,10 +263,7 @@ impl MarfedKV {
263263
}
264264

265265
pub fn index_conn<C>(&self, context: C) -> IndexDBConn<'_, C, StacksBlockId> {
266-
IndexDBConn {
267-
index: &self.marf,
268-
context,
269-
}
266+
IndexDBConn::new(&self.marf, context)
270267
}
271268
}
272269

stackslib/src/util_lib/db.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -613,13 +613,13 @@ impl<'a, C, T: MarfTrieId> IndexDBConn<'a, C, T> {
613613
ancestor_block_hash: &T,
614614
tip_block_hash: &T,
615615
) -> Result<Option<u64>, Error> {
616-
get_ancestor_block_height(self.index, ancestor_block_hash, tip_block_hash)
616+
get_ancestor_block_height(&self.index, ancestor_block_hash, tip_block_hash)
617617
}
618618

619619
/// Get a value from the fork index
620620
pub fn get_indexed(&self, header_hash: &T, key: &str) -> Result<Option<String>, Error> {
621-
let mut ro_index = self.index.reopen_readonly()?;
622-
get_indexed(&mut ro_index, header_hash, key)
621+
let mut connection = self.index.reopen_connection()?;
622+
get_indexed(&mut connection, header_hash, key)
623623
}
624624

625625
pub fn conn(&self) -> &DBConn {
@@ -727,7 +727,7 @@ pub fn get_ancestor_block_hash<T: MarfTrieId>(
727727
tip_block_hash: &T,
728728
) -> Result<Option<T>, Error> {
729729
assert!(block_height <= u32::MAX as u64);
730-
let mut read_only = index.reopen_readonly()?;
730+
let mut read_only = index.reopen_connection()?;
731731
let bh = read_only.get_block_at_height(block_height as u32, tip_block_hash)?;
732732
Ok(bh)
733733
}
@@ -738,7 +738,7 @@ pub fn get_ancestor_block_height<T: MarfTrieId>(
738738
ancestor_block_hash: &T,
739739
tip_block_hash: &T,
740740
) -> Result<Option<u64>, Error> {
741-
let mut read_only = index.reopen_readonly()?;
741+
let mut read_only = index.reopen_connection()?;
742742
let height_opt = read_only
743743
.get_block_height(ancestor_block_hash, tip_block_hash)?
744744
.map(|height| height as u64);

0 commit comments

Comments
 (0)