change meaning of none in blockchain cache (#2973)

This commit is contained in:
Svyatoslav Nikolsky
2019-06-30 18:54:51 +03:00
committed by Gavin Wood
parent 18be937cb5
commit 83e014c214
3 changed files with 224 additions and 263 deletions
+186 -221
View File
@@ -163,13 +163,15 @@ impl<Block: BlockT, T: CacheItemT, S: Storage<Block, T>> ListCache<Block, T, S>
};
match head {
Some(head) => head.search_best_before(&self.storage, at.number, true)
.map(|e| e.and_then(|e| e.0.value)),
Some(head) => head.search_best_before(&self.storage, at.number)
.map(|e| e.map(|e| e.0.value)),
None => Ok(None),
}
}
/// When new block is inserted into database.
///
/// None passed as value means that the value has not changed since previous block.
pub fn on_block_insert<Tx: StorageTransaction<Block, T>>(
&self,
tx: &mut Tx,
@@ -191,6 +193,11 @@ impl<Block: BlockT, T: CacheItemT, S: Storage<Block, T>> ListCache<Block, T, S>
if !is_final {
let mut fork_and_action = None;
// when value hasn't changed and block isn't final, there's nothing we need to do
if value.is_none() {
return Ok(None);
}
// first: try to find fork that is known to has the best block we're appending to
for (index, fork) in self.unfinalized.iter().enumerate() {
if fork.try_append(&parent) {
@@ -231,7 +238,7 @@ impl<Block: BlockT, T: CacheItemT, S: Storage<Block, T>> ListCache<Block, T, S>
// it is possible that we're inserting extra (but still required) fork here
let new_storage_entry = StorageEntry {
prev_valid_from: Some(prev_valid_from),
value,
value: value.expect("chcecked abpve that !value.is_none(); qed"),
};
tx.insert_storage_entry(&block, &new_storage_entry);
@@ -250,7 +257,10 @@ impl<Block: BlockT, T: CacheItemT, S: Storage<Block, T>> ListCache<Block, T, S>
let new_storage_entry = match self.best_finalized_entry.as_ref() {
Some(best_finalized_entry) => best_finalized_entry.try_update(value),
None if value.is_some() => Some(StorageEntry { prev_valid_from: None, value }),
None if value.is_some() => Some(StorageEntry {
prev_valid_from: None,
value: value.expect("value.is_some(); qed"),
}),
None => None,
};
@@ -378,8 +388,12 @@ impl<Block: BlockT, T: CacheItemT, S: Storage<Block, T>> ListCache<Block, T, S>
});
// destroy 'fork' ending with previous entry
Fork { best_block: None, head: Entry { valid_from: first_entry_to_truncate, value: None } }
.destroy(&self.storage, tx, None)
destroy_fork(
first_entry_to_truncate,
&self.storage,
tx,
None,
)
};
if let Err(error) = do_pruning() {
@@ -491,25 +505,40 @@ impl<Block: BlockT, T: CacheItemT> Fork<Block, T> {
tx: &mut Tx,
best_finalized_block: Option<NumberFor<Block>>,
) -> ClientResult<()> {
let mut current = self.head.valid_from.clone();
loop {
// optionally: deletion stops when we found entry at finalized block
if let Some(best_finalized_block) = best_finalized_block {
if chain::is_finalized_block(storage, &current, best_finalized_block)? {
return Ok(());
}
destroy_fork(
self.head.valid_from.clone(),
storage,
tx,
best_finalized_block,
)
}
}
/// Destroy fork by deleting all unfinalized entries.
pub fn destroy_fork<Block: BlockT, T: CacheItemT, S: Storage<Block, T>, Tx: StorageTransaction<Block, T>>(
head_valid_from: ComplexBlockId<Block>,
storage: &S,
tx: &mut Tx,
best_finalized_block: Option<NumberFor<Block>>,
) -> ClientResult<()> {
let mut current = head_valid_from;
loop {
// optionally: deletion stops when we found entry at finalized block
if let Some(best_finalized_block) = best_finalized_block {
if chain::is_finalized_block(storage, &current, best_finalized_block)? {
return Ok(());
}
// read pointer to previous entry
let entry = storage.require_entry(&current)?;
tx.remove_storage_entry(&current);
// deletion stops when there are no more entries in the list
current = match entry.prev_valid_from {
Some(prev_valid_from) => prev_valid_from,
None => return Ok(()),
};
}
// read pointer to previous entry
let entry = storage.require_entry(&current)?;
tx.remove_storage_entry(&current);
// deletion stops when there are no more entries in the list
current = match entry.prev_valid_from {
Some(prev_valid_from) => prev_valid_from,
None => return Ok(()),
};
}
}
@@ -639,24 +668,14 @@ pub mod tests {
// ----------> [100]
assert_eq!(ListCache::<_, u64, _>::new(DummyStorage::new(), 1024, test_id(100))
.value_at_block(&test_id(50)).unwrap(), None);
// when block is earlier than best finalized block AND it is finalized AND value is empty
// [30] ---- 50 ---> [100]
assert_eq!(ListCache::new(
DummyStorage::new()
.with_meta(Some(test_id(100)), Vec::new())
.with_id(50, H256::from_low_u64_be(50))
.with_entry(test_id(100), StorageEntry { prev_valid_from: Some(test_id(30)), value: Some(100) })
.with_entry(test_id(30), StorageEntry { prev_valid_from: None, value: None }),
1024, test_id(100)
).value_at_block(&test_id(50)).unwrap(), None);
// when block is earlier than best finalized block AND it is finalized AND value is some
// [30] ---- 50 ---> [100]
assert_eq!(ListCache::new(
DummyStorage::new()
.with_meta(Some(test_id(100)), Vec::new())
.with_id(50, H256::from_low_u64_be(50))
.with_entry(test_id(100), StorageEntry { prev_valid_from: Some(test_id(30)), value: Some(100) })
.with_entry(test_id(30), StorageEntry { prev_valid_from: None, value: Some(30) }),
.with_entry(test_id(100), StorageEntry { prev_valid_from: Some(test_id(30)), value: 100 })
.with_entry(test_id(30), StorageEntry { prev_valid_from: None, value: 30 }),
1024, test_id(100)
).value_at_block(&test_id(50)).unwrap(), Some(30));
// when block is the best finalized block AND value is some
@@ -665,8 +684,8 @@ pub mod tests {
DummyStorage::new()
.with_meta(Some(test_id(100)), Vec::new())
.with_id(100, H256::from_low_u64_be(100))
.with_entry(test_id(100), StorageEntry { prev_valid_from: Some(test_id(30)), value: Some(100) })
.with_entry(test_id(30), StorageEntry { prev_valid_from: None, value: Some(30) }),
.with_entry(test_id(100), StorageEntry { prev_valid_from: Some(test_id(30)), value: 100 })
.with_entry(test_id(30), StorageEntry { prev_valid_from: None, value: 30 }),
1024, test_id(100)
).value_at_block(&test_id(100)).unwrap(), Some(100));
// when block is parallel to the best finalized block
@@ -676,45 +695,21 @@ pub mod tests {
DummyStorage::new()
.with_meta(Some(test_id(100)), Vec::new())
.with_id(50, H256::from_low_u64_be(50))
.with_entry(test_id(100), StorageEntry { prev_valid_from: Some(test_id(30)), value: Some(100) })
.with_entry(test_id(30), StorageEntry { prev_valid_from: None, value: Some(30) }),
.with_entry(test_id(100), StorageEntry { prev_valid_from: Some(test_id(30)), value: 100 })
.with_entry(test_id(30), StorageEntry { prev_valid_from: None, value: 30 }),
1024, test_id(100)
).value_at_block(&ComplexBlockId::new(H256::from_low_u64_be(2), 100)).unwrap(), None);
// when block is later than last finalized block AND there are no forks AND finalized value is None
// ---> [100] --- 200
assert_eq!(ListCache::<_, u64, _>::new(
DummyStorage::new()
.with_meta(Some(test_id(100)), Vec::new())
.with_id(50, H256::from_low_u64_be(50))
.with_entry(test_id(100), StorageEntry { prev_valid_from: Some(test_id(30)), value: None }),
1024, test_id(100)
).value_at_block(&test_id(200)).unwrap(), None);
// when block is later than last finalized block AND there are no forks AND finalized value is Some
// ---> [100] --- 200
assert_eq!(ListCache::new(
DummyStorage::new()
.with_meta(Some(test_id(100)), Vec::new())
.with_id(50, H256::from_low_u64_be(50))
.with_entry(test_id(100), StorageEntry { prev_valid_from: Some(test_id(30)), value: Some(100) }),
.with_entry(test_id(100), StorageEntry { prev_valid_from: Some(test_id(30)), value: 100 }),
1024, test_id(100)
).value_at_block(&test_id(200)).unwrap(), Some(100));
// when block is later than last finalized block AND there are no matching forks
// AND block is connected to finalized block AND finalized value is None
// --- 3
// ---> [2] /---------> [4]
assert_eq!(ListCache::new(
DummyStorage::new()
.with_meta(Some(correct_id(2)), vec![correct_id(4)])
.with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: None })
.with_entry(correct_id(4), StorageEntry { prev_valid_from: Some(correct_id(2)), value: Some(4) })
.with_header(test_header(2))
.with_header(test_header(3))
.with_header(test_header(4))
.with_header(fork_header(0, 2, 3)),
1024, test_id(2)
).value_at_block(&fork_id(0, 2, 3)).unwrap(), None);
// when block is later than last finalized block AND there are no matching forks
// AND block is connected to finalized block AND finalized value is Some
// --- 3
@@ -722,8 +717,8 @@ pub mod tests {
assert_eq!(ListCache::new(
DummyStorage::new()
.with_meta(Some(correct_id(2)), vec![correct_id(4)])
.with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: Some(2) })
.with_entry(correct_id(4), StorageEntry { prev_valid_from: Some(correct_id(2)), value: Some(4) })
.with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: 2 })
.with_entry(correct_id(4), StorageEntry { prev_valid_from: Some(correct_id(2)), value: 4 })
.with_header(test_header(2))
.with_header(test_header(3))
.with_header(test_header(4))
@@ -737,8 +732,8 @@ pub mod tests {
assert_eq!(ListCache::new(
DummyStorage::new()
.with_meta(Some(correct_id(2)), vec![correct_id(4)])
.with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: Some(2) })
.with_entry(correct_id(4), StorageEntry { prev_valid_from: Some(correct_id(2)), value: Some(4) })
.with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: 2 })
.with_entry(correct_id(4), StorageEntry { prev_valid_from: Some(correct_id(2)), value: 4 })
.with_header(test_header(1))
.with_header(test_header(2))
.with_header(test_header(3))
@@ -754,52 +749,12 @@ pub mod tests {
assert_eq!(ListCache::new(
DummyStorage::new()
.with_meta(Some(correct_id(2)), vec![correct_id(4)])
.with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: Some(2) })
.with_entry(correct_id(4), StorageEntry { prev_valid_from: Some(correct_id(2)), value: Some(4) })
.with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: 2 })
.with_entry(correct_id(4), StorageEntry { prev_valid_from: Some(correct_id(2)), value: 4 })
.with_header(test_header(4))
.with_header(test_header(5)),
1024, test_id(2)
).value_at_block(&correct_id(5)).unwrap(), Some(4));
// when block is later than last finalized block AND it appends to unfinalized fork from the end
// AND unfinalized value is None
// ---> [2] ---> [4] ---> 5
assert_eq!(ListCache::new(
DummyStorage::new()
.with_meta(Some(correct_id(2)), vec![correct_id(4)])
.with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: Some(2) })
.with_entry(correct_id(4), StorageEntry { prev_valid_from: Some(correct_id(2)), value: None })
.with_header(test_header(4))
.with_header(test_header(5)),
1024, test_id(2)
).value_at_block(&correct_id(5)).unwrap(), None);
// when block is later than last finalized block AND it fits to the middle of unfinalized fork
// AND unfinalized value is Some
// ---> [2] ---> [4] ---> 5 ---> [6]
assert_eq!(ListCache::new(
DummyStorage::new()
.with_meta(Some(correct_id(2)), vec![correct_id(6)])
.with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: Some(2) })
.with_entry(correct_id(4), StorageEntry { prev_valid_from: Some(correct_id(2)), value: Some(4) })
.with_entry(correct_id(6), StorageEntry { prev_valid_from: Some(correct_id(4)), value: None })
.with_header(test_header(4))
.with_header(test_header(5))
.with_header(test_header(6)),
1024, test_id(2)
).value_at_block(&correct_id(5)).unwrap(), Some(4));
// when block is later than last finalized block AND it fits to the middle of unfinalized fork
// AND unfinalized value is None
// ---> [2] ---> [4] ---> 5 ---> [6]
assert_eq!(ListCache::new(
DummyStorage::new()
.with_meta(Some(correct_id(2)), vec![correct_id(6)])
.with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: Some(2) })
.with_entry(correct_id(4), StorageEntry { prev_valid_from: Some(correct_id(2)), value: None })
.with_entry(correct_id(6), StorageEntry { prev_valid_from: Some(correct_id(4)), value: Some(4) })
.with_header(test_header(4))
.with_header(test_header(5))
.with_header(test_header(6)),
1024, test_id(2)
).value_at_block(&correct_id(5)).unwrap(), None);
// when block is later than last finalized block AND it does not fits unfinalized fork
// AND it is connected to the finalized block AND finalized value is Some
// ---> [2] ----------> [4]
@@ -807,29 +762,14 @@ pub mod tests {
assert_eq!(ListCache::new(
DummyStorage::new()
.with_meta(Some(correct_id(2)), vec![correct_id(4)])
.with_entry(correct_id(4), StorageEntry { prev_valid_from: Some(correct_id(2)), value: Some(4) })
.with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: Some(2) })
.with_entry(correct_id(4), StorageEntry { prev_valid_from: Some(correct_id(2)), value: 4 })
.with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: 2 })
.with_header(test_header(2))
.with_header(test_header(3))
.with_header(test_header(4))
.with_header(fork_header(0, 2, 3)),
1024, test_id(2)
).value_at_block(&fork_id(0, 2, 3)).unwrap(), Some(2));
// when block is later than last finalized block AND it does not fits unfinalized fork
// AND it is connected to the finalized block AND finalized value is Some
// ---> [2] ----------> [4]
// \--- 3
assert_eq!(ListCache::new(
DummyStorage::new()
.with_meta(Some(correct_id(2)), vec![correct_id(4)])
.with_entry(correct_id(4), StorageEntry { prev_valid_from: Some(correct_id(2)), value: Some(4) })
.with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: None })
.with_header(test_header(2))
.with_header(test_header(3))
.with_header(test_header(4))
.with_header(fork_header(0, 2, 3)),
1024, test_id(2)
).value_at_block(&fork_id(0, 2, 3)).unwrap(), None);
}
#[test]
@@ -861,7 +801,7 @@ pub mod tests {
let mut cache = ListCache::new(
DummyStorage::new()
.with_meta(None, vec![test_id(4)])
.with_entry(test_id(4), StorageEntry { prev_valid_from: None, value: Some(4) }),
.with_entry(test_id(4), StorageEntry { prev_valid_from: None, value: 4 }),
1024, test_id(2)
);
cache.unfinalized[0].best_block = Some(test_id(4));
@@ -875,7 +815,7 @@ pub mod tests {
// AND new value is the same as in the fork' best block
let mut tx = DummyTransaction::new();
assert_eq!(cache.on_block_insert(&mut tx, test_id(4), test_id(5), Some(5), nfin).unwrap(),
Some(CommitOperation::AppendNewEntry(0, Entry { valid_from: test_id(5), value: Some(5) })));
Some(CommitOperation::AppendNewEntry(0, Entry { valid_from: test_id(5), value: 5 })));
assert_eq!(*tx.inserted_entries(), vec![test_id(5).hash].into_iter().collect());
assert!(tx.removed_entries().is_empty());
assert_eq!(*tx.updated_meta(), Some(Metadata { finalized: None, unfinalized: vec![test_id(5)] }));
@@ -885,7 +825,7 @@ pub mod tests {
let cache = ListCache::new(
DummyStorage::new()
.with_meta(None, vec![correct_id(4)])
.with_entry(correct_id(4), StorageEntry { prev_valid_from: None, value: Some(4) })
.with_entry(correct_id(4), StorageEntry { prev_valid_from: None, value: 4 })
.with_header(test_header(4)),
1024, test_id(2)
);
@@ -899,7 +839,7 @@ pub mod tests {
// AND new value is the same as in the fork' best block
let mut tx = DummyTransaction::new();
assert_eq!(cache.on_block_insert(&mut tx, correct_id(4), correct_id(5), Some(5), nfin).unwrap(),
Some(CommitOperation::AppendNewEntry(0, Entry { valid_from: correct_id(5), value: Some(5) })));
Some(CommitOperation::AppendNewEntry(0, Entry { valid_from: correct_id(5), value: 5 })));
assert_eq!(*tx.inserted_entries(), vec![correct_id(5).hash].into_iter().collect());
assert!(tx.removed_entries().is_empty());
assert_eq!(*tx.updated_meta(), Some(Metadata { finalized: None, unfinalized: vec![correct_id(5)] }));
@@ -908,8 +848,8 @@ pub mod tests {
let cache = ListCache::new(
DummyStorage::new()
.with_meta(Some(correct_id(2)), vec![correct_id(4)])
.with_entry(correct_id(4), StorageEntry { prev_valid_from: Some(correct_id(2)), value: Some(4) })
.with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: Some(2) })
.with_entry(correct_id(4), StorageEntry { prev_valid_from: Some(correct_id(2)), value: 4 })
.with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: 2 })
.with_header(test_header(2))
.with_header(test_header(3))
.with_header(test_header(4)),
@@ -917,7 +857,7 @@ pub mod tests {
);
let mut tx = DummyTransaction::new();
assert_eq!(cache.on_block_insert(&mut tx, correct_id(3), fork_id(0, 3, 4), Some(14), nfin).unwrap(),
Some(CommitOperation::AddNewFork(Entry { valid_from: fork_id(0, 3, 4), value: Some(14) })));
Some(CommitOperation::AddNewFork(Entry { valid_from: fork_id(0, 3, 4), value: 14 })));
assert_eq!(*tx.inserted_entries(), vec![fork_id(0, 3, 4).hash].into_iter().collect());
assert!(tx.removed_entries().is_empty());
assert_eq!(*tx.updated_meta(), Some(Metadata { finalized: Some(correct_id(2)), unfinalized: vec![correct_id(4), fork_id(0, 3, 4)] }));
@@ -927,7 +867,7 @@ pub mod tests {
let cache = ListCache::new(
DummyStorage::new()
.with_meta(Some(correct_id(2)), vec![])
.with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: Some(2) }),
.with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: 2 }),
1024, correct_id(2)
);
let mut tx = DummyTransaction::new();
@@ -940,12 +880,12 @@ pub mod tests {
let cache = ListCache::new(
DummyStorage::new()
.with_meta(Some(correct_id(2)), vec![])
.with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: Some(2) }),
.with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: 2 }),
1024, correct_id(2)
);
let mut tx = DummyTransaction::new();
assert_eq!(cache.on_block_insert(&mut tx, correct_id(2), correct_id(3), Some(3), nfin).unwrap(),
Some(CommitOperation::AddNewFork(Entry { valid_from: correct_id(3), value: Some(3) })));
Some(CommitOperation::AddNewFork(Entry { valid_from: correct_id(3), value: 3 })));
assert_eq!(*tx.inserted_entries(), vec![correct_id(3).hash].into_iter().collect());
assert!(tx.removed_entries().is_empty());
assert_eq!(*tx.updated_meta(), Some(Metadata { finalized: Some(correct_id(2)), unfinalized: vec![correct_id(3)] }));
@@ -953,8 +893,14 @@ pub mod tests {
// when inserting finalized entry AND there are no previous finalized entries
let cache = ListCache::new(DummyStorage::new(), 1024, correct_id(2));
let mut tx = DummyTransaction::new();
assert_eq!(cache.on_block_insert(&mut tx, correct_id(2), correct_id(3), Some(3), fin).unwrap(),
Some(CommitOperation::BlockFinalized(correct_id(3), Some(Entry { valid_from: correct_id(3), value: Some(3) }), Default::default())));
assert_eq!(
cache.on_block_insert(&mut tx, correct_id(2), correct_id(3), Some(3), fin).unwrap(),
Some(CommitOperation::BlockFinalized(
correct_id(3),
Some(Entry { valid_from: correct_id(3), value: 3 }),
Default::default(),
)),
);
assert_eq!(*tx.inserted_entries(), vec![correct_id(3).hash].into_iter().collect());
assert!(tx.removed_entries().is_empty());
assert_eq!(*tx.updated_meta(), Some(Metadata { finalized: Some(correct_id(3)), unfinalized: vec![] }));
@@ -962,7 +908,7 @@ pub mod tests {
let cache = ListCache::new(
DummyStorage::new()
.with_meta(Some(correct_id(2)), vec![])
.with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: Some(2) }),
.with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: 2 }),
1024, correct_id(2)
);
let mut tx = DummyTransaction::new();
@@ -973,8 +919,14 @@ pub mod tests {
assert!(tx.updated_meta().is_none());
// when inserting finalized entry AND value differs from previous finalized
let mut tx = DummyTransaction::new();
assert_eq!(cache.on_block_insert(&mut tx, correct_id(2), correct_id(3), Some(3), fin).unwrap(),
Some(CommitOperation::BlockFinalized(correct_id(3), Some(Entry { valid_from: correct_id(3), value: Some(3) }), Default::default())));
assert_eq!(
cache.on_block_insert(&mut tx, correct_id(2), correct_id(3), Some(3), fin).unwrap(),
Some(CommitOperation::BlockFinalized(
correct_id(3),
Some(Entry { valid_from: correct_id(3), value: 3 }),
Default::default(),
)),
);
assert_eq!(*tx.inserted_entries(), vec![correct_id(3).hash].into_iter().collect());
assert!(tx.removed_entries().is_empty());
assert_eq!(*tx.updated_meta(), Some(Metadata { finalized: Some(correct_id(3)), unfinalized: vec![] }));
@@ -983,8 +935,8 @@ pub mod tests {
let cache = ListCache::new(
DummyStorage::new()
.with_meta(Some(correct_id(2)), vec![fork_id(0, 1, 3)])
.with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: Some(2) })
.with_entry(fork_id(0, 1, 3), StorageEntry { prev_valid_from: None, value: Some(13) }),
.with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: 2 })
.with_entry(fork_id(0, 1, 3), StorageEntry { prev_valid_from: None, value: 13 }),
1024, correct_id(2)
);
let mut tx = DummyTransaction::new();
@@ -998,8 +950,8 @@ pub mod tests {
let cache = ListCache::new(
DummyStorage::new()
.with_meta(Some(correct_id(2)), vec![correct_id(5)])
.with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: Some(2) })
.with_entry(correct_id(5), StorageEntry { prev_valid_from: Some(correct_id(2)), value: Some(5) }),
.with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: 2 })
.with_entry(correct_id(5), StorageEntry { prev_valid_from: Some(correct_id(2)), value: 5 }),
1024, correct_id(2)
);
let mut tx = DummyTransaction::new();
@@ -1012,13 +964,19 @@ pub mod tests {
let cache = ListCache::new(
DummyStorage::new()
.with_meta(Some(correct_id(2)), vec![correct_id(5)])
.with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: Some(2) })
.with_entry(correct_id(5), StorageEntry { prev_valid_from: Some(correct_id(2)), value: Some(5) }),
.with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: 2 })
.with_entry(correct_id(5), StorageEntry { prev_valid_from: Some(correct_id(2)), value: 5 }),
1024, correct_id(4)
);
let mut tx = DummyTransaction::new();
assert_eq!(cache.on_block_finalize(&mut tx, correct_id(4), correct_id(5)).unwrap(),
Some(CommitOperation::BlockFinalized(correct_id(5), Some(Entry { valid_from: correct_id(5), value: Some(5) }), vec![0].into_iter().collect())));
assert_eq!(
cache.on_block_finalize(&mut tx, correct_id(4), correct_id(5)).unwrap(),
Some(CommitOperation::BlockFinalized(
correct_id(5),
Some(Entry { valid_from: correct_id(5), value: 5 }),
vec![0].into_iter().collect(),
)),
);
assert!(tx.inserted_entries().is_empty());
assert!(tx.removed_entries().is_empty());
assert_eq!(*tx.updated_meta(), Some(Metadata { finalized: Some(correct_id(5)), unfinalized: vec![] }));
@@ -1026,8 +984,8 @@ pub mod tests {
let cache = ListCache::new(
DummyStorage::new()
.with_meta(Some(correct_id(2)), vec![fork_id(0, 1, 3)])
.with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: Some(2) })
.with_entry(fork_id(0, 1, 3), StorageEntry { prev_valid_from: None, value: Some(13) }),
.with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: 2 })
.with_entry(fork_id(0, 1, 3), StorageEntry { prev_valid_from: None, value: 13 }),
1024, correct_id(2)
);
let mut tx = DummyTransaction::new();
@@ -1040,9 +998,9 @@ pub mod tests {
let mut cache = ListCache::new(
DummyStorage::new()
.with_meta(Some(correct_id(2)), vec![correct_id(5), correct_id(6)])
.with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: Some(2) })
.with_entry(correct_id(5), StorageEntry { prev_valid_from: Some(correct_id(2)), value: Some(5) })
.with_entry(correct_id(6), StorageEntry { prev_valid_from: Some(correct_id(5)), value: Some(6) }),
.with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: 2 })
.with_entry(correct_id(5), StorageEntry { prev_valid_from: Some(correct_id(2)), value: 5 })
.with_entry(correct_id(6), StorageEntry { prev_valid_from: Some(correct_id(5)), value: 6 }),
1024, correct_id(2)
);
@@ -1050,17 +1008,21 @@ pub mod tests {
cache.on_transaction_commit(CommitOperation::AppendNewBlock(0, correct_id(6)));
assert_eq!(cache.unfinalized[0].best_block, Some(correct_id(6)));
// when new entry is appended to unfinalized fork
cache.on_transaction_commit(CommitOperation::AppendNewEntry(0, Entry { valid_from: correct_id(7), value: Some(7) }));
cache.on_transaction_commit(CommitOperation::AppendNewEntry(0, Entry { valid_from: correct_id(7), value: 7 }));
assert_eq!(cache.unfinalized[0].best_block, Some(correct_id(7)));
assert_eq!(cache.unfinalized[0].head, Entry { valid_from: correct_id(7), value: Some(7) });
assert_eq!(cache.unfinalized[0].head, Entry { valid_from: correct_id(7), value: 7 });
// when new fork is added
cache.on_transaction_commit(CommitOperation::AddNewFork(Entry { valid_from: correct_id(10), value: Some(10) }));
cache.on_transaction_commit(CommitOperation::AddNewFork(Entry { valid_from: correct_id(10), value: 10 }));
assert_eq!(cache.unfinalized[2].best_block, Some(correct_id(10)));
assert_eq!(cache.unfinalized[2].head, Entry { valid_from: correct_id(10), value: Some(10) });
assert_eq!(cache.unfinalized[2].head, Entry { valid_from: correct_id(10), value: 10 });
// when block is finalized + entry is finalized + unfinalized forks are deleted
cache.on_transaction_commit(CommitOperation::BlockFinalized(correct_id(20), Some(Entry { valid_from: correct_id(20), value: Some(20) }), vec![0, 1, 2].into_iter().collect()));
cache.on_transaction_commit(CommitOperation::BlockFinalized(
correct_id(20),
Some(Entry { valid_from: correct_id(20), value: 20 }),
vec![0, 1, 2].into_iter().collect(),
));
assert_eq!(cache.best_finalized_block, correct_id(20));
assert_eq!(cache.best_finalized_entry, Some(Entry { valid_from: correct_id(20), value: Some(20) }));
assert_eq!(cache.best_finalized_entry, Some(Entry { valid_from: correct_id(20), value: 20 }));
assert!(cache.unfinalized.is_empty());
}
@@ -1071,9 +1033,9 @@ pub mod tests {
assert_eq!(ListCache::new(
DummyStorage::new()
.with_meta(None, vec![fork_id(0, 1, 3), correct_id(5)])
.with_entry(fork_id(0, 1, 3), StorageEntry { prev_valid_from: Some(correct_id(1)), value: Some(13) })
.with_entry(correct_id(5), StorageEntry { prev_valid_from: Some(correct_id(2)), value: Some(5) })
.with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: None })
.with_entry(fork_id(0, 1, 3), StorageEntry { prev_valid_from: Some(correct_id(1)), value: 13 })
.with_entry(correct_id(5), StorageEntry { prev_valid_from: Some(correct_id(2)), value: 5 })
.with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: 2 })
.with_header(test_header(2))
.with_header(test_header(3))
.with_header(test_header(4))
@@ -1085,9 +1047,9 @@ pub mod tests {
assert_eq!(ListCache::new(
DummyStorage::new()
.with_meta(None, vec![correct_id(5), fork_id(0, 1, 3)])
.with_entry(fork_id(0, 1, 3), StorageEntry { prev_valid_from: Some(correct_id(1)), value: Some(13) })
.with_entry(correct_id(5), StorageEntry { prev_valid_from: Some(correct_id(2)), value: Some(5) })
.with_entry(correct_id(2), StorageEntry { prev_valid_from: Some(correct_id(1)), value: Some(2) })
.with_entry(fork_id(0, 1, 3), StorageEntry { prev_valid_from: Some(correct_id(1)), value: 13 })
.with_entry(correct_id(5), StorageEntry { prev_valid_from: Some(correct_id(2)), value: 5 })
.with_entry(correct_id(2), StorageEntry { prev_valid_from: Some(correct_id(1)), value: 2 })
.with_header(test_header(2))
.with_header(test_header(3))
.with_header(test_header(4))
@@ -1103,9 +1065,9 @@ pub mod tests {
assert!(ListCache::new(
DummyStorage::new()
.with_meta(None, vec![correct_id(5), fork_id(0, 1, 3)])
.with_entry(fork_id(0, 1, 3), StorageEntry { prev_valid_from: Some(correct_id(1)), value: Some(13) })
.with_entry(correct_id(5), StorageEntry { prev_valid_from: Some(correct_id(2)), value: Some(5) })
.with_entry(correct_id(2), StorageEntry { prev_valid_from: Some(correct_id(1)), value: Some(2) })
.with_entry(fork_id(0, 1, 3), StorageEntry { prev_valid_from: Some(correct_id(1)), value: 13 })
.with_entry(correct_id(5), StorageEntry { prev_valid_from: Some(correct_id(2)), value: 5 })
.with_entry(correct_id(2), StorageEntry { prev_valid_from: Some(correct_id(1)), value: 2 })
.with_header(test_header(2))
.with_header(test_header(3))
.with_header(test_header(4))
@@ -1123,59 +1085,59 @@ pub mod tests {
fn fork_matches_works() {
// when block is not within list range
let storage = DummyStorage::new()
.with_entry(test_id(100), StorageEntry { prev_valid_from: Some(test_id(50)), value: Some(100) })
.with_entry(test_id(50), StorageEntry { prev_valid_from: None, value: Some(50) });
assert_eq!(Fork::<_, u64> { best_block: None, head: Entry { valid_from: test_id(100), value: None } }
.with_entry(test_id(100), StorageEntry { prev_valid_from: Some(test_id(50)), value: 100 })
.with_entry(test_id(50), StorageEntry { prev_valid_from: None, value: 50 });
assert_eq!(Fork::<_, u64> { best_block: None, head: Entry { valid_from: test_id(100), value: 0 } }
.matches(&storage, &test_id(20)).unwrap(), false);
// when block is not connected to the begin block
let storage = DummyStorage::new()
.with_entry(correct_id(5), StorageEntry { prev_valid_from: Some(correct_id(3)), value: Some(100) })
.with_entry(correct_id(3), StorageEntry { prev_valid_from: None, value: Some(200) })
.with_entry(correct_id(5), StorageEntry { prev_valid_from: Some(correct_id(3)), value: 100 })
.with_entry(correct_id(3), StorageEntry { prev_valid_from: None, value: 200 })
.with_header(test_header(5))
.with_header(test_header(4))
.with_header(test_header(3))
.with_header(fork_header(0, 2, 4))
.with_header(fork_header(0, 2, 3));
assert_eq!(Fork::<_, u64> { best_block: None, head: Entry { valid_from: correct_id(5), value: Some(100) } }
assert_eq!(Fork::<_, u64> { best_block: None, head: Entry { valid_from: correct_id(5), value: 100 } }
.matches(&storage, &fork_id(0, 2, 4)).unwrap(), false);
// when block is not connected to the end block
let storage = DummyStorage::new()
.with_entry(correct_id(5), StorageEntry { prev_valid_from: Some(correct_id(3)), value: Some(100) })
.with_entry(correct_id(3), StorageEntry { prev_valid_from: None, value: Some(200) })
.with_entry(correct_id(5), StorageEntry { prev_valid_from: Some(correct_id(3)), value: 100 })
.with_entry(correct_id(3), StorageEntry { prev_valid_from: None, value: 200 })
.with_header(test_header(5))
.with_header(test_header(4))
.with_header(test_header(3))
.with_header(fork_header(0, 3, 4));
assert_eq!(Fork::<_, u64> { best_block: None, head: Entry { valid_from: correct_id(5), value: Some(100) } }
assert_eq!(Fork::<_, u64> { best_block: None, head: Entry { valid_from: correct_id(5), value: 100 } }
.matches(&storage, &fork_id(0, 3, 4)).unwrap(), false);
// when block is connected to the begin block AND end is open
let storage = DummyStorage::new()
.with_entry(correct_id(5), StorageEntry { prev_valid_from: None, value: Some(100) })
.with_entry(correct_id(5), StorageEntry { prev_valid_from: None, value: 100 })
.with_header(test_header(5))
.with_header(test_header(6));
assert_eq!(Fork::<_, u64> { best_block: None, head: Entry { valid_from: correct_id(5), value: Some(100) } }
assert_eq!(Fork::<_, u64> { best_block: None, head: Entry { valid_from: correct_id(5), value: 100 } }
.matches(&storage, &correct_id(6)).unwrap(), true);
// when block is connected to the begin block AND to the end block
let storage = DummyStorage::new()
.with_entry(correct_id(5), StorageEntry { prev_valid_from: Some(correct_id(3)), value: Some(100) })
.with_entry(correct_id(3), StorageEntry { prev_valid_from: None, value: Some(200) })
.with_entry(correct_id(5), StorageEntry { prev_valid_from: Some(correct_id(3)), value: 100 })
.with_entry(correct_id(3), StorageEntry { prev_valid_from: None, value: 200 })
.with_header(test_header(5))
.with_header(test_header(4))
.with_header(test_header(3));
assert_eq!(Fork::<_, u64> { best_block: None, head: Entry { valid_from: correct_id(5), value: Some(100) } }
assert_eq!(Fork::<_, u64> { best_block: None, head: Entry { valid_from: correct_id(5), value: 100 } }
.matches(&storage, &correct_id(4)).unwrap(), true);
}
#[test]
fn fork_try_append_works() {
// when best block is unknown
assert_eq!(Fork::<_, u64> { best_block: None, head: Entry { valid_from: test_id(100), value: None } }
assert_eq!(Fork::<_, u64> { best_block: None, head: Entry { valid_from: test_id(100), value: 0 } }
.try_append(&test_id(100)), false);
// when best block is known but different
assert_eq!(Fork::<_, u64> { best_block: None, head: Entry { valid_from: test_id(100), value: None } }
assert_eq!(Fork::<_, u64> { best_block: None, head: Entry { valid_from: test_id(100), value: 0 } }
.try_append(&test_id(101)), false);
// when best block is known and the same
assert_eq!(Fork::<_, u64> { best_block: Some(test_id(100)), head: Entry { valid_from: test_id(100), value: None } }
assert_eq!(Fork::<_, u64> { best_block: Some(test_id(100)), head: Entry { valid_from: test_id(100), value: 0 } }
.try_append(&test_id(100)), true);
}
@@ -1183,49 +1145,52 @@ pub mod tests {
fn fork_try_append_or_fork_works() {
// when there's no entry before parent
let storage = DummyStorage::new()
.with_entry(test_id(100), StorageEntry { prev_valid_from: Some(test_id(50)), value: Some(100) })
.with_entry(test_id(50), StorageEntry { prev_valid_from: None, value: Some(50) });
assert_eq!(Fork::<_, u64> { best_block: None, head: Entry { valid_from: test_id(100), value: None } }
.with_entry(test_id(100), StorageEntry { prev_valid_from: Some(test_id(50)), value: 100 })
.with_entry(test_id(50), StorageEntry { prev_valid_from: None, value: 50 });
assert_eq!(Fork::<_, u64> { best_block: None, head: Entry { valid_from: test_id(100), value: 0 } }
.try_append_or_fork(&storage, &test_id(30), None).unwrap(), None);
// when parent does not belong to the fork
let storage = DummyStorage::new()
.with_entry(correct_id(5), StorageEntry { prev_valid_from: Some(correct_id(3)), value: Some(100) })
.with_entry(correct_id(3), StorageEntry { prev_valid_from: None, value: Some(200) })
.with_entry(correct_id(5), StorageEntry { prev_valid_from: Some(correct_id(3)), value: 100 })
.with_entry(correct_id(3), StorageEntry { prev_valid_from: None, value: 200 })
.with_header(test_header(5))
.with_header(test_header(4))
.with_header(test_header(3))
.with_header(fork_header(0, 2, 4))
.with_header(fork_header(0, 2, 3));
assert_eq!(Fork::<_, u64> { best_block: None, head: Entry { valid_from: correct_id(5), value: Some(100) } }
assert_eq!(Fork::<_, u64> { best_block: None, head: Entry { valid_from: correct_id(5), value: 100 } }
.try_append_or_fork(&storage, &fork_id(0, 2, 4), None).unwrap(), None);
// when the entry before parent is the head entry
let storage = DummyStorage::new()
.with_entry(ComplexBlockId::new(test_header(5).hash(), 5), StorageEntry { prev_valid_from: Some(correct_id(3)), value: Some(100) })
.with_entry(
ComplexBlockId::new(test_header(5).hash(), 5),
StorageEntry { prev_valid_from: Some(correct_id(3)), value: 100 },
)
.with_header(test_header(6))
.with_header(test_header(5));
assert_eq!(Fork::<_, u64> { best_block: None, head: Entry { valid_from: correct_id(5), value: Some(100) } }
assert_eq!(Fork::<_, u64> { best_block: None, head: Entry { valid_from: correct_id(5), value: 100 } }
.try_append_or_fork(&storage, &correct_id(6), None).unwrap(), Some(ForkAppendResult::Append));
// when the parent located after last finalized entry
let storage = DummyStorage::new()
.with_entry(correct_id(6), StorageEntry { prev_valid_from: Some(correct_id(3)), value: Some(100) })
.with_entry(correct_id(3), StorageEntry { prev_valid_from: None, value: Some(200) })
.with_entry(correct_id(6), StorageEntry { prev_valid_from: Some(correct_id(3)), value: 100 })
.with_entry(correct_id(3), StorageEntry { prev_valid_from: None, value: 200 })
.with_header(test_header(6))
.with_header(test_header(5))
.with_header(test_header(4))
.with_header(test_header(3))
.with_header(fork_header(0, 4, 5));
assert_eq!(Fork::<_, u64> { best_block: None, head: Entry { valid_from: correct_id(6), value: Some(100) } }
assert_eq!(Fork::<_, u64> { best_block: None, head: Entry { valid_from: correct_id(6), value: 100 } }
.try_append_or_fork(&storage, &fork_id(0, 4, 5), None).unwrap(), Some(ForkAppendResult::Fork(ComplexBlockId::new(test_header(3).hash(), 3))));
// when the parent located before last finalized entry
let storage = DummyStorage::new()
.with_entry(correct_id(6), StorageEntry { prev_valid_from: Some(correct_id(3)), value: Some(100) })
.with_entry(correct_id(3), StorageEntry { prev_valid_from: None, value: Some(200) })
.with_entry(correct_id(6), StorageEntry { prev_valid_from: Some(correct_id(3)), value: 100 })
.with_entry(correct_id(3), StorageEntry { prev_valid_from: None, value: 200 })
.with_header(test_header(6))
.with_header(test_header(5))
.with_header(test_header(4))
.with_header(test_header(3))
.with_header(fork_header(0, 4, 5));
assert_eq!(Fork::<_, u64> { best_block: None, head: Entry { valid_from: correct_id(6), value: Some(100) } }
assert_eq!(Fork::<_, u64> { best_block: None, head: Entry { valid_from: correct_id(6), value: 100 } }
.try_append_or_fork(&storage, &fork_id(0, 4, 5), Some(3)).unwrap(), None);
}
@@ -1234,30 +1199,30 @@ pub mod tests {
// when we reached finalized entry without iterations
let storage = DummyStorage::new().with_id(100, H256::from_low_u64_be(100));
let mut tx = DummyTransaction::new();
Fork::<_, u64> { best_block: None, head: Entry { valid_from: test_id(100), value: None } }
Fork::<_, u64> { best_block: None, head: Entry { valid_from: test_id(100), value: 0 } }
.destroy(&storage, &mut tx, Some(200)).unwrap();
assert!(tx.removed_entries().is_empty());
// when we reach finalized entry with iterations
let storage = DummyStorage::new()
.with_id(10, H256::from_low_u64_be(10))
.with_entry(test_id(100), StorageEntry { prev_valid_from: Some(test_id(50)), value: Some(100) })
.with_entry(test_id(50), StorageEntry { prev_valid_from: Some(test_id(20)), value: Some(50) })
.with_entry(test_id(20), StorageEntry { prev_valid_from: Some(test_id(10)), value: Some(20) })
.with_entry(test_id(10), StorageEntry { prev_valid_from: Some(test_id(5)), value: Some(10) })
.with_entry(test_id(5), StorageEntry { prev_valid_from: Some(test_id(3)), value: Some(5) })
.with_entry(test_id(3), StorageEntry { prev_valid_from: None, value: None });
.with_entry(test_id(100), StorageEntry { prev_valid_from: Some(test_id(50)), value: 100 })
.with_entry(test_id(50), StorageEntry { prev_valid_from: Some(test_id(20)), value: 50 })
.with_entry(test_id(20), StorageEntry { prev_valid_from: Some(test_id(10)), value: 20 })
.with_entry(test_id(10), StorageEntry { prev_valid_from: Some(test_id(5)), value: 10 })
.with_entry(test_id(5), StorageEntry { prev_valid_from: Some(test_id(3)), value: 5 })
.with_entry(test_id(3), StorageEntry { prev_valid_from: None, value: 0 });
let mut tx = DummyTransaction::new();
Fork::<_, u64> { best_block: None, head: Entry { valid_from: test_id(100), value: None } }
Fork::<_, u64> { best_block: None, head: Entry { valid_from: test_id(100), value: 0 } }
.destroy(&storage, &mut tx, Some(200)).unwrap();
assert_eq!(*tx.removed_entries(),
vec![test_id(100).hash, test_id(50).hash, test_id(20).hash].into_iter().collect());
// when we reach beginning of fork before finalized block
let storage = DummyStorage::new()
.with_id(10, H256::from_low_u64_be(10))
.with_entry(test_id(100), StorageEntry { prev_valid_from: Some(test_id(50)), value: Some(100) })
.with_entry(test_id(50), StorageEntry { prev_valid_from: None, value: Some(50) });
.with_entry(test_id(100), StorageEntry { prev_valid_from: Some(test_id(50)), value: 100 })
.with_entry(test_id(50), StorageEntry { prev_valid_from: None, value: 50 });
let mut tx = DummyTransaction::new();
Fork::<_, u64> { best_block: None, head: Entry { valid_from: test_id(100), value: None } }
Fork::<_, u64> { best_block: None, head: Entry { valid_from: test_id(100), value: 0 } }
.destroy(&storage, &mut tx, Some(200)).unwrap();
assert_eq!(*tx.removed_entries(),
vec![test_id(100).hash, test_id(50).hash].into_iter().collect());
@@ -1355,14 +1320,14 @@ pub mod tests {
#[test]
fn read_forks_works() {
let storage = DummyStorage::new()
.with_entry(test_id(10), StorageEntry { prev_valid_from: Some(test_id(1)), value: Some(11) })
.with_entry(test_id(20), StorageEntry { prev_valid_from: Some(test_id(2)), value: None })
.with_entry(test_id(30), StorageEntry { prev_valid_from: None, value: Some(33) });
.with_entry(test_id(10), StorageEntry { prev_valid_from: Some(test_id(1)), value: 11 })
.with_entry(test_id(20), StorageEntry { prev_valid_from: Some(test_id(2)), value: 0 })
.with_entry(test_id(30), StorageEntry { prev_valid_from: None, value: 33 });
let expected = (
Some(Entry { valid_from: test_id(10), value: Some(11) }),
Some(Entry { valid_from: test_id(10), value: 11 }),
vec![
Fork { best_block: None, head: Entry { valid_from: test_id(20), value: None } },
Fork { best_block: None, head: Entry { valid_from: test_id(30), value: Some(33) } },
Fork { best_block: None, head: Entry { valid_from: test_id(20), value: 0 } },
Fork { best_block: None, head: Entry { valid_from: test_id(30), value: 33 } },
],
);
@@ -1378,9 +1343,9 @@ pub mod tests {
.with_id(10, H256::from_low_u64_be(10))
.with_id(20, H256::from_low_u64_be(20))
.with_id(30, H256::from_low_u64_be(30))
.with_entry(test_id(10), StorageEntry { prev_valid_from: None, value: Some(10) })
.with_entry(test_id(20), StorageEntry { prev_valid_from: Some(test_id(10)), value: Some(20) })
.with_entry(test_id(30), StorageEntry { prev_valid_from: Some(test_id(20)), value: Some(30) }),
.with_entry(test_id(10), StorageEntry { prev_valid_from: None, value: 10 })
.with_entry(test_id(20), StorageEntry { prev_valid_from: Some(test_id(10)), value: 20 })
.with_entry(test_id(30), StorageEntry { prev_valid_from: Some(test_id(20)), value: 30 }),
10, test_id(9));
let mut tx = DummyTransaction::new();
+37 -41
View File
@@ -27,10 +27,10 @@ use crate::cache::list_storage::{Storage};
#[derive(Debug)]
#[cfg_attr(test, derive(PartialEq))]
pub struct Entry<Block: BlockT, T> {
/// first block, when this value became actual
/// first block, when this value became actual.
pub valid_from: ComplexBlockId<Block>,
/// None means that we do not know the value starting from `valid_from` block
pub value: Option<T>,
/// Value stored at this entry.
pub value: T,
}
/// Internal representation of the single list-based cache entry. The entry points to the
@@ -38,21 +38,24 @@ pub struct Entry<Block: BlockT, T> {
#[derive(Debug, Encode, Decode)]
#[cfg_attr(test, derive(Clone, PartialEq))]
pub struct StorageEntry<Block: BlockT, T: CacheItemT> {
/// None if valid from the beginning
/// None if valid from the beginning.
pub prev_valid_from: Option<ComplexBlockId<Block>>,
/// None means that we do not know the value starting from `valid_from` block
pub value: Option<T>,
/// Value stored at this entry.
pub value: T,
}
impl<Block: BlockT, T: CacheItemT> Entry<Block, T> {
/// Returns Some if the entry should be updated with the new value.
pub fn try_update(&self, value: Option<T>) -> Option<StorageEntry<Block, T>> {
match self.value == value {
true => None,
false => Some(StorageEntry {
prev_valid_from: Some(self.valid_from.clone()),
value,
}),
match value {
Some(value) => match self.value == value {
true => None,
false => Some(StorageEntry {
prev_valid_from: Some(self.valid_from.clone()),
value,
}),
},
None => None,
}
}
@@ -62,7 +65,7 @@ impl<Block: BlockT, T: CacheItemT> Entry<Block, T> {
storage: &S,
block: NumberFor<Block>,
) -> ClientResult<Option<(ComplexBlockId<Block>, Option<ComplexBlockId<Block>>)>> {
Ok(self.search_best_before(storage, block, false)?
Ok(self.search_best_before(storage, block)?
.map(|(entry, next)| (entry.valid_from, next)))
}
@@ -75,13 +78,12 @@ impl<Block: BlockT, T: CacheItemT> Entry<Block, T> {
&self,
storage: &S,
block: NumberFor<Block>,
require_value: bool,
) -> ClientResult<Option<(Entry<Block, T>, Option<ComplexBlockId<Block>>)>> {
// we're looking for the best value
let mut next = None;
let mut current = self.valid_from.clone();
if block >= self.valid_from.number {
let value = if require_value { self.value.clone() } else { None };
let value = self.value.clone();
return Ok(Some((Entry { valid_from: current, value }, next)));
}
@@ -119,47 +121,41 @@ mod tests {
#[test]
fn entry_try_update_works() {
// when trying to update with the same None value
assert_eq!(Entry::<_, u64> { valid_from: test_id(1), value: None }.try_update(None), None);
// when trying to update with None value
assert_eq!(Entry::<_, u64> { valid_from: test_id(1), value: 42 }.try_update(None), None);
// when trying to update with the same Some value
assert_eq!(Entry { valid_from: test_id(1), value: Some(1) }.try_update(Some(1)), None);
// when trying to update with different None value
assert_eq!(Entry { valid_from: test_id(1), value: Some(1) }.try_update(None),
Some(StorageEntry { prev_valid_from: Some(test_id(1)), value: None }));
assert_eq!(Entry { valid_from: test_id(1), value: 1 }.try_update(Some(1)), None);
// when trying to update with different Some value
assert_eq!(Entry { valid_from: test_id(1), value: Some(1) }.try_update(Some(2)),
Some(StorageEntry { prev_valid_from: Some(test_id(1)), value: Some(2) }));
assert_eq!(Entry { valid_from: test_id(1), value: 1 }.try_update(Some(2)),
Some(StorageEntry { prev_valid_from: Some(test_id(1)), value: 2 }));
}
#[test]
fn entry_search_best_before_fails() {
// when storage returns error
assert!(Entry::<_, u64> { valid_from: test_id(100), value: None }.search_best_before(&FaultyStorage, 50, false).is_err());
assert!(Entry::<_, u64> { valid_from: test_id(100), value: 42 }
.search_best_before(&FaultyStorage, 50).is_err());
}
#[test]
fn entry_search_best_before_works() {
// when block is better than our best block AND value is not required
assert_eq!(Entry::<_, u64> { valid_from: test_id(100), value: Some(100) }
.search_best_before(&DummyStorage::new(), 150, false).unwrap(),
Some((Entry::<_, u64> { valid_from: test_id(100), value: None }, None)));
// when block is better than our best block AND value is required
assert_eq!(Entry::<_, u64> { valid_from: test_id(100), value: Some(100) }
.search_best_before(&DummyStorage::new(), 150, true).unwrap(),
Some((Entry::<_, u64> { valid_from: test_id(100), value: Some(100) }, None)));
// when block is better than our best block
assert_eq!(Entry::<_, u64> { valid_from: test_id(100), value: 100 }
.search_best_before(&DummyStorage::new(), 150).unwrap(),
Some((Entry::<_, u64> { valid_from: test_id(100), value: 100 }, None)));
// when block is found between two entries
assert_eq!(Entry::<_, u64> { valid_from: test_id(100), value: Some(100) }
assert_eq!(Entry::<_, u64> { valid_from: test_id(100), value: 100 }
.search_best_before(&DummyStorage::new()
.with_entry(test_id(100), StorageEntry { prev_valid_from: Some(test_id(50)), value: Some(100) })
.with_entry(test_id(50), StorageEntry { prev_valid_from: Some(test_id(30)), value: Some(50) }),
75, false).unwrap(),
Some((Entry::<_, u64> { valid_from: test_id(50), value: Some(50) }, Some(test_id(100)))));
.with_entry(test_id(100), StorageEntry { prev_valid_from: Some(test_id(50)), value: 100 })
.with_entry(test_id(50), StorageEntry { prev_valid_from: Some(test_id(30)), value: 50 }),
75).unwrap(),
Some((Entry::<_, u64> { valid_from: test_id(50), value: 50 }, Some(test_id(100)))));
// when block is not found
assert_eq!(Entry::<_, u64> { valid_from: test_id(100), value: Some(100) }
assert_eq!(Entry::<_, u64> { valid_from: test_id(100), value: 100 }
.search_best_before(&DummyStorage::new()
.with_entry(test_id(100), StorageEntry { prev_valid_from: Some(test_id(50)), value: Some(100) })
.with_entry(test_id(50), StorageEntry { prev_valid_from: None, value: Some(50) }),
30, true).unwrap(),
.with_entry(test_id(100), StorageEntry { prev_valid_from: Some(test_id(50)), value: 100 })
.with_entry(test_id(50), StorageEntry { prev_valid_from: None, value: 50 }),
30).unwrap(),
None);
}
}
+1 -1
View File
@@ -221,7 +221,7 @@ impl<'a, Block: BlockT> DbCacheTransaction<'a, Block> {
),
parent.clone(),
block.clone(),
value.or(cache.value_at_block(&parent)?),
value,
entry_type,
)?;
if let Some(op) = op {