Skip to content

Commit 404f87d

Browse files
authored
Merge pull request #2266 from blockstack/fix/net-slow-lock
Fix/net slow lock
2 parents 6343af7 + 1d977c1 commit 404f87d

File tree

11 files changed

+407
-207
lines changed

11 files changed

+407
-207
lines changed

src/chainstate/stacks/db/blocks.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5109,6 +5109,12 @@ impl StacksChainState {
51095109
tx_size,
51105110
)
51115111
})
5112+
.map_err(|_| {
5113+
MemPoolRejection::NoSuchChainTip(
5114+
current_consensus_hash.clone(),
5115+
current_block.clone(),
5116+
)
5117+
})?
51125118
.expect("BUG: do not have unconfirmed state, despite being Some(..)")
51135119
} else {
51145120
Err(MemPoolRejection::BadNonces(mismatch_error))

src/chainstate/stacks/db/mod.rs

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1621,30 +1621,32 @@ impl StacksChainState {
16211621
&mut self,
16221622
burn_dbconn: &dyn BurnStateDB,
16231623
to_do: F,
1624-
) -> Option<R>
1624+
) -> Result<Option<R>, Error>
16251625
where
16261626
F: FnOnce(&mut ClarityReadOnlyConnection) -> R,
16271627
{
16281628
if let Some(ref unconfirmed) = self.unconfirmed_state.as_ref() {
16291629
if !unconfirmed.is_readable() {
1630-
return None;
1630+
return Ok(None);
16311631
}
16321632
}
16331633

16341634
let mut unconfirmed_state_opt = self.unconfirmed_state.take();
16351635
let res = if let Some(ref mut unconfirmed_state) = unconfirmed_state_opt {
1636-
let mut conn = unconfirmed_state.clarity_inst.read_only_connection(
1637-
&unconfirmed_state.unconfirmed_chain_tip,
1638-
self.db(),
1639-
burn_dbconn,
1640-
);
1636+
let mut conn = unconfirmed_state
1637+
.clarity_inst
1638+
.read_only_connection_checked(
1639+
&unconfirmed_state.unconfirmed_chain_tip,
1640+
self.db(),
1641+
burn_dbconn,
1642+
)?;
16411643
let result = to_do(&mut conn);
16421644
Some(result)
16431645
} else {
16441646
None
16451647
};
16461648
self.unconfirmed_state = unconfirmed_state_opt;
1647-
res
1649+
Ok(res)
16481650
}
16491651

