Implement nested storage transactions (#6269)

* Add transactional storage functionality to OverlayChanges

A collection already has a natural None state. No need to
wrap it with an option.

* Add storage transactions runtime interface

* Add frame support for transactions

* Fix committed typo

* Rename 'changes' variable to 'overlay'

* Fix renaming change

* Fixed strange line break

* Rename clear to clear_where

* Add comment regarding delete value on mutation

* Add comment which changes are covered by a transaction

* Do force the arg to with_transaction return a Result

* Use rust doc comments on every documentable place

* Fix wording of insert_diry doc

* Improve doc on start_transaction

* Rename value to overlayed in close_transaction

* Inline negation

* Improve wording of close_transaction comments

* Get rid of an expect by using get_or_insert_with

* Remove trailing whitespace

* Rename should to expected in tests

* Rolling back a transaction must mark the overlay as dirty

* Protect client initiated storage tx from being droped by runtime

* Review nits

* Return Err when entering or exiting runtime fails

* Documentation fixup

* Remove close type

* Move enter/exit runtime to excute_aux in the state-machine

* Rename Discard -> Rollback

* Move child changeset creation to constructor

* Move child spawning into the closure

* Apply suggestions from code review

Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>

* Fixup for code suggestion

* Unify re-exports

* Rename overlay_changes to mod.rs and move into subdir

* Change proof wording

* Adapt a new test from master to storage-tx

* Suggestions from the latest round of review

* Fix warning message

Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>
This commit is contained in:
Alexander Theißen
2020-06-23 11:17:53 +02:00
committed by GitHub
parent 6aa8965f33
commit bb2df2122e
16 changed files with 1383 additions and 532 deletions
+34 -14
View File
@@ -37,6 +37,10 @@ use std::{error, fmt, any::{Any, TypeId}};
use log::{warn, trace};
const EXT_NOT_ALLOWED_TO_FAIL: &str = "Externalities not allowed to fail within runtime";
const BENCHMARKING_FN: &str = "\
This is a special fn only for benchmarking where a database commit happens from the runtime.
For that reason client started transactions before calling into runtime are not allowed.
Without client transactions the loop condition garantuees the success of the tx close.";
/// Errors that can occur when interacting with the externalities.
#[derive(Debug, Copy, Clone)]
@@ -147,7 +151,7 @@ where
self.backend.pairs().iter()
.map(|&(ref k, ref v)| (k.to_vec(), Some(v.to_vec())))
.chain(self.overlay.changes(None).map(|(k, v)| (k.clone(), v.value().cloned())))
.chain(self.overlay.changes().map(|(k, v)| (k.clone(), v.value().cloned())))
.collect::<HashMap<_, _>>()
.into_iter()
.filter_map(|(k, maybe_val)| maybe_val.map(|val| (k, val)))
@@ -477,15 +481,14 @@ where
);
root.encode()
} else {
let root = if let Some((changes, info)) = self.overlay.child_changes(storage_key) {
let delta = changes.map(|(k, v)| (k.as_ref(), v.value().map(AsRef::as_ref)));
Some(self.backend.child_storage_root(info, delta))
} else {
None
};
if let Some(child_info) = self.overlay.default_child_info(storage_key) {
let (root, is_empty, _) = {
let delta = self.overlay.changes(Some(child_info))
.map(|(k, v)| (k.as_ref(), v.value().map(AsRef::as_ref)));
self.backend.child_storage_root(child_info, delta)
};
if let Some((root, is_empty, _)) = root {
let root = root.encode();
// We store update in the overlay in order to be able to use 'self.storage_transaction'
// cache. This is brittle as it rely on Ext only querying the trie backend for
@@ -547,20 +550,37 @@ where
root.map(|r| r.map(|o| o.encode()))
}
fn storage_start_transaction(&mut self) {
self.overlay.start_transaction()
}
fn storage_rollback_transaction(&mut self) -> Result<(), ()> {
self.mark_dirty();
self.overlay.rollback_transaction().map_err(|_| ())
}
fn storage_commit_transaction(&mut self) -> Result<(), ()> {
self.overlay.commit_transaction().map_err(|_| ())
}
fn wipe(&mut self) {
self.overlay.discard_prospective();
for _ in 0..self.overlay.transaction_depth() {
self.overlay.rollback_transaction().expect(BENCHMARKING_FN);
}
self.overlay.drain_storage_changes(
&self.backend,
None,
Default::default(),
self.storage_transaction_cache,
).expect(EXT_NOT_ALLOWED_TO_FAIL);
self.storage_transaction_cache.reset();
self.backend.wipe().expect(EXT_NOT_ALLOWED_TO_FAIL)
self.backend.wipe().expect(EXT_NOT_ALLOWED_TO_FAIL);
self.mark_dirty();
}
fn commit(&mut self) {
self.overlay.commit_prospective();
for _ in 0..self.overlay.transaction_depth() {
self.overlay.commit_transaction().expect(BENCHMARKING_FN);
}
let changes = self.overlay.drain_storage_changes(
&self.backend,
None,
@@ -571,7 +591,7 @@ where
changes.transaction_storage_root,
changes.transaction,
).expect(EXT_NOT_ALLOWED_TO_FAIL);
self.storage_transaction_cache.reset();
self.mark_dirty();
}
}