From ade667f9283414262e641254bedfc422a9ea71eb Mon Sep 17 00:00:00 2001
From: cheme <emericchevalier.pro@gmail.com>
Date: Fri, 23 Jul 2021 13:30:00 +0200
Subject: [PATCH] State machine local child root cache. (#9107)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* cache root for child api.

* minimal testing

* Reset cache on test 'set_root'.

* Update primitives/state-machine/src/trie_backend_essence.rs

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

* Update primitives/state-machine/src/trie_backend_essence.rs

* Update primitives/state-machine/src/trie_backend_essence.rs

* Renaming to 'reset_cache'.

* correct rust fmt

Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>
---
 .../state-machine/src/trie_backend.rs         | 16 ++++++
 .../state-machine/src/trie_backend_essence.rs | 56 ++++++++++++++++++-
 2 files changed, 70 insertions(+), 2 deletions(-)

diff --git a/substrate/primitives/state-machine/src/trie_backend.rs b/substrate/primitives/state-machine/src/trie_backend.rs
index e8c9fa475cf..95007653321 100644
--- a/substrate/primitives/state-machine/src/trie_backend.rs
+++ b/substrate/primitives/state-machine/src/trie_backend.rs
@@ -326,6 +326,22 @@ pub mod tests {
 				.unwrap(),
 			Some(vec![142u8]),
 		);
+		// Change cache entry to check that caching is active.
+		test_trie
+			.essence
+			.cache
+			.write()
+			.child_root
+			.entry(b"sub1".to_vec())
+			.and_modify(|value| {
+				*value = None;
+			});
+		assert_eq!(
+			test_trie
+				.child_storage(&ChildInfo::new_default(CHILD_KEY_1), b"value3")
+				.unwrap(),
+			None,
+		);
 	}
 
 	#[test]
diff --git a/substrate/primitives/state-machine/src/trie_backend_essence.rs b/substrate/primitives/state-machine/src/trie_backend_essence.rs
index 06a99f93880..052c61bd6ee 100644
--- a/substrate/primitives/state-machine/src/trie_backend_essence.rs
+++ b/substrate/primitives/state-machine/src/trie_backend_essence.rs
@@ -21,6 +21,8 @@
 use crate::{backend::Consolidate, debug, warn, StorageKey, StorageValue};
 use codec::Encode;
 use hash_db::{self, Hasher, Prefix};
+#[cfg(feature = "std")]
+use parking_lot::RwLock;
 use sp_core::storage::ChildInfo;
 use sp_std::{boxed::Box, ops::Deref, vec::Vec};
 use sp_trie::{
@@ -29,6 +31,8 @@ use sp_trie::{
 	DBValue, KeySpacedDB, MemoryDB, PrefixedMemoryDB, Trie, TrieDBIterator,
 };
 #[cfg(feature = "std")]
+use std::collections::HashMap;
+#[cfg(feature = "std")]
 use std::sync::Arc;
 
 #[cfg(not(feature = "std"))]
@@ -46,11 +50,26 @@ pub trait Storage<H: Hasher>: Send + Sync {
 	fn get(&self, key: &H::Out, prefix: Prefix) -> Result<Option<DBValue>>;
 }
 
+/// Local cache for child root.
+#[cfg(feature = "std")]
+pub(crate) struct Cache {
+	pub child_root: HashMap<Vec<u8>, Option<Vec<u8>>>,
+}
+
+#[cfg(feature = "std")]
+impl Cache {
+	fn new() -> Self {
+		Cache { child_root: HashMap::new() }
+	}
+}
+
 /// Patricia trie-based pairs storage essence.
 pub struct TrieBackendEssence<S: TrieBackendStorage<H>, H: Hasher> {
 	storage: S,
 	root: H::Out,
 	empty: H::Out,
+	#[cfg(feature = "std")]
+	pub(crate) cache: Arc<RwLock<Cache>>,
 }
 
 impl<S: TrieBackendStorage<H>, H: Hasher> TrieBackendEssence<S, H>
@@ -59,7 +78,13 @@ where
 {
 	/// Create new trie-based backend.
 	pub fn new(storage: S, root: H::Out) -> Self {
-		TrieBackendEssence { storage, root, empty: H::hash(&[0u8]) }
+		TrieBackendEssence {
+			storage,
+			root,
+			empty: H::hash(&[0u8]),
+			#[cfg(feature = "std")]
+			cache: Arc::new(RwLock::new(Cache::new())),
+		}
 	}
 
 	/// Get backend storage reference.
@@ -79,9 +104,19 @@ where
 
 	/// Set trie root. This is useful for testing.
 	pub fn set_root(&mut self, root: H::Out) {
+		// If root did change so can have cached content.
+		self.reset_cache();
 		self.root = root;
 	}
 
+	#[cfg(feature = "std")]
+	fn reset_cache(&mut self) {
+		self.cache = Arc::new(RwLock::new(Cache::new()));
+	}
+
+	#[cfg(not(feature = "std"))]
+	fn reset_cache(&mut self) {}
+
 	/// Consumes self and returns underlying storage.
 	pub fn into_storage(self) -> S {
 		self.storage
@@ -95,7 +130,24 @@ where
 
 	/// Access the root of the child storage in its parent trie
 	fn child_root(&self, child_info: &ChildInfo) -> Result<Option<StorageValue>> {
-		self.storage(child_info.prefixed_storage_key().as_slice())
+		#[cfg(feature = "std")]
+		{
+			if let Some(result) = self.cache.read().child_root.get(child_info.storage_key()) {
+				return Ok(result.clone())
+			}
+		}
+
+		let result = self.storage(child_info.prefixed_storage_key().as_slice())?;
+
+		#[cfg(feature = "std")]
+		{
+			self.cache
+				.write()
+				.child_root
+				.insert(child_info.storage_key().to_vec(), result.clone());
+		}
+
+		Ok(result)
 	}
 
 	/// Return the next key in the child trie i.e. the minimum key that is strictly superior to
-- 
GitLab