16501652
/// Run to_do on the unconfirmed Clarity VM state if the tip refers to the unconfirmed state;
@@ -1655,7 +1657,7 @@ impl StacksChainState {
16551657
burn_dbconn: &dyn BurnStateDB,
16561658
parent_tip: &StacksBlockId,
16571659
to_do: F,
1658-
) -> Option<R>
1660+
) -> Result<Option<R>, Error>
16591661
where
16601662
F: FnOnce(&mut ClarityReadOnlyConnection) -> R,
16611663
{
@@ -1669,7 +1671,7 @@ impl StacksChainState {
16691671
if unconfirmed {
16701672
self.with_read_only_unconfirmed_clarity_tx(burn_dbconn, to_do)
16711673
} else {
1672-
self.with_read_only_clarity_tx(burn_dbconn, parent_tip, to_do)
1674+
Ok(self.with_read_only_clarity_tx(burn_dbconn, parent_tip, to_do))
16731675
}
16741676
}
16751677

@@ -1708,8 +1710,6 @@ impl StacksChainState {
17081710
}
17091711

17101712
/// Open a Clarity transaction against this chainstate's unconfirmed state, if it exists.
1711-
/// This marks the unconfirmed chainstate as "dirty" so that no future queries against it can
1712-
/// happen.
17131713
pub fn begin_unconfirmed<'a>(
17141714
&'a mut self,
17151715
burn_dbconn: &'a dyn BurnStateDB,
@@ -1721,8 +1721,6 @@ impl StacksChainState {
17211721
return None;
17221722
}
17231723

1724-
unconfirmed.set_dirty(true);
1725-
17261724
Some(StacksChainState::chainstate_begin_unconfirmed(
17271725
conf,
17281726
self.state_index.sqlite_conn(),

src/chainstate/stacks/db/unconfirmed.rs

Lines changed: 62 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,17 +41,20 @@ use vm::database::NULL_HEADER_DB;
4141

4242
use vm::costs::ExecutionCost;
4343

44+
pub type UnconfirmedTxMap = HashMap<Txid, (StacksTransaction, BlockHeaderHash, u16)>;
45+
4446
pub struct UnconfirmedState {
4547
pub confirmed_chain_tip: StacksBlockId,
4648
pub unconfirmed_chain_tip: StacksBlockId,
4749
pub clarity_inst: ClarityInstance,
48-
pub mined_txs: HashMap<Txid, (StacksTransaction, BlockHeaderHash, u16)>,
50+
pub mined_txs: UnconfirmedTxMap,
4951
pub cost_so_far: ExecutionCost,
5052
pub bytes_so_far: u64,
5153

5254
pub last_mblock: Option<StacksMicroblockHeader>,
5355
pub last_mblock_seq: u16,
5456

57+
readonly: bool,
5558
dirty: bool,
5659
}
5760

@@ -68,13 +71,40 @@ impl UnconfirmedState {
6871
confirmed_chain_tip: tip,
6972
unconfirmed_chain_tip: unconfirmed_tip,
7073
clarity_inst: clarity_instance,
71-
mined_txs: HashMap::new(),
74+
mined_txs: UnconfirmedTxMap::new(),
75+
cost_so_far: ExecutionCost::zero(),
76+
bytes_so_far: 0,
77+
78+
last_mblock: None,
79+
last_mblock_seq: 0,
80+
81+
readonly: false,
82+
dirty: false,
83+
})
84+
}
85+
86+
/// Make a new unconfirmed state, but don't do anything with it yet, and deny refreshes.
87+
fn new_readonly(
88+
chainstate: &StacksChainState,
89+
tip: StacksBlockId,
90+
) -> Result<UnconfirmedState, Error> {
91+
let marf = MarfedKV::open_unconfirmed(&chainstate.clarity_state_index_root, None)?;
92+
93+
let clarity_instance = ClarityInstance::new(marf, chainstate.block_limit.clone());
94+
let unconfirmed_tip = MARF::make_unconfirmed_chain_tip(&tip);
95+
96+
Ok(UnconfirmedState {
97+
confirmed_chain_tip: tip,
98+
unconfirmed_chain_tip: unconfirmed_tip,
99+
clarity_inst: clarity_instance,
100+
mined_txs: UnconfirmedTxMap::new(),
72101
cost_so_far: ExecutionCost::zero(),
73102
bytes_so_far: 0,
74103

75104
last_mblock: None,
76105
last_mblock_seq: 0,
77106

107+
readonly: true,
78108
dirty: false,
79109
})
80110
}
@@ -91,7 +121,7 @@ impl UnconfirmedState {
91121
mblocks: Vec<StacksMicroblock>,
92122
) -> Result<(u128, u128, Vec<StacksTransactionReceipt>), Error> {
93123
if self.last_mblock_seq == u16::max_value() {
94-
// drop them
124+
// drop them -- nothing to do
95125
return Ok((0, 0, vec![]));
96126
}
97127

@@ -108,7 +138,7 @@ impl UnconfirmedState {
108138
let mut total_fees = 0;
109139
let mut total_burns = 0;
110140
let mut all_receipts = vec![];
111-
let mut mined_txs = HashMap::new();
141+
let mut mined_txs = UnconfirmedTxMap::new();
112142
let new_cost;
113143
let mut new_bytes = 0;
114144

@@ -195,7 +225,7 @@ impl UnconfirmedState {
195225
Ok((total_fees, total_burns, all_receipts))
196226
}
197227

198-
/// Load up Stacks microblock stream to process
228+
/// Load up the Stacks microblock stream to process, composed of only the new microblocks
199229
fn load_child_microblocks(
200230
&self,
201231
chainstate: &StacksChainState,
@@ -222,6 +252,11 @@ impl UnconfirmedState {
222252
chainstate: &StacksChainState,
223253
burn_dbconn: &dyn BurnStateDB,
224254
) -> Result<(u128, u128, Vec<StacksTransactionReceipt>), Error> {
255+
assert!(
256+
!self.readonly,
257+
"BUG: code tried to write unconfirmed state to a read-only instance"
258+
);
259+
225260
if self.last_mblock_seq == u16::max_value() {
226261
// no-op
227262
return Ok((0, 0, vec![]));
@@ -235,7 +270,7 @@ impl UnconfirmedState {
235270

236271
/// Is there any state to read?
237272
pub fn is_readable(&self) -> bool {
238-
self.has_data() && !self.dirty
273+
(self.has_data() || self.readonly) && !self.dirty
239274
}
240275

241276
/// Can we write to this unconfirmed state?
@@ -395,6 +430,25 @@ impl StacksChainState {
395430
res
396431
}
397432

433+
/// Instantiate a read-only view of unconfirmed state.
434+
/// Use from a dedicated chainstate handle that will only do read-only operations on it (such
435+
/// as the p2p network thread)
436+
pub fn refresh_unconfirmed_readonly(
437+
&mut self,
438+
canonical_tip: StacksBlockId,
439+
) -> Result<(), Error> {
440+
if let Some(ref unconfirmed) = self.unconfirmed_state {
441+
assert!(
442+
unconfirmed.readonly,
443+
"BUG: tried to replace a read/write unconfirmed state instance"
444+
);
445+
}
446+
447+
let unconfirmed = UnconfirmedState::new_readonly(self, canonical_tip)?;
448+
self.unconfirmed_state = Some(unconfirmed);
449+
Ok(())
450+
}
451+
398452
pub fn set_unconfirmed_dirty(&mut self, dirty: bool) {
399453
if let Some(ref mut unconfirmed) = self.unconfirmed_state.as_mut() {
400454
unconfirmed.dirty = dirty;
@@ -632,6 +686,7 @@ mod test {
632686
clarity_db.get_account_stx_balance(&recv_addr.into())
633687
})
634688
})
689+
.unwrap()
635690
.unwrap();
636691
peer.sortdb = Some(sortdb);
637692

@@ -862,6 +917,7 @@ mod test {
862917
clarity_db.get_account_stx_balance(&recv_addr.into())
863918
})
864919
})
920+
.unwrap()
865921
.unwrap();
866922
peer.sortdb = Some(sortdb);
867923

src/chainstate/stacks/index/marf.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2653,7 +2653,7 @@ mod test {
26532653
m.commit().unwrap();
26542654
let flush_end_time = get_epoch_time_ms();
26552655

2656-
debug!(
2656+
eprintln!(
26572657
"Inserted {} in {} (1 insert = {} ms). Processed {} keys in {} ms (flush = {} ms)",
26582658
i,
26592659
end_time - start_time,
@@ -2703,7 +2703,7 @@ mod test {
27032703

27042704
end_time = get_epoch_time_ms();
27052705

2706-
debug!(
2706+
eprintln!(
27072707
"Got {} in {} (1 get = {} ms)",
27082708
i,
27092709
end_time - start_time,

src/net/p2p.rs

Lines changed: 6 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -3939,9 +3939,7 @@ impl PeerNetwork {
39393939
// update burnchain snapshot if we need to (careful -- it's expensive)
39403940
let sn = SortitionDB::get_canonical_burn_chain_tip(&sortdb.conn())?;
39413941
let mut ret: HashMap<NeighborKey, Vec<StacksMessage>> = HashMap::new();
3942-
if sn.block_height > self.chain_view.burn_block_height
3943-
|| sn.burn_header_hash != self.antientropy_last_burnchain_tip
3944-
{
3942+
if sn.block_height > self.chain_view.burn_block_height {
39453943
debug!(
39463944
"{:?}: load chain view for burn block {}",
39473945
&self.local_peer, sn.block_height
@@ -3955,7 +3953,9 @@ impl PeerNetwork {
39553953
self.hint_sync_invs();
39563954
self.hint_download_rescan();
39573955
self.chain_view = new_chain_view;
3956+
}
39583957

3958+
if sn.burn_header_hash != self.antientropy_last_burnchain_tip {
39593959
// try processing previously-buffered messages (best-effort)
39603960
let buffered_messages = mem::replace(&mut self.pending_messages, HashMap::new());
39613961
ret = self.handle_unsolicited_messages(sortdb, chainstate, buffered_messages, false)?;
@@ -4104,26 +4104,6 @@ impl PeerNetwork {
41044104
Ok(())
41054105
}
41064106

4107-
/// Set up the unconfirmed chain state off of the canonical chain tip.
4108-
pub fn setup_unconfirmed_state(
4109-
chainstate: &mut StacksChainState,
4110-
sortdb: &SortitionDB,
4111-
) -> Result<(), Error> {
4112-
let (canonical_consensus_hash, canonical_block_hash) =
4113-
SortitionDB::get_canonical_stacks_chain_tip_hash(sortdb.conn())?;
4114-
let canonical_tip = StacksBlockHeader::make_index_block_hash(
4115-
&canonical_consensus_hash,
4116-
&canonical_block_hash,
4117-
);
4118-
// setup unconfirmed state off of this tip
4119-
debug!(
4120-
"Reload unconfirmed state off of {}/{}",
4121-
&canonical_consensus_hash, &canonical_block_hash
4122-
);
4123-
chainstate.reload_unconfirmed_state(&sortdb.index_conn(), canonical_tip)?;
4124-
Ok(())
4125-
}
4126-
41274107
/// Store a single transaction
41284108
/// Return true if stored; false if it was a dup.
41294109
/// Has to be done here, since only the p2p network has the unconfirmed state.
@@ -4153,7 +4133,7 @@ impl PeerNetwork {
41534133

41544134
/// Store all inbound transactions, and return the ones that we actually stored so they can be
41554135
/// relayed.
4156-
fn store_transactions(
4136+
pub fn store_transactions(
41574137
mempool: &mut MemPoolDB,
41584138
chainstate: &mut StacksChainState,
41594139
sortdb: &SortitionDB,
@@ -4184,6 +4164,8 @@ impl PeerNetwork {
41844164
}
41854165
}
41864166

4167+
// (HTTP-uploaded transactions are already in the mempool)
4168+
41874169
network_result.pushed_transactions.extend(ret);
41884170
Ok(())
41894171
}
@@ -4269,24 +4251,6 @@ impl PeerNetwork {
42694251
p2p_poll_state,
42704252
)?;
42714253

4272-
if let Err(e) =
4273-
PeerNetwork::store_transactions(mempool, chainstate, sortdb, &mut network_result)
4274-
{
4275-
warn!("Failed to store transactions: {:?}", &e);
4276-
}
4277-
4278-
if let Err(e) = PeerNetwork::setup_unconfirmed_state(chainstate, sortdb) {
4279-
if let net_error::ChainstateError(ref err_msg) = e {
4280-
if err_msg == "Stacks chainstate error: NoSuchBlockError" {
4281-
trace!("Failed to instantiate unconfirmed state: {:?}", &e);
4282-
} else {
4283-
warn!("Failed to instantiate unconfirmed state: {:?}", &e);
4284-
}
4285-
} else {
4286-
warn!("Failed to instantiate unconfirmed state: {:?}", &e);
4287-
}
4288-
}
4289-
42904254
debug!("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< End Network Dispatch <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<");
42914255
Ok(network_result)
42924256
}

0 commit comments

Comments
 (0)