Skip to content

Commit 106db82

Browse files
committed
[Hot State] Have all checkpoint indices in StateUpdateRefs
1 parent c032887 commit 106db82

File tree

4 files changed

+266
-49
lines changed

4 files changed

+266
-49
lines changed

execution/executor-types/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ serde = { workspace = true }
3333
thiserror = { workspace = true }
3434

3535
[dev-dependencies]
36-
aptos-types = { workspace = true, features = ["fuzzing"] }
36+
aptos-types = { workspace = true, features = ["fuzzing", "testing"] }
3737

3838
[features]
3939
default = []

execution/executor-types/src/transactions_with_output.rs

Lines changed: 254 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use anyhow::{ensure, Result};
66
use aptos_metrics_core::TimerHelper;
77
use aptos_storage_interface::state_store::state_update_refs::StateUpdateRefs;
88
use aptos_types::transaction::{PersistedAuxiliaryInfo, Transaction, TransactionOutput, Version};
9-
use itertools::izip;
9+
use itertools::{izip, Itertools};
1010
use std::{
1111
fmt::{Debug, Formatter},
1212
ops::Deref,
@@ -67,6 +67,16 @@ impl TransactionsWithOutput {
6767
self.persisted_auxiliary_infos.iter()
6868
)
6969
}
70+
71+
pub fn last(&self) -> Option<(&Transaction, &TransactionOutput, &PersistedAuxiliaryInfo)> {
72+
self.transactions.last().map(|txn| {
73+
(
74+
txn,
75+
self.transaction_outputs.last().unwrap(),
76+
self.persisted_auxiliary_infos.last().unwrap(),
77+
)
78+
})
79+
}
7080
}
7181

