referrerpolicy=no-referrer-when-downgrade

sc_client_db/
stats.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
19//! Database usage statistics
20
21use std::sync::atomic::{AtomicU64, Ordering as AtomicOrdering};
22
23/// Accumulated usage statistics for state queries.
24pub struct StateUsageStats {
25	started: std::time::Instant,
26	reads: AtomicU64,
27	bytes_read: AtomicU64,
28	writes: AtomicU64,
29	bytes_written: AtomicU64,
30	writes_nodes: AtomicU64,
31	bytes_written_nodes: AtomicU64,
32	removed_nodes: AtomicU64,
33	bytes_removed_nodes: AtomicU64,
34	reads_cache: AtomicU64,
35	bytes_read_cache: AtomicU64,
36}
37
38impl StateUsageStats {
39	/// New empty usage stats.
40	pub fn new() -> Self {
41		Self {
42			started: std::time::Instant::now(),
43			reads: 0.into(),
44			bytes_read: 0.into(),
45			writes: 0.into(),
46			bytes_written: 0.into(),
47			writes_nodes: 0.into(),
48			bytes_written_nodes: 0.into(),
49			removed_nodes: 0.into(),
50			bytes_removed_nodes: 0.into(),
51			reads_cache: 0.into(),
52			bytes_read_cache: 0.into(),
53		}
54	}
55
56	/// Tally one read operation, of some length.
57	pub fn tally_read(&self, data_bytes: u64, cache: bool) {
58		self.reads.fetch_add(1, AtomicOrdering::Relaxed);
59		self.bytes_read.fetch_add(data_bytes, AtomicOrdering::Relaxed);
60		if cache {
61			self.reads_cache.fetch_add(1, AtomicOrdering::Relaxed);
62			self.bytes_read_cache.fetch_add(data_bytes, AtomicOrdering::Relaxed);
63		}
64	}
65
66	/// Tally one key read.
67	pub fn tally_key_read(&self, key: &[u8], val: Option<&Vec<u8>>, cache: bool) {
68		self.tally_read(
69			key.len() as u64 + val.as_ref().map(|x| x.len() as u64).unwrap_or(0),
70			cache,
71		);
72	}
73
74	/// Tally one child key read.
75	pub fn tally_child_key_read(
76		&self,
77		key: &(Vec<u8>, Vec<u8>),
78		val: Option<Vec<u8>>,
79		cache: bool,
80	) -> Option<Vec<u8>> {
81		let bytes = key.0.len() + key.1.len() + val.as_ref().map(|x| x.len()).unwrap_or(0);
82		self.tally_read(bytes as u64, cache);
83		val
84	}
85
86	/// Tally some write trie nodes operations, including their byte count.
87	pub fn tally_writes_nodes(&self, ops: u64, data_bytes: u64) {
88		self.writes_nodes.fetch_add(ops, AtomicOrdering::Relaxed);
89		self.bytes_written_nodes.fetch_add(data_bytes, AtomicOrdering::Relaxed);
90	}
91
92	/// Tally some removed trie nodes operations, including their byte count.
93	pub fn tally_removed_nodes(&self, ops: u64, data_bytes: u64) {
94		self.removed_nodes.fetch_add(ops, AtomicOrdering::Relaxed);
95		self.bytes_removed_nodes.fetch_add(data_bytes, AtomicOrdering::Relaxed);
96	}
97
98	/// Tally some write trie nodes operations, including their byte count.
99	pub fn tally_writes(&self, ops: u64, data_bytes: u64) {
100		self.writes.fetch_add(ops, AtomicOrdering::Relaxed);
101		self.bytes_written.fetch_add(data_bytes, AtomicOrdering::Relaxed);
102	}
103
104	/// Merge state machine usage info.
105	pub fn merge_sm(&self, info: sp_state_machine::UsageInfo) {
106		self.reads.fetch_add(info.reads.ops, AtomicOrdering::Relaxed);
107		self.bytes_read.fetch_add(info.reads.bytes, AtomicOrdering::Relaxed);
108		self.writes_nodes.fetch_add(info.nodes_writes.ops, AtomicOrdering::Relaxed);
109		self.bytes_written_nodes
110			.fetch_add(info.nodes_writes.bytes, AtomicOrdering::Relaxed);
111		self.removed_nodes.fetch_add(info.removed_nodes.ops, AtomicOrdering::Relaxed);
112		self.bytes_removed_nodes
113			.fetch_add(info.removed_nodes.bytes, AtomicOrdering::Relaxed);
114		self.reads_cache.fetch_add(info.cache_reads.ops, AtomicOrdering::Relaxed);
115		self.bytes_read_cache.fetch_add(info.cache_reads.bytes, AtomicOrdering::Relaxed);
116	}
117
118	/// Returns the collected `UsageInfo` and resets the internal state.
119	pub fn take(&self) -> sp_state_machine::UsageInfo {
120		use sp_state_machine::UsageUnit;
121
122		fn unit(ops: &AtomicU64, bytes: &AtomicU64) -> UsageUnit {
123			UsageUnit {
124				ops: ops.swap(0, AtomicOrdering::Relaxed),
125				bytes: bytes.swap(0, AtomicOrdering::Relaxed),
126			}
127		}
128
129		sp_state_machine::UsageInfo {
130			reads: unit(&self.reads, &self.bytes_read),
131			writes: unit(&self.writes, &self.bytes_written),
132			nodes_writes: unit(&self.writes_nodes, &self.bytes_written_nodes),
133			removed_nodes: unit(&self.removed_nodes, &self.bytes_removed_nodes),
134			cache_reads: unit(&self.reads_cache, &self.bytes_read_cache),
135			modified_reads: Default::default(),
136			overlay_writes: Default::default(),
137			// TODO: Proper tracking state of memory footprint here requires
138			//       imposing `MallocSizeOf` requirement on half of the codebase,
139			//       so it is an open question how to do it better
140			memory: 0,
141			started: self.started,
142			span: self.started.elapsed(),
143		}
144	}
145}