referrerpolicy=no-referrer-when-downgrade

sc_client_db/
pinned_blocks_cache.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19use schnellru::{Limiter, LruMap};
20use sp_runtime::{traits::Block as BlockT, Justifications};
21
22const LOG_TARGET: &str = "db::pin";
23const PINNING_CACHE_SIZE: usize = 2048;
24
25/// Entry for pinned blocks cache.
26struct PinnedBlockCacheEntry<Block: BlockT> {
27	/// How many times this item has been pinned
28	ref_count: u32,
29
30	/// Cached justifications for this block
31	pub justifications: Option<Option<Justifications>>,
32
33	/// Cached body for this block
34	pub body: Option<Option<Vec<Block::Extrinsic>>>,
35}
36
37impl<Block: BlockT> Default for PinnedBlockCacheEntry<Block> {
38	fn default() -> Self {
39		Self { ref_count: 0, justifications: None, body: None }
40	}
41}
42
43impl<Block: BlockT> PinnedBlockCacheEntry<Block> {
44	pub fn decrease_ref(&mut self) {
45		self.ref_count = self.ref_count.saturating_sub(1);
46	}
47
48	pub fn increase_ref(&mut self) {
49		self.ref_count = self.ref_count.saturating_add(1);
50	}
51
52	pub fn has_no_references(&self) -> bool {
53		self.ref_count == 0
54	}
55}
56
57/// A limiter for a map which is limited by the number of elements.
58#[derive(Copy, Clone, Debug)]
59struct LoggingByLengthLimiter {
60	max_length: usize,
61}
62
63impl LoggingByLengthLimiter {
64	/// Creates a new length limiter with a given `max_length`.
65	pub const fn new(max_length: usize) -> LoggingByLengthLimiter {
66		LoggingByLengthLimiter { max_length }
67	}
68}
69
70impl<Block: BlockT> Limiter<Block::Hash, PinnedBlockCacheEntry<Block>> for LoggingByLengthLimiter {
71	type KeyToInsert<'a> = Block::Hash;
72	type LinkType = usize;
73
74	fn is_over_the_limit(&self, length: usize) -> bool {
75		length > self.max_length
76	}
77
78	fn on_insert(
79		&mut self,
80		_length: usize,
81		key: Self::KeyToInsert<'_>,
82		value: PinnedBlockCacheEntry<Block>,
83	) -> Option<(Block::Hash, PinnedBlockCacheEntry<Block>)> {
84		if self.max_length > 0 {
85			Some((key, value))
86		} else {
87			None
88		}
89	}
90
91	fn on_replace(
92		&mut self,
93		_length: usize,
94		_old_key: &mut Block::Hash,
95		_new_key: Block::Hash,
96		_old_value: &mut PinnedBlockCacheEntry<Block>,
97		_new_value: &mut PinnedBlockCacheEntry<Block>,
98	) -> bool {
99		true
100	}
101
102	fn on_removed(&mut self, key: &mut Block::Hash, value: &mut PinnedBlockCacheEntry<Block>) {
103		// If reference count was larger than 0 on removal,
104		// the item was removed due to capacity limitations.
105		// Since the cache should be large enough for pinned items,
106		// we want to know about these evictions.
107		if value.ref_count > 0 {
108			log::warn!(
109				target: LOG_TARGET,
110				"Pinned block cache limit reached. Evicting value. hash = {}",
111				key
112			);
113		} else {
114			log::trace!(
115				target: LOG_TARGET,
116				"Evicting value from pinned block cache. hash = {}",
117				key
118			)
119		}
120	}
121
122	fn on_cleared(&mut self) {}
123
124	fn on_grow(&mut self, _new_memory_usage: usize) -> bool {
125		true
126	}
127}
128
129/// Reference counted cache for pinned block bodies and justifications.
130pub struct PinnedBlocksCache<Block: BlockT> {
131	cache: LruMap<Block::Hash, PinnedBlockCacheEntry<Block>, LoggingByLengthLimiter>,
132}
133
134impl<Block: BlockT> PinnedBlocksCache<Block> {
135	pub fn new() -> Self {
136		Self { cache: LruMap::new(LoggingByLengthLimiter::new(PINNING_CACHE_SIZE)) }
137	}
138
139	/// Increase reference count of an item.
140	/// Create an entry with empty value in the cache if necessary.
141	pub fn pin(&mut self, hash: Block::Hash) {
142		match self.cache.get_or_insert(hash, Default::default) {
143			Some(entry) => {
144				entry.increase_ref();
145				log::trace!(
146					target: LOG_TARGET,
147					"Bumped cache refcount. hash = {}, num_entries = {}",
148					hash,
149					self.cache.len()
150				);
151			},
152			None => {
153				log::warn!(target: LOG_TARGET, "Unable to bump reference count. hash = {}", hash)
154			},
155		};
156	}
157
158	/// Clear the cache
159	pub fn clear(&mut self) {
160		self.cache.clear();
161	}
162
163	/// Check if item is contained in the cache
164	pub fn contains(&self, hash: Block::Hash) -> bool {
165		self.cache.peek(&hash).is_some()
166	}
167
168	/// Attach body to an existing cache item
169	pub fn insert_body(&mut self, hash: Block::Hash, extrinsics: Option<Vec<Block::Extrinsic>>) {
170		match self.cache.peek_mut(&hash) {
171			Some(entry) => {
172				entry.body = Some(extrinsics);
173				log::trace!(
174					target: LOG_TARGET,
175					"Cached body. hash = {}, num_entries = {}",
176					hash,
177					self.cache.len()
178				);
179			},
180			None => log::warn!(
181				target: LOG_TARGET,
182				"Unable to insert body for uncached item. hash = {}",
183				hash
184			),
185		}
186	}
187
188	/// Attach justification to an existing cache item
189	pub fn insert_justifications(
190		&mut self,
191		hash: Block::Hash,
192		justifications: Option<Justifications>,
193	) {
194		match self.cache.peek_mut(&hash) {
195			Some(entry) => {
196				entry.justifications = Some(justifications);
197				log::trace!(
198					target: LOG_TARGET,
199					"Cached justification. hash = {}, num_entries = {}",
200					hash,
201					self.cache.len()
202				);
203			},
204			None => log::warn!(
205				target: LOG_TARGET,
206				"Unable to insert justifications for uncached item. hash = {}",
207				hash
208			),
209		}
210	}
211
212	/// Decreases reference count of an item.
213	/// If the count hits 0, the item is removed.
214	pub fn unpin(&mut self, hash: Block::Hash) {
215		if let Some(entry) = self.cache.peek_mut(&hash) {
216			entry.decrease_ref();
217			if entry.has_no_references() {
218				self.cache.remove(&hash);
219			}
220		}
221	}
222
223	/// Get justifications for cached block
224	pub fn justifications(&self, hash: &Block::Hash) -> Option<&Option<Justifications>> {
225		self.cache.peek(hash).and_then(|entry| entry.justifications.as_ref())
226	}
227
228	/// Get body for cached block
229	pub fn body(&self, hash: &Block::Hash) -> Option<&Option<Vec<Block::Extrinsic>>> {
230		self.cache.peek(hash).and_then(|entry| entry.body.as_ref())
231	}
232}