7282
#[ouroboros::self_referencing]
@@ -82,10 +92,13 @@ impl TransactionsToKeep {
8292
pub fn index(
8393
first_version: Version,
8494
transactions_with_output: TransactionsWithOutput,
85-
is_reconfig: bool,
95+
must_be_block: bool,
8696
) -> Self {
8797
let _timer = TIMER.timer_with(&["transactions_to_keep__index"]);
8898

99+
let (all_checkpoint_indices, is_reconfig) =
100+
Self::get_all_checkpoint_indices(&transactions_with_output, must_be_block);
101+
89102
TransactionsToKeepBuilder {
90103
transactions_with_output,
91104
is_reconfig,
@@ -94,10 +107,7 @@ impl TransactionsToKeep {
94107
.transaction_outputs
95108
.iter()
96109
.map(TransactionOutput::write_set);
97-
let last_checkpoint_index = Self::get_last_checkpoint_index(
98-
is_reconfig,
99-
&transactions_with_output.transactions,
100-
);
110+
let last_checkpoint_index = all_checkpoint_indices.last().copied();
101111
StateUpdateRefs::index_write_sets(
102112
first_version,
103113
write_sets,
@@ -114,18 +124,17 @@ impl TransactionsToKeep {
114124
transactions: Vec<Transaction>,
115125
transaction_outputs: Vec<TransactionOutput>,
116126
persisted_auxiliary_infos: Vec<PersistedAuxiliaryInfo>,
117-
is_reconfig: bool,
118127
) -> Self {
119128
let txns_with_output = TransactionsWithOutput::new(
120129
transactions,
121130
transaction_outputs,
122131
persisted_auxiliary_infos,
123132
);
124-
Self::index(first_version, txns_with_output, is_reconfig)
133+
Self::index(first_version, txns_with_output, false)
125134
}
126135

127136
pub fn new_empty() -> Self {
128-
Self::make(0, vec![], vec![], vec![], false)
137+
Self::make(0, vec![], vec![], vec![])
129138
}
130139

131140
pub fn new_dummy_success(txns: Vec<Transaction>) -> Self {
@@ -135,9 +144,10 @@ impl TransactionsToKeep {
135144
transaction_index: i as u32,
136145
})
137146
.collect();
138-
Self::make(0, txns, txn_outputs, persisted_auxiliary_infos, false)
147+
Self::make(0, txns, txn_outputs, persisted_auxiliary_infos)
139148
}
140149

150+
/// Whether the last txn of this block/chunk is reconfig.
141151
pub fn is_reconfig(&self) -> bool {
142152
*self.borrow_is_reconfig()
143153
}
@@ -162,16 +172,33 @@ impl TransactionsToKeep {
162172
}
163173
}
164174

165-
fn get_last_checkpoint_index(is_reconfig: bool, transactions: &[Transaction]) -> Option<usize> {
166-
let _timer = TIMER.timer_with(&["get_last_checkpoint_index"]);
175+
fn get_all_checkpoint_indices(
176+
transactions_with_output: &TransactionsWithOutput,
177+
must_be_block: bool,
178+
) -> (Vec<usize>, bool) {
179+
let _timer = TIMER.timer_with(&["get_all_checkpoint_indices"]);
180+
181+
let (last_txn, last_output) = match transactions_with_output.last() {
182+
Some((txn, output, _)) => (txn, output),
183+
None => return (Vec::new(), false),
184+
};
185+
let is_reconfig = last_output.has_new_epoch_event();
167186

168-
if is_reconfig {
169-
return Some(transactions.len() - 1);
187+
if must_be_block {
188+
assert!(last_txn.is_non_reconfig_block_ending() || is_reconfig);
189+
return (vec![transactions_with_output.len() - 1], is_reconfig);
170190
}
171191

172-
transactions
192+
let all = transactions_with_output
193+
.transactions
173194
.iter()
174-
.rposition(Transaction::is_non_reconfig_block_ending)
195+
.zip_eq(transactions_with_output.transaction_outputs.iter())
196+
.enumerate()
197+
.filter_map(|(idx, (txn, output))| {
198+
(txn.is_non_reconfig_block_ending() || output.has_new_epoch_event()).then_some(idx)
199+
})
200+
.collect();
201+
(all, is_reconfig)
175202
}
176203

177204
pub fn ensure_at_most_one_checkpoint(&self) -> Result<()> {
@@ -217,3 +244,214 @@ impl Debug for TransactionsToKeep {
217244
.finish()
218245
}
219246
}
247+
248+
#[cfg(test)]
249+
mod tests {
250+
use super::{TransactionsToKeep, TransactionsWithOutput};
251+
use aptos_crypto::{ed25519::Ed25519PrivateKey, HashValue, PrivateKey, Uniform};
252+
use aptos_types::{
253+
account_address::AccountAddress,
254+
contract_event::ContractEvent,
255+
test_helpers::transaction_test_helpers::get_test_signed_txn,
256+
transaction::{
257+
ExecutionStatus, PersistedAuxiliaryInfo, Transaction, TransactionAuxiliaryData,
258+
TransactionOutput, TransactionStatus,
259+
},
260+
write_set::WriteSet,
261+
};
262+
263+
fn dummy_txn() -> Transaction {
264+
let private_key = Ed25519PrivateKey::generate_for_testing();
265+
let public_key = private_key.public_key();
266+
let sender = AccountAddress::ZERO;
267+
Transaction::UserTransaction(get_test_signed_txn(
268+
sender,
269+
0,
270+
&private_key,
271+
public_key,
272+
None,
273+
))
274+
}
275+
276+
fn ckpt_txn() -> Transaction {
277+
Transaction::StateCheckpoint(HashValue::zero())
278+
}
279+
280+
fn default_output() -> TransactionOutput {
281+
TransactionOutput::new(
282+
WriteSet::default(),
283+
vec![],
284+
0,
285+
TransactionStatus::Keep(ExecutionStatus::Success),
286+
TransactionAuxiliaryData::default(),
287+
)
288+
}
289+
290+
fn output_with_reconfig() -> TransactionOutput {
291+
let reconfig_event = ContractEvent::new_v2_with_type_tag_str(
292+
"0x1::reconfiguration::NewEpochEvent",
293+
b"".to_vec(),
294+
);
295+
TransactionOutput::new(
296+
WriteSet::default(),
297+
vec![reconfig_event],
298+
0,
299+
TransactionStatus::Keep(ExecutionStatus::Success),
300+
TransactionAuxiliaryData::default(),
301+
)
302+
}
303+
304+
fn default_aux_info() -> PersistedAuxiliaryInfo {
305+
PersistedAuxiliaryInfo::None
306+
}
307+
308+
#[test]
309+
fn test_regular_block_without_reconfig() {
310+
let txns = vec![dummy_txn(), dummy_txn(), ckpt_txn()];
311+
let outputs = vec![default_output(), default_output(), default_output()];
312+
let aux_infos = vec![default_aux_info(), default_aux_info(), default_aux_info()];
313+
let txn_with_outputs = TransactionsWithOutput::new(txns, outputs, aux_infos);
314+
315+
{
316+
let (all_ckpt_indices, is_reconfig) =
317+
TransactionsToKeep::get_all_checkpoint_indices(&txn_with_outputs, true);
318+
assert_eq!(all_ckpt_indices, vec![2]);
319+
assert!(!is_reconfig);
320+
}
321+
322+
{
323+
let (all_ckpt_indices, is_reconfig) =
324+
TransactionsToKeep::get_all_checkpoint_indices(&txn_with_outputs, false);
325+
assert_eq!(all_ckpt_indices, vec![2]);
326+
assert!(!is_reconfig);
327+
}
328+
}
329+
330+
#[test]
331+
fn test_regular_block_with_reconfig() {
332+
let txns = vec![dummy_txn(), dummy_txn(), dummy_txn()];
333+
let outputs = vec![default_output(), default_output(), output_with_reconfig()];
334+
let aux_infos = vec![default_aux_info(), default_aux_info(), default_aux_info()];
335+
let txn_with_outputs = TransactionsWithOutput::new(txns, outputs, aux_infos);
336+
337+
{
338+
let (all_ckpt_indices, is_reconfig) =
339+
TransactionsToKeep::get_all_checkpoint_indices(&txn_with_outputs, true);
340+
assert_eq!(all_ckpt_indices, vec![2]);
341+
assert!(is_reconfig);
342+
}
343+
344+
{
345+
let (all_ckpt_indices, is_reconfig) =
346+
TransactionsToKeep::get_all_checkpoint_indices(&txn_with_outputs, false);
347+
assert_eq!(all_ckpt_indices, vec![2]);
348+
assert!(is_reconfig);
349+
}
350+
}
351+
352+
#[test]
353+
fn test_chunk_with_no_ckpt() {
354+
let txns = vec![dummy_txn(), dummy_txn(), dummy_txn()];
355+
let outputs = vec![default_output(), default_output(), default_output()];
356+
let aux_infos = vec![default_aux_info(), default_aux_info(), default_aux_info()];
357+
let txn_with_outputs = TransactionsWithOutput::new(txns, outputs, aux_infos);
358+
359+
let (all_ckpt_indices, is_reconfig) =
360+
TransactionsToKeep::get_all_checkpoint_indices(&txn_with_outputs, false);
361+
assert!(all_ckpt_indices.is_empty());
362+
assert!(!is_reconfig);
363+
}
364+
365+
#[test]
366+
fn test_chunk_with_ckpts_no_reconfig() {
367+
let txns = vec![
368+
dummy_txn(),
369+
ckpt_txn(),
370+
dummy_txn(),
371+
ckpt_txn(),
372+
dummy_txn(),
373+
];
374+
let outputs = vec![
375+
default_output(),
376+
default_output(),
377+
default_output(),
378+
default_output(),
379+
default_output(),
380+
];
381+
let aux_infos = vec![
382+
default_aux_info(),
383+
default_aux_info(),
384+
default_aux_info(),
385+
default_aux_info(),
386+
default_aux_info(),
387+
];
388+
let txn_with_outputs = TransactionsWithOutput::new(txns, outputs, aux_infos);
389+
390+
let (all_ckpt_indices, is_reconfig) =
391+
TransactionsToKeep::get_all_checkpoint_indices(&txn_with_outputs, false);
392+
assert_eq!(all_ckpt_indices, vec![1, 3]);
393+
assert!(!is_reconfig);
394+
}
395+
396+
#[test]
397+
fn test_chunk_with_ckpts_with_reconfig_in_the_middle() {
398+
let txns = vec![
399+
dummy_txn(),
400+
ckpt_txn(),
401+
dummy_txn(),
402+
dummy_txn(),
403+
dummy_txn(),
404+
];
405+
let outputs = vec![
406+
default_output(),
407+
default_output(),
408+
default_output(),
409+
output_with_reconfig(),
410+
default_output(),
411+
];
412+
let aux_infos = vec![
413+
default_aux_info(),
414+
default_aux_info(),
415+
default_aux_info(),
416+
default_aux_info(),
417+
default_aux_info(),
418+
];
419+
let txn_with_outputs = TransactionsWithOutput::new(txns, outputs, aux_infos);
420+
421+
let (all_ckpt_indices, is_reconfig) =
422+
TransactionsToKeep::get_all_checkpoint_indices(&txn_with_outputs, false);
423+
assert_eq!(all_ckpt_indices, vec![1, 3]);
424+
assert!(!is_reconfig);
425+
}
426+
427+
#[test]
428+
fn test_chunk_with_ckpts_with_reconfig_at_end() {
429+
let txns = vec![
430+
dummy_txn(),
431+
ckpt_txn(),
432+
dummy_txn(),
433+
dummy_txn(),
434+
dummy_txn(),
435+
];
436+
let outputs = vec![
437+
default_output(),
438+
default_output(),
439+
default_output(),
440+
default_output(),
441+
output_with_reconfig(),
442+
];
443+
let aux_infos = vec![
444+
default_aux_info(),
445+
default_aux_info(),
446+
default_aux_info(),
447+
default_aux_info(),
448+
default_aux_info(),
449+
];
450+
let txn_with_outputs = TransactionsWithOutput::new(txns, outputs, aux_infos);
451+
452+
let (all_ckpt_indices, is_reconfig) =
453+
TransactionsToKeep::get_all_checkpoint_indices(&txn_with_outputs, false);
454+
assert_eq!(all_ckpt_indices, vec![1, 4]);
455+
assert!(is_reconfig);
456+
}
457+
}

0 commit comments

Comments
 (0)