From c168a77e264cbdd9de8665fa04fc2798a7d56a0e Mon Sep 17 00:00:00 2001
From: ordian <write@reusable.software>
Date: Mon, 28 Aug 2023 19:04:11 +0200
Subject: [PATCH] deps: replace `lru` with `schnellru` (#1217)

* deps: replace lru with schnellru

* bring the peace to the galaxy
---
 Cargo.lock                                    |  27 +--
 cumulus/client/consensus/aura/Cargo.toml      |   2 +-
 .../aura/src/equivocation_import_queue.rs     |  17 +-
 .../relay-chain-minimal-node/Cargo.toml       |   2 +-
 .../src/collator_overseer.rs                  |   4 +-
 .../relay-chain-rpc-interface/Cargo.toml      |   2 +-
 .../src/reconnecting_ws_client.rs             |  11 +-
 polkadot/node/core/approval-voting/Cargo.toml |   2 +-
 .../node/core/approval-voting/src/import.rs   |  10 +-
 polkadot/node/core/approval-voting/src/lib.rs |  19 +-
 .../node/core/dispute-coordinator/Cargo.toml  |   2 +-
 .../node/core/dispute-coordinator/src/lib.rs  |   5 +-
 .../dispute-coordinator/src/scraping/mod.rs   |  18 +-
 polkadot/node/core/runtime-api/Cargo.toml     |   2 +-
 polkadot/node/core/runtime-api/src/cache.rs   | 219 +++++++++---------
 .../availability-distribution/Cargo.toml      |   2 +-
 .../src/requester/session_cache.rs            |  12 +-
 .../network/availability-recovery/Cargo.toml  |   2 +-
 .../network/availability-recovery/src/lib.rs  |  15 +-
 .../network/dispute-distribution/Cargo.toml   |   2 +-
 .../network/dispute-distribution/src/lib.rs   |   5 +-
 .../dispute-distribution/src/receiver/mod.rs  |   4 +-
 polkadot/node/overseer/Cargo.toml             |   2 +-
 polkadot/node/overseer/src/dummy.rs           |   4 +-
 polkadot/node/overseer/src/lib.rs             |  13 +-
 polkadot/node/primitives/src/lib.rs           |   8 +-
 polkadot/node/service/Cargo.toml              |   2 +-
 polkadot/node/service/src/overseer.rs         |   4 +-
 polkadot/node/subsystem-util/Cargo.toml       |   2 +-
 .../node/subsystem-util/src/runtime/mod.rs    |  25 +-
 .../availability/availability-recovery.md     |   2 +-
 .../src/node/disputes/dispute-coordinator.md  |   4 +-
 32 files changed, 208 insertions(+), 242 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index c755be63042..40aaf2d0d9e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3332,7 +3332,6 @@ dependencies = [
  "cumulus-primitives-parachain-inherent",
  "cumulus-relay-chain-interface",
  "futures",
- "lru 0.10.1",
  "parity-scale-codec",
  "polkadot-node-primitives",
  "polkadot-node-subsystem",
@@ -3344,6 +3343,7 @@ dependencies = [
  "sc-consensus-babe",
  "sc-consensus-slots",
  "sc-telemetry",
+ "schnellru",
  "sp-api",
  "sp-application-crypto",
  "sp-block-builder",
@@ -3822,7 +3822,6 @@ dependencies = [
  "cumulus-relay-chain-interface",
  "cumulus-relay-chain-rpc-interface",
  "futures",
- "lru 0.11.0",
  "polkadot-availability-recovery",
  "polkadot-collator-protocol",
  "polkadot-core-primitives",
@@ -3839,6 +3838,7 @@ dependencies = [
  "sc-service",
  "sc-tracing",
  "sc-utils",
+ "schnellru",
  "sp-api",
  "sp-consensus",
  "sp-consensus-babe",
@@ -3857,7 +3857,6 @@ dependencies = [
  "futures",
  "futures-timer",
  "jsonrpsee",
- "lru 0.11.0",
  "parity-scale-codec",
  "pin-project",
  "polkadot-overseer",
@@ -3865,6 +3864,7 @@ dependencies = [
  "sc-client-api",
  "sc-rpc-api",
  "sc-service",
+ "schnellru",
  "serde",
  "serde_json",
  "smoldot",
@@ -7765,9 +7765,6 @@ name = "lru"
 version = "0.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "eedb2bdbad7e0634f83989bf596f497b070130daaa398ab22d84c39e266deec5"
-dependencies = [
- "hashbrown 0.14.0",
-]
 
 [[package]]
 name = "lru-cache"
@@ -11696,7 +11693,6 @@ dependencies = [
  "fatality",
  "futures",
  "futures-timer",
- "lru 0.11.0",
  "parity-scale-codec",
  "polkadot-erasure-coding",
  "polkadot-node-network-protocol",
@@ -11708,6 +11704,7 @@ dependencies = [
  "polkadot-primitives-test-helpers",
  "rand 0.8.5",
  "sc-network",
+ "schnellru",
  "sp-core",
  "sp-keyring",
  "sp-keystore",
@@ -11726,7 +11723,6 @@ dependencies = [
  "futures",
  "futures-timer",
  "log",
- "lru 0.11.0",
  "parity-scale-codec",
  "polkadot-erasure-coding",
  "polkadot-node-network-protocol",
@@ -11738,6 +11734,7 @@ dependencies = [
  "polkadot-primitives-test-helpers",
  "rand 0.8.5",
  "sc-network",
+ "schnellru",
  "sp-application-crypto",
  "sp-core",
  "sp-keyring",
@@ -11827,7 +11824,6 @@ dependencies = [
  "futures-timer",
  "indexmap 1.9.3",
  "lazy_static",
- "lru 0.11.0",
  "parity-scale-codec",
  "polkadot-erasure-coding",
  "polkadot-node-network-protocol",
@@ -11839,6 +11835,7 @@ dependencies = [
  "polkadot-primitives-test-helpers",
  "sc-keystore",
  "sc-network",
+ "schnellru",
  "sp-application-crypto",
  "sp-keyring",
  "sp-keystore",
@@ -11951,7 +11948,6 @@ dependencies = [
  "futures-timer",
  "kvdb",
  "kvdb-memorydb",
- "lru 0.11.0",
  "merlin 2.0.1",
  "parity-scale-codec",
  "parking_lot 0.12.1",
@@ -11965,6 +11961,7 @@ dependencies = [
  "polkadot-primitives-test-helpers",
  "rand_core 0.5.1",
  "sc-keystore",
+ "schnellru",
  "schnorrkel 0.9.1",
  "sp-application-crypto",
  "sp-consensus",
@@ -12125,7 +12122,6 @@ dependencies = [
  "futures-timer",
  "kvdb",
  "kvdb-memorydb",
- "lru 0.11.0",
  "parity-scale-codec",
  "polkadot-node-primitives",
  "polkadot-node-subsystem",
@@ -12134,6 +12130,7 @@ dependencies = [
  "polkadot-primitives",
  "polkadot-primitives-test-helpers",
  "sc-keystore",
+ "schnellru",
  "sp-application-crypto",
  "sp-core",
  "sp-keyring",
@@ -12333,7 +12330,6 @@ version = "1.0.0"
 dependencies = [
  "async-trait",
  "futures",
- "lru 0.11.0",
  "polkadot-node-metrics",
  "polkadot-node-primitives",
  "polkadot-node-subsystem",
@@ -12341,6 +12337,7 @@ dependencies = [
  "polkadot-node-subsystem-types",
  "polkadot-primitives",
  "polkadot-primitives-test-helpers",
+ "schnellru",
  "sp-api",
  "sp-consensus-babe",
  "sp-core",
@@ -12504,7 +12501,6 @@ dependencies = [
  "kvdb-shared-tests",
  "lazy_static",
  "log",
- "lru 0.11.0",
  "parity-db",
  "parity-scale-codec",
  "parking_lot 0.11.2",
@@ -12520,6 +12516,7 @@ dependencies = [
  "polkadot-primitives-test-helpers",
  "prioritized-metered-channel",
  "rand 0.8.5",
+ "schnellru",
  "sp-application-crypto",
  "sp-core",
  "sp-keystore",
@@ -12537,7 +12534,6 @@ dependencies = [
  "femme",
  "futures",
  "futures-timer",
- "lru 0.11.0",
  "orchestra",
  "parking_lot 0.12.1",
  "polkadot-node-metrics",
@@ -12548,6 +12544,7 @@ dependencies = [
  "polkadot-primitives-test-helpers",
  "prioritized-metered-channel",
  "sc-client-api",
+ "schnellru",
  "sp-api",
  "sp-core",
  "tikv-jemalloc-ctl",
@@ -12992,7 +12989,6 @@ dependencies = [
  "kvdb",
  "kvdb-rocksdb",
  "log",
- "lru 0.11.0",
  "mmr-gadget",
  "pallet-babe",
  "pallet-im-online",
@@ -13066,6 +13062,7 @@ dependencies = [
  "sc-telemetry",
  "sc-transaction-pool",
  "sc-transaction-pool-api",
+ "schnellru",
  "serde",
  "serde_json",
  "serial_test",
diff --git a/cumulus/client/consensus/aura/Cargo.toml b/cumulus/client/consensus/aura/Cargo.toml
index d5781d5ed81..030ab705f16 100644
--- a/cumulus/client/consensus/aura/Cargo.toml
+++ b/cumulus/client/consensus/aura/Cargo.toml
@@ -10,7 +10,7 @@ async-trait = "0.1.73"
 codec = { package = "parity-scale-codec", version = "3.0.0", features = [ "derive" ] }
 futures = "0.3.28"
 tracing = "0.1.37"
-lru = "0.10.0"
+schnellru = "0.2.1"
 
 # Substrate
 sc-client-api = { path = "../../../../substrate/client/api" }
diff --git a/cumulus/client/consensus/aura/src/equivocation_import_queue.rs b/cumulus/client/consensus/aura/src/equivocation_import_queue.rs
index 682b7b91a65..919e0da39b1 100644
--- a/cumulus/client/consensus/aura/src/equivocation_import_queue.rs
+++ b/cumulus/client/consensus/aura/src/equivocation_import_queue.rs
@@ -21,7 +21,7 @@
 /// should be thrown out and which ones should be kept.
 use codec::Codec;
 use cumulus_client_consensus_common::ParachainBlockImportMarker;
-use lru::LruCache;
+use schnellru::{ByLength, LruMap};
 
 use sc_consensus::{
 	import_queue::{BasicQueue, Verifier as VerifierT},
@@ -36,27 +36,28 @@ use sp_consensus_aura::{AuraApi, Slot, SlotDuration};
 use sp_core::crypto::Pair;
 use sp_inherents::{CreateInherentDataProviders, InherentDataProvider};
 use sp_runtime::traits::{Block as BlockT, Header as HeaderT};
-use std::{fmt::Debug, num::NonZeroUsize, sync::Arc};
+use std::{fmt::Debug, sync::Arc};
 
-const LRU_WINDOW: usize = 256;
+const LRU_WINDOW: u32 = 256;
 const EQUIVOCATION_LIMIT: usize = 16;
 
 struct NaiveEquivocationDefender {
-	cache: LruCache<u64, usize>,
+	cache: LruMap<u64, usize>,
 }
 
 impl Default for NaiveEquivocationDefender {
 	fn default() -> Self {
-		NaiveEquivocationDefender {
-			cache: LruCache::new(NonZeroUsize::new(LRU_WINDOW).expect("window > 0; qed")),
-		}
+		NaiveEquivocationDefender { cache: LruMap::new(ByLength::new(LRU_WINDOW)) }
 	}
 }
 
 impl NaiveEquivocationDefender {
 	// return `true` if equivocation is beyond the limit.
 	fn insert_and_check(&mut self, slot: Slot) -> bool {
-		let val = self.cache.get_or_insert_mut(*slot, || 0);
+		let val = self
+			.cache
+			.get_or_insert(*slot, || 0)
+			.expect("insertion with ByLength limiter always succeeds; qed");
 		if *val == EQUIVOCATION_LIMIT {
 			true
 		} else {
diff --git a/cumulus/client/relay-chain-minimal-node/Cargo.toml b/cumulus/client/relay-chain-minimal-node/Cargo.toml
index 7fc53d17470..c3914573aab 100644
--- a/cumulus/client/relay-chain-minimal-node/Cargo.toml
+++ b/cumulus/client/relay-chain-minimal-node/Cargo.toml
@@ -36,7 +36,7 @@ cumulus-relay-chain-rpc-interface = { path = "../relay-chain-rpc-interface" }
 cumulus-primitives-core = { path = "../../primitives/core" }
 
 array-bytes = "6.1"
-lru = "0.11.0"
+schnellru = "0.2.1"
 tracing = "0.1.37"
 async-trait = "0.1.73"
 futures = "0.3.28"
diff --git a/cumulus/client/relay-chain-minimal-node/src/collator_overseer.rs b/cumulus/client/relay-chain-minimal-node/src/collator_overseer.rs
index 4d843eb224f..0acd04e73cd 100644
--- a/cumulus/client/relay-chain-minimal-node/src/collator_overseer.rs
+++ b/cumulus/client/relay-chain-minimal-node/src/collator_overseer.rs
@@ -15,7 +15,7 @@
 // along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
 
 use futures::{select, StreamExt};
-use lru::LruCache;
+use schnellru::{ByLength, LruMap};
 use std::sync::Arc;
 
 use polkadot_availability_recovery::AvailabilityRecoverySubsystem;
@@ -157,7 +157,7 @@ fn build_overseer(
 		.span_per_active_leaf(Default::default())
 		.active_leaves(Default::default())
 		.supports_parachains(runtime_client)
-		.known_leaves(LruCache::new(KNOWN_LEAVES_CACHE_SIZE))
+		.known_leaves(LruMap::new(ByLength::new(KNOWN_LEAVES_CACHE_SIZE)))
 		.metrics(Metrics::register(registry)?)
 		.spawner(spawner);
 
diff --git a/cumulus/client/relay-chain-rpc-interface/Cargo.toml b/cumulus/client/relay-chain-rpc-interface/Cargo.toml
index d73b9ae8989..2356e747333 100644
--- a/cumulus/client/relay-chain-rpc-interface/Cargo.toml
+++ b/cumulus/client/relay-chain-rpc-interface/Cargo.toml
@@ -34,7 +34,7 @@ async-trait = "0.1.73"
 url = "2.4.0"
 serde_json = "1.0.105"
 serde = "1.0.183"
-lru = "0.11.0"
+schnellru = "0.2.1"
 smoldot = { version = "0.11.0",  default_features = false, features = ["std"]}
 smoldot-light = { version = "0.9.0", default_features = false, features = ["std"] }
 either = "1.8.1"
diff --git a/cumulus/client/relay-chain-rpc-interface/src/reconnecting_ws_client.rs b/cumulus/client/relay-chain-rpc-interface/src/reconnecting_ws_client.rs
index 5add8a96ef1..5a35b2b5bfa 100644
--- a/cumulus/client/relay-chain-rpc-interface/src/reconnecting_ws_client.rs
+++ b/cumulus/client/relay-chain-rpc-interface/src/reconnecting_ws_client.rs
@@ -31,10 +31,10 @@ use jsonrpsee::{
 	},
 	ws_client::WsClientBuilder,
 };
-use lru::LruCache;
 use sc_rpc_api::chain::ChainApiClient;
+use schnellru::{ByLength, LruMap};
 use sp_runtime::generic::SignedBlock;
-use std::{num::NonZeroUsize, sync::Arc};
+use std::sync::Arc;
 use tokio::sync::mpsc::{
 	channel as tokio_channel, Receiver as TokioReceiver, Sender as TokioSender,
 };
@@ -307,8 +307,7 @@ impl ReconnectingWebsocketWorker {
 			return
 		};
 
-		let mut imported_blocks_cache =
-			LruCache::new(NonZeroUsize::new(40).expect("40 is nonzero; qed."));
+		let mut imported_blocks_cache = LruMap::new(ByLength::new(40));
 		let mut should_reconnect = ConnectionStatus::Connected;
 		let mut last_seen_finalized_num: RelayNumber = 0;
 		loop {
@@ -365,7 +364,7 @@ impl ReconnectingWebsocketWorker {
 					match import_event {
 						Some(Ok(header)) => {
 							let hash = header.hash();
-							if imported_blocks_cache.contains(&hash) {
+							if imported_blocks_cache.peek(&hash).is_some() {
 								tracing::debug!(
 									target: LOG_TARGET,
 									number = header.number,
@@ -374,7 +373,7 @@ impl ReconnectingWebsocketWorker {
 								);
 								continue;
 							}
-							imported_blocks_cache.put(hash, ());
+							imported_blocks_cache.insert(hash, ());
 							distribute_header(header, &mut self.imported_header_listeners);
 						},
 						None => {
diff --git a/polkadot/node/core/approval-voting/Cargo.toml b/polkadot/node/core/approval-voting/Cargo.toml
index 5ae99d44d0a..593f6bff4c8 100644
--- a/polkadot/node/core/approval-voting/Cargo.toml
+++ b/polkadot/node/core/approval-voting/Cargo.toml
@@ -11,7 +11,7 @@ futures-timer = "3.0.2"
 parity-scale-codec = { version = "3.6.1", default-features = false, features = ["bit-vec", "derive"] }
 gum = { package = "tracing-gum", path = "../../gum" }
 bitvec = { version = "1.0.0", default-features = false, features = ["alloc"] }
-lru = "0.11.0"
+schnellru = "0.2.1"
 merlin = "2.0"
 schnorrkel = "0.9.1"
 kvdb = "0.13.0"
diff --git a/polkadot/node/core/approval-voting/src/import.rs b/polkadot/node/core/approval-voting/src/import.rs
index c504ba71b3c..6f376fc6fcc 100644
--- a/polkadot/node/core/approval-voting/src/import.rs
+++ b/polkadot/node/core/approval-voting/src/import.rs
@@ -643,7 +643,7 @@ pub(crate) mod tests {
 			blank_state(),
 			RuntimeInfo::new_with_config(RuntimeInfoConfig {
 				keystore: None,
-				session_cache_lru_size: DISPUTE_WINDOW.into(),
+				session_cache_lru_size: DISPUTE_WINDOW.get(),
 			}),
 		)
 	}
@@ -755,7 +755,7 @@ pub(crate) mod tests {
 
 			let mut runtime_info = RuntimeInfo::new_with_config(RuntimeInfoConfig {
 				keystore: None,
-				session_cache_lru_size: DISPUTE_WINDOW.into(),
+				session_cache_lru_size: DISPUTE_WINDOW.get(),
 			});
 
 			let header = header.clone();
@@ -878,7 +878,7 @@ pub(crate) mod tests {
 		let test_fut = {
 			let mut runtime_info = RuntimeInfo::new_with_config(RuntimeInfoConfig {
 				keystore: None,
-				session_cache_lru_size: DISPUTE_WINDOW.into(),
+				session_cache_lru_size: DISPUTE_WINDOW.get(),
 			});
 
 			let header = header.clone();
@@ -994,7 +994,7 @@ pub(crate) mod tests {
 		let test_fut = {
 			let mut runtime_info = RuntimeInfo::new_with_config(RuntimeInfoConfig {
 				keystore: None,
-				session_cache_lru_size: DISPUTE_WINDOW.into(),
+				session_cache_lru_size: DISPUTE_WINDOW.get(),
 			});
 
 			let header = header.clone();
@@ -1092,7 +1092,7 @@ pub(crate) mod tests {
 
 			let mut runtime_info = RuntimeInfo::new_with_config(RuntimeInfoConfig {
 				keystore: None,
-				session_cache_lru_size: DISPUTE_WINDOW.into(),
+				session_cache_lru_size: DISPUTE_WINDOW.get(),
 			});
 
 			let header = header.clone();
diff --git a/polkadot/node/core/approval-voting/src/lib.rs b/polkadot/node/core/approval-voting/src/lib.rs
index b29e47b4c43..e99572b31c4 100644
--- a/polkadot/node/core/approval-voting/src/lib.rs
+++ b/polkadot/node/core/approval-voting/src/lib.rs
@@ -69,11 +69,12 @@ use std::{
 	collections::{
 		btree_map::Entry as BTMEntry, hash_map::Entry as HMEntry, BTreeMap, HashMap, HashSet,
 	},
-	num::NonZeroUsize,
 	sync::Arc,
 	time::Duration,
 };
 
+use schnellru::{ByLength, LruMap};
+
 use approval_checking::RequiredTranches;
 use criteria::{AssignmentCriteria, RealAssignmentCriteria};
 use persisted_entries::{ApprovalEntry, BlockEntry, CandidateEntry};
@@ -102,10 +103,7 @@ const APPROVAL_CHECKING_TIMEOUT: Duration = Duration::from_secs(120);
 /// Value rather arbitrarily: Should not be hit in practice, it exists to more easily diagnose dead
 /// lock issues for example.
 const WAIT_FOR_SIGS_TIMEOUT: Duration = Duration::from_millis(500);
-const APPROVAL_CACHE_SIZE: NonZeroUsize = match NonZeroUsize::new(1024) {
-	Some(cap) => cap,
-	None => panic!("Approval cache size must be non-zero."),
-};
+const APPROVAL_CACHE_SIZE: u32 = 1024;
 
 const TICK_TOO_FAR_IN_FUTURE: Tick = 20; // 10 seconds.
 const APPROVAL_DELAY: Tick = 2;
@@ -627,7 +625,7 @@ impl CurrentlyCheckingSet {
 
 	pub async fn next(
 		&mut self,
-		approvals_cache: &mut lru::LruCache<CandidateHash, ApprovalOutcome>,
+		approvals_cache: &mut LruMap<CandidateHash, ApprovalOutcome>,
 	) -> (HashSet<Hash>, ApprovalState) {
 		if !self.currently_checking.is_empty() {
 			if let Some(approval_state) = self.currently_checking.next().await {
@@ -635,7 +633,8 @@ impl CurrentlyCheckingSet {
 					.candidate_hash_map
 					.remove(&approval_state.candidate_hash)
 					.unwrap_or_default();
-				approvals_cache.put(approval_state.candidate_hash, approval_state.approval_outcome);
+				approvals_cache
+					.insert(approval_state.candidate_hash, approval_state.approval_outcome);
 				return (out, approval_state)
 			}
 		}
@@ -782,11 +781,11 @@ where
 	// `None` on start-up. Gets initialized/updated on leaf update
 	let mut session_info_provider = RuntimeInfo::new_with_config(RuntimeInfoConfig {
 		keystore: None,
-		session_cache_lru_size: DISPUTE_WINDOW.into(),
+		session_cache_lru_size: DISPUTE_WINDOW.get(),
 	});
 	let mut wakeups = Wakeups::default();
 	let mut currently_checking_set = CurrentlyCheckingSet::default();
-	let mut approvals_cache = lru::LruCache::new(APPROVAL_CACHE_SIZE);
+	let mut approvals_cache = LruMap::new(ByLength::new(APPROVAL_CACHE_SIZE));
 
 	let mut last_finalized_height: Option<BlockNumber> = {
 		let (tx, rx) = oneshot::channel();
@@ -922,7 +921,7 @@ async fn handle_actions<Context>(
 	metrics: &Metrics,
 	wakeups: &mut Wakeups,
 	currently_checking_set: &mut CurrentlyCheckingSet,
-	approvals_cache: &mut lru::LruCache<CandidateHash, ApprovalOutcome>,
+	approvals_cache: &mut LruMap<CandidateHash, ApprovalOutcome>,
 	mode: &mut Mode,
 	actions: Vec<Action>,
 ) -> SubsystemResult<bool> {
diff --git a/polkadot/node/core/dispute-coordinator/Cargo.toml b/polkadot/node/core/dispute-coordinator/Cargo.toml
index 465c108211e..5698114d278 100644
--- a/polkadot/node/core/dispute-coordinator/Cargo.toml
+++ b/polkadot/node/core/dispute-coordinator/Cargo.toml
@@ -11,7 +11,7 @@ gum = { package = "tracing-gum", path = "../../gum" }
 parity-scale-codec = "3.6.1"
 kvdb = "0.13.0"
 thiserror = "1.0.31"
-lru = "0.11.0"
+schnellru = "0.2.1"
 fatality = "0.0.6"
 
 polkadot-primitives = { path = "../../../primitives" }
diff --git a/polkadot/node/core/dispute-coordinator/src/lib.rs b/polkadot/node/core/dispute-coordinator/src/lib.rs
index a2c500e08e2..f7e3d0657f2 100644
--- a/polkadot/node/core/dispute-coordinator/src/lib.rs
+++ b/polkadot/node/core/dispute-coordinator/src/lib.rs
@@ -25,7 +25,7 @@
 //! When importing a dispute vote from another node, this will trigger dispute participation to
 //! recover and validate the block.
 
-use std::{num::NonZeroUsize, sync::Arc};
+use std::sync::Arc;
 
 use futures::FutureExt;
 
@@ -222,8 +222,7 @@ impl DisputeCoordinatorSubsystem {
 			// keep all sessions for a dispute window
 			let mut runtime_info = RuntimeInfo::new_with_config(RuntimeInfoConfig {
 				keystore: None,
-				session_cache_lru_size: NonZeroUsize::new(DISPUTE_WINDOW.get() as usize)
-					.expect("DISPUTE_WINDOW can't be 0; qed."),
+				session_cache_lru_size: DISPUTE_WINDOW.get(),
 			});
 			let mut overlay_db = OverlayedBackend::new(&mut backend);
 			let (
diff --git a/polkadot/node/core/dispute-coordinator/src/scraping/mod.rs b/polkadot/node/core/dispute-coordinator/src/scraping/mod.rs
index f93ad0abab9..67434bca85d 100644
--- a/polkadot/node/core/dispute-coordinator/src/scraping/mod.rs
+++ b/polkadot/node/core/dispute-coordinator/src/scraping/mod.rs
@@ -14,13 +14,10 @@
 // You should have received a copy of the GNU General Public License
 // along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
 
-use std::{
-	collections::{BTreeMap, HashSet},
-	num::NonZeroUsize,
-};
+use std::collections::{BTreeMap, HashSet};
 
 use futures::channel::oneshot;
-use lru::LruCache;
+use schnellru::{ByLength, LruMap};
 
 use polkadot_node_primitives::{DISPUTE_CANDIDATE_LIFETIME_AFTER_FINALIZATION, MAX_FINALITY_LAG};
 use polkadot_node_subsystem::{
@@ -52,10 +49,7 @@ mod candidates;
 /// `last_observed_blocks` LRU. This means, this value should the very least be as large as the
 /// number of expected forks for keeping chain scraping efficient. Making the LRU much larger than
 /// that has very limited use.
-const LRU_OBSERVED_BLOCKS_CAPACITY: NonZeroUsize = match NonZeroUsize::new(20) {
-	Some(cap) => cap,
-	None => panic!("Observed blocks cache size must be non-zero"),
-};
+const LRU_OBSERVED_BLOCKS_CAPACITY: u32 = 20;
 
 /// `ScrapedUpdates`
 ///
@@ -173,7 +167,7 @@ pub struct ChainScraper {
 	///
 	/// We assume that ancestors of cached blocks are already processed, i.e. we have saved
 	/// corresponding included candidates.
-	last_observed_blocks: LruCache<Hash, ()>,
+	last_observed_blocks: LruMap<Hash, ()>,
 	/// Maps included candidate hashes to one or more relay block heights and hashes.
 	/// These correspond to all the relay blocks which marked a candidate as included,
 	/// and are needed to apply reversions in case a dispute is concluded against the
@@ -202,7 +196,7 @@ impl ChainScraper {
 		let mut s = Self {
 			included_candidates: candidates::ScrapedCandidates::new(),
 			backed_candidates: candidates::ScrapedCandidates::new(),
-			last_observed_blocks: LruCache::new(LRU_OBSERVED_BLOCKS_CAPACITY),
+			last_observed_blocks: LruMap::new(ByLength::new(LRU_OBSERVED_BLOCKS_CAPACITY)),
 			inclusions: Inclusions::new(),
 		};
 		let update =
@@ -288,7 +282,7 @@ impl ChainScraper {
 			},
 		}
 
-		self.last_observed_blocks.put(activated.hash, ());
+		self.last_observed_blocks.insert(activated.hash, ());
 
 		Ok(scraped_updates)
 	}
diff --git a/polkadot/node/core/runtime-api/Cargo.toml b/polkadot/node/core/runtime-api/Cargo.toml
index 1a74ccdf73d..de165ebcc5c 100644
--- a/polkadot/node/core/runtime-api/Cargo.toml
+++ b/polkadot/node/core/runtime-api/Cargo.toml
@@ -8,7 +8,7 @@ license.workspace = true
 [dependencies]
 futures = "0.3.21"
 gum = { package = "tracing-gum", path = "../../gum" }
-lru = "0.11.0"
+schnellru = "0.2.1"
 
 sp-consensus-babe = { path = "../../../../substrate/primitives/consensus/babe" }
 
diff --git a/polkadot/node/core/runtime-api/src/cache.rs b/polkadot/node/core/runtime-api/src/cache.rs
index 26aaf3fb6ec..cd22f37ddee 100644
--- a/polkadot/node/core/runtime-api/src/cache.rs
+++ b/polkadot/node/core/runtime-api/src/cache.rs
@@ -14,9 +14,9 @@
 // You should have received a copy of the GNU General Public License
 // along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
 
-use std::{collections::btree_map::BTreeMap, num::NonZeroUsize};
+use std::collections::btree_map::BTreeMap;
 
-use lru::LruCache;
+use schnellru::{ByLength, LruMap};
 use sp_consensus_babe::Epoch;
 
 use polkadot_primitives::{
@@ -32,77 +32,74 @@ use polkadot_primitives::{
 /// much if finality stalls (we only query state for unfinalized blocks + maybe latest finalized).
 /// In any case, a cache is an optimization. We should avoid a situation where having a large cache
 /// leads to OOM or puts pressure on other important stuff like PVF execution/preparation.
-const DEFAULT_CACHE_CAP: NonZeroUsize = match NonZeroUsize::new(128) {
-	Some(cap) => cap,
-	None => panic!("lru capacity must be non-zero"),
-};
+const DEFAULT_CACHE_CAP: u32 = 128;
 
 pub(crate) struct RequestResultCache {
-	authorities: LruCache<Hash, Vec<AuthorityDiscoveryId>>,
-	validators: LruCache<Hash, Vec<ValidatorId>>,
-	validator_groups: LruCache<Hash, (Vec<Vec<ValidatorIndex>>, GroupRotationInfo)>,
-	availability_cores: LruCache<Hash, Vec<CoreState>>,
+	authorities: LruMap<Hash, Vec<AuthorityDiscoveryId>>,
+	validators: LruMap<Hash, Vec<ValidatorId>>,
+	validator_groups: LruMap<Hash, (Vec<Vec<ValidatorIndex>>, GroupRotationInfo)>,
+	availability_cores: LruMap<Hash, Vec<CoreState>>,
 	persisted_validation_data:
-		LruCache<(Hash, ParaId, OccupiedCoreAssumption), Option<PersistedValidationData>>,
+		LruMap<(Hash, ParaId, OccupiedCoreAssumption), Option<PersistedValidationData>>,
 	assumed_validation_data:
-		LruCache<(ParaId, Hash), Option<(PersistedValidationData, ValidationCodeHash)>>,
-	check_validation_outputs: LruCache<(Hash, ParaId, CandidateCommitments), bool>,
-	session_index_for_child: LruCache<Hash, SessionIndex>,
-	validation_code: LruCache<(Hash, ParaId, OccupiedCoreAssumption), Option<ValidationCode>>,
-	validation_code_by_hash: LruCache<ValidationCodeHash, Option<ValidationCode>>,
-	candidate_pending_availability: LruCache<(Hash, ParaId), Option<CommittedCandidateReceipt>>,
-	candidate_events: LruCache<Hash, Vec<CandidateEvent>>,
-	session_executor_params: LruCache<SessionIndex, Option<ExecutorParams>>,
-	session_info: LruCache<SessionIndex, SessionInfo>,
-	dmq_contents: LruCache<(Hash, ParaId), Vec<InboundDownwardMessage<BlockNumber>>>,
+		LruMap<(ParaId, Hash), Option<(PersistedValidationData, ValidationCodeHash)>>,
+	check_validation_outputs: LruMap<(Hash, ParaId, CandidateCommitments), bool>,
+	session_index_for_child: LruMap<Hash, SessionIndex>,
+	validation_code: LruMap<(Hash, ParaId, OccupiedCoreAssumption), Option<ValidationCode>>,
+	validation_code_by_hash: LruMap<ValidationCodeHash, Option<ValidationCode>>,
+	candidate_pending_availability: LruMap<(Hash, ParaId), Option<CommittedCandidateReceipt>>,
+	candidate_events: LruMap<Hash, Vec<CandidateEvent>>,
+	session_executor_params: LruMap<SessionIndex, Option<ExecutorParams>>,
+	session_info: LruMap<SessionIndex, SessionInfo>,
+	dmq_contents: LruMap<(Hash, ParaId), Vec<InboundDownwardMessage<BlockNumber>>>,
 	inbound_hrmp_channels_contents:
-		LruCache<(Hash, ParaId), BTreeMap<ParaId, Vec<InboundHrmpMessage<BlockNumber>>>>,
-	current_babe_epoch: LruCache<Hash, Epoch>,
-	on_chain_votes: LruCache<Hash, Option<ScrapedOnChainVotes>>,
-	pvfs_require_precheck: LruCache<Hash, Vec<ValidationCodeHash>>,
+		LruMap<(Hash, ParaId), BTreeMap<ParaId, Vec<InboundHrmpMessage<BlockNumber>>>>,
+	current_babe_epoch: LruMap<Hash, Epoch>,
+	on_chain_votes: LruMap<Hash, Option<ScrapedOnChainVotes>>,
+	pvfs_require_precheck: LruMap<Hash, Vec<ValidationCodeHash>>,
 	validation_code_hash:
-		LruCache<(Hash, ParaId, OccupiedCoreAssumption), Option<ValidationCodeHash>>,
-	version: LruCache<Hash, u32>,
-	disputes: LruCache<Hash, Vec<(SessionIndex, CandidateHash, DisputeState<BlockNumber>)>>,
+		LruMap<(Hash, ParaId, OccupiedCoreAssumption), Option<ValidationCodeHash>>,
+	version: LruMap<Hash, u32>,
+	disputes: LruMap<Hash, Vec<(SessionIndex, CandidateHash, DisputeState<BlockNumber>)>>,
 	unapplied_slashes:
-		LruCache<Hash, Vec<(SessionIndex, CandidateHash, vstaging::slashing::PendingSlashes)>>,
+		LruMap<Hash, Vec<(SessionIndex, CandidateHash, vstaging::slashing::PendingSlashes)>>,
 	key_ownership_proof:
-		LruCache<(Hash, ValidatorId), Option<vstaging::slashing::OpaqueKeyOwnershipProof>>,
+		LruMap<(Hash, ValidatorId), Option<vstaging::slashing::OpaqueKeyOwnershipProof>>,
 
-	staging_para_backing_state: LruCache<(Hash, ParaId), Option<vstaging::BackingState>>,
-	staging_async_backing_params: LruCache<Hash, vstaging::AsyncBackingParams>,
+	staging_para_backing_state: LruMap<(Hash, ParaId), Option<vstaging::BackingState>>,
+	staging_async_backing_params: LruMap<Hash, vstaging::AsyncBackingParams>,
 }
 
 impl Default for RequestResultCache {
 	fn default() -> Self {
 		Self {
-			authorities: LruCache::new(DEFAULT_CACHE_CAP),
-			validators: LruCache::new(DEFAULT_CACHE_CAP),
-			validator_groups: LruCache::new(DEFAULT_CACHE_CAP),
-			availability_cores: LruCache::new(DEFAULT_CACHE_CAP),
-			persisted_validation_data: LruCache::new(DEFAULT_CACHE_CAP),
-			assumed_validation_data: LruCache::new(DEFAULT_CACHE_CAP),
-			check_validation_outputs: LruCache::new(DEFAULT_CACHE_CAP),
-			session_index_for_child: LruCache::new(DEFAULT_CACHE_CAP),
-			validation_code: LruCache::new(DEFAULT_CACHE_CAP),
-			validation_code_by_hash: LruCache::new(DEFAULT_CACHE_CAP),
-			candidate_pending_availability: LruCache::new(DEFAULT_CACHE_CAP),
-			candidate_events: LruCache::new(DEFAULT_CACHE_CAP),
-			session_executor_params: LruCache::new(DEFAULT_CACHE_CAP),
-			session_info: LruCache::new(DEFAULT_CACHE_CAP),
-			dmq_contents: LruCache::new(DEFAULT_CACHE_CAP),
-			inbound_hrmp_channels_contents: LruCache::new(DEFAULT_CACHE_CAP),
-			current_babe_epoch: LruCache::new(DEFAULT_CACHE_CAP),
-			on_chain_votes: LruCache::new(DEFAULT_CACHE_CAP),
-			pvfs_require_precheck: LruCache::new(DEFAULT_CACHE_CAP),
-			validation_code_hash: LruCache::new(DEFAULT_CACHE_CAP),
-			version: LruCache::new(DEFAULT_CACHE_CAP),
-			disputes: LruCache::new(DEFAULT_CACHE_CAP),
-			unapplied_slashes: LruCache::new(DEFAULT_CACHE_CAP),
-			key_ownership_proof: LruCache::new(DEFAULT_CACHE_CAP),
-
-			staging_para_backing_state: LruCache::new(DEFAULT_CACHE_CAP),
-			staging_async_backing_params: LruCache::new(DEFAULT_CACHE_CAP),
+			authorities: LruMap::new(ByLength::new(DEFAULT_CACHE_CAP)),
+			validators: LruMap::new(ByLength::new(DEFAULT_CACHE_CAP)),
+			validator_groups: LruMap::new(ByLength::new(DEFAULT_CACHE_CAP)),
+			availability_cores: LruMap::new(ByLength::new(DEFAULT_CACHE_CAP)),
+			persisted_validation_data: LruMap::new(ByLength::new(DEFAULT_CACHE_CAP)),
+			assumed_validation_data: LruMap::new(ByLength::new(DEFAULT_CACHE_CAP)),
+			check_validation_outputs: LruMap::new(ByLength::new(DEFAULT_CACHE_CAP)),
+			session_index_for_child: LruMap::new(ByLength::new(DEFAULT_CACHE_CAP)),
+			validation_code: LruMap::new(ByLength::new(DEFAULT_CACHE_CAP)),
+			validation_code_by_hash: LruMap::new(ByLength::new(DEFAULT_CACHE_CAP)),
+			candidate_pending_availability: LruMap::new(ByLength::new(DEFAULT_CACHE_CAP)),
+			candidate_events: LruMap::new(ByLength::new(DEFAULT_CACHE_CAP)),
+			session_executor_params: LruMap::new(ByLength::new(DEFAULT_CACHE_CAP)),
+			session_info: LruMap::new(ByLength::new(DEFAULT_CACHE_CAP)),
+			dmq_contents: LruMap::new(ByLength::new(DEFAULT_CACHE_CAP)),
+			inbound_hrmp_channels_contents: LruMap::new(ByLength::new(DEFAULT_CACHE_CAP)),
+			current_babe_epoch: LruMap::new(ByLength::new(DEFAULT_CACHE_CAP)),
+			on_chain_votes: LruMap::new(ByLength::new(DEFAULT_CACHE_CAP)),
+			pvfs_require_precheck: LruMap::new(ByLength::new(DEFAULT_CACHE_CAP)),
+			validation_code_hash: LruMap::new(ByLength::new(DEFAULT_CACHE_CAP)),
+			version: LruMap::new(ByLength::new(DEFAULT_CACHE_CAP)),
+			disputes: LruMap::new(ByLength::new(DEFAULT_CACHE_CAP)),
+			unapplied_slashes: LruMap::new(ByLength::new(DEFAULT_CACHE_CAP)),
+			key_ownership_proof: LruMap::new(ByLength::new(DEFAULT_CACHE_CAP)),
+
+			staging_para_backing_state: LruMap::new(ByLength::new(DEFAULT_CACHE_CAP)),
+			staging_async_backing_params: LruMap::new(ByLength::new(DEFAULT_CACHE_CAP)),
 		}
 	}
 }
@@ -112,7 +109,7 @@ impl RequestResultCache {
 		&mut self,
 		relay_parent: &Hash,
 	) -> Option<&Vec<AuthorityDiscoveryId>> {
-		self.authorities.get(relay_parent)
+		self.authorities.get(relay_parent).map(|v| &*v)
 	}
 
 	pub(crate) fn cache_authorities(
@@ -120,22 +117,22 @@ impl RequestResultCache {
 		relay_parent: Hash,
 		authorities: Vec<AuthorityDiscoveryId>,
 	) {
-		self.authorities.put(relay_parent, authorities);
+		self.authorities.insert(relay_parent, authorities);
 	}
 
 	pub(crate) fn validators(&mut self, relay_parent: &Hash) -> Option<&Vec<ValidatorId>> {
-		self.validators.get(relay_parent)
+		self.validators.get(relay_parent).map(|v| &*v)
 	}
 
 	pub(crate) fn cache_validators(&mut self, relay_parent: Hash, validators: Vec<ValidatorId>) {
-		self.validators.put(relay_parent, validators);
+		self.validators.insert(relay_parent, validators);
 	}
 
 	pub(crate) fn validator_groups(
 		&mut self,
 		relay_parent: &Hash,
 	) -> Option<&(Vec<Vec<ValidatorIndex>>, GroupRotationInfo)> {
-		self.validator_groups.get(relay_parent)
+		self.validator_groups.get(relay_parent).map(|v| &*v)
 	}
 
 	pub(crate) fn cache_validator_groups(
@@ -143,22 +140,22 @@ impl RequestResultCache {
 		relay_parent: Hash,
 		groups: (Vec<Vec<ValidatorIndex>>, GroupRotationInfo),
 	) {
-		self.validator_groups.put(relay_parent, groups);
+		self.validator_groups.insert(relay_parent, groups);
 	}
 
 	pub(crate) fn availability_cores(&mut self, relay_parent: &Hash) -> Option<&Vec<CoreState>> {
-		self.availability_cores.get(relay_parent)
+		self.availability_cores.get(relay_parent).map(|v| &*v)
 	}
 
 	pub(crate) fn cache_availability_cores(&mut self, relay_parent: Hash, cores: Vec<CoreState>) {
-		self.availability_cores.put(relay_parent, cores);
+		self.availability_cores.insert(relay_parent, cores);
 	}
 
 	pub(crate) fn persisted_validation_data(
 		&mut self,
 		key: (Hash, ParaId, OccupiedCoreAssumption),
 	) -> Option<&Option<PersistedValidationData>> {
-		self.persisted_validation_data.get(&key)
+		self.persisted_validation_data.get(&key).map(|v| &*v)
 	}
 
 	pub(crate) fn cache_persisted_validation_data(
@@ -166,14 +163,14 @@ impl RequestResultCache {
 		key: (Hash, ParaId, OccupiedCoreAssumption),
 		data: Option<PersistedValidationData>,
 	) {
-		self.persisted_validation_data.put(key, data);
+		self.persisted_validation_data.insert(key, data);
 	}
 
 	pub(crate) fn assumed_validation_data(
 		&mut self,
 		key: (Hash, ParaId, Hash),
 	) -> Option<&Option<(PersistedValidationData, ValidationCodeHash)>> {
-		self.assumed_validation_data.get(&(key.1, key.2))
+		self.assumed_validation_data.get(&(key.1, key.2)).map(|v| &*v)
 	}
 
 	pub(crate) fn cache_assumed_validation_data(
@@ -181,14 +178,14 @@ impl RequestResultCache {
 		key: (ParaId, Hash),
 		data: Option<(PersistedValidationData, ValidationCodeHash)>,
 	) {
-		self.assumed_validation_data.put(key, data);
+		self.assumed_validation_data.insert(key, data);
 	}
 
 	pub(crate) fn check_validation_outputs(
 		&mut self,
 		key: (Hash, ParaId, CandidateCommitments),
 	) -> Option<&bool> {
-		self.check_validation_outputs.get(&key)
+		self.check_validation_outputs.get(&key).map(|v| &*v)
 	}
 
 	pub(crate) fn cache_check_validation_outputs(
@@ -196,11 +193,11 @@ impl RequestResultCache {
 		key: (Hash, ParaId, CandidateCommitments),
 		value: bool,
 	) {
-		self.check_validation_outputs.put(key, value);
+		self.check_validation_outputs.insert(key, value);
 	}
 
 	pub(crate) fn session_index_for_child(&mut self, relay_parent: &Hash) -> Option<&SessionIndex> {
-		self.session_index_for_child.get(relay_parent)
+		self.session_index_for_child.get(relay_parent).map(|v| &*v)
 	}
 
 	pub(crate) fn cache_session_index_for_child(
@@ -208,14 +205,14 @@ impl RequestResultCache {
 		relay_parent: Hash,
 		index: SessionIndex,
 	) {
-		self.session_index_for_child.put(relay_parent, index);
+		self.session_index_for_child.insert(relay_parent, index);
 	}
 
 	pub(crate) fn validation_code(
 		&mut self,
 		key: (Hash, ParaId, OccupiedCoreAssumption),
 	) -> Option<&Option<ValidationCode>> {
-		self.validation_code.get(&key)
+		self.validation_code.get(&key).map(|v| &*v)
 	}
 
 	pub(crate) fn cache_validation_code(
@@ -223,7 +220,7 @@ impl RequestResultCache {
 		key: (Hash, ParaId, OccupiedCoreAssumption),
 		value: Option<ValidationCode>,
 	) {
-		self.validation_code.put(key, value);
+		self.validation_code.insert(key, value);
 	}
 
 	// the actual key is `ValidationCodeHash` (`Hash` is ignored),
@@ -232,7 +229,7 @@ impl RequestResultCache {
 		&mut self,
 		key: (Hash, ValidationCodeHash),
 	) -> Option<&Option<ValidationCode>> {
-		self.validation_code_by_hash.get(&key.1)
+		self.validation_code_by_hash.get(&key.1).map(|v| &*v)
 	}
 
 	pub(crate) fn cache_validation_code_by_hash(
@@ -240,14 +237,14 @@ impl RequestResultCache {
 		key: ValidationCodeHash,
 		value: Option<ValidationCode>,
 	) {
-		self.validation_code_by_hash.put(key, value);
+		self.validation_code_by_hash.insert(key, value);
 	}
 
 	pub(crate) fn candidate_pending_availability(
 		&mut self,
 		key: (Hash, ParaId),
 	) -> Option<&Option<CommittedCandidateReceipt>> {
-		self.candidate_pending_availability.get(&key)
+		self.candidate_pending_availability.get(&key).map(|v| &*v)
 	}
 
 	pub(crate) fn cache_candidate_pending_availability(
@@ -255,11 +252,11 @@ impl RequestResultCache {
 		key: (Hash, ParaId),
 		value: Option<CommittedCandidateReceipt>,
 	) {
-		self.candidate_pending_availability.put(key, value);
+		self.candidate_pending_availability.insert(key, value);
 	}
 
 	pub(crate) fn candidate_events(&mut self, relay_parent: &Hash) -> Option<&Vec<CandidateEvent>> {
-		self.candidate_events.get(relay_parent)
+		self.candidate_events.get(relay_parent).map(|v| &*v)
 	}
 
 	pub(crate) fn cache_candidate_events(
@@ -267,22 +264,22 @@ impl RequestResultCache {
 		relay_parent: Hash,
 		events: Vec<CandidateEvent>,
 	) {
-		self.candidate_events.put(relay_parent, events);
+		self.candidate_events.insert(relay_parent, events);
 	}
 
 	pub(crate) fn session_info(&mut self, key: SessionIndex) -> Option<&SessionInfo> {
-		self.session_info.get(&key)
+		self.session_info.get(&key).map(|v| &*v)
 	}
 
 	pub(crate) fn cache_session_info(&mut self, key: SessionIndex, value: SessionInfo) {
-		self.session_info.put(key, value);
+		self.session_info.insert(key, value);
 	}
 
 	pub(crate) fn session_executor_params(
 		&mut self,
 		session_index: SessionIndex,
 	) -> Option<&Option<ExecutorParams>> {
-		self.session_executor_params.get(&session_index)
+		self.session_executor_params.get(&session_index).map(|v| &*v)
 	}
 
 	pub(crate) fn cache_session_executor_params(
@@ -290,14 +287,14 @@ impl RequestResultCache {
 		session_index: SessionIndex,
 		value: Option<ExecutorParams>,
 	) {
-		self.session_executor_params.put(session_index, value);
+		self.session_executor_params.insert(session_index, value);
 	}
 
 	pub(crate) fn dmq_contents(
 		&mut self,
 		key: (Hash, ParaId),
 	) -> Option<&Vec<InboundDownwardMessage<BlockNumber>>> {
-		self.dmq_contents.get(&key)
+		self.dmq_contents.get(&key).map(|v| &*v)
 	}
 
 	pub(crate) fn cache_dmq_contents(
@@ -305,14 +302,14 @@ impl RequestResultCache {
 		key: (Hash, ParaId),
 		value: Vec<InboundDownwardMessage<BlockNumber>>,
 	) {
-		self.dmq_contents.put(key, value);
+		self.dmq_contents.insert(key, value);
 	}
 
 	pub(crate) fn inbound_hrmp_channels_contents(
 		&mut self,
 		key: (Hash, ParaId),
 	) -> Option<&BTreeMap<ParaId, Vec<InboundHrmpMessage<BlockNumber>>>> {
-		self.inbound_hrmp_channels_contents.get(&key)
+		self.inbound_hrmp_channels_contents.get(&key).map(|v| &*v)
 	}
 
 	pub(crate) fn cache_inbound_hrmp_channel_contents(
@@ -320,22 +317,22 @@ impl RequestResultCache {
 		key: (Hash, ParaId),
 		value: BTreeMap<ParaId, Vec<InboundHrmpMessage<BlockNumber>>>,
 	) {
-		self.inbound_hrmp_channels_contents.put(key, value);
+		self.inbound_hrmp_channels_contents.insert(key, value);
 	}
 
 	pub(crate) fn current_babe_epoch(&mut self, relay_parent: &Hash) -> Option<&Epoch> {
-		self.current_babe_epoch.get(relay_parent)
+		self.current_babe_epoch.get(relay_parent).map(|v| &*v)
 	}
 
 	pub(crate) fn cache_current_babe_epoch(&mut self, relay_parent: Hash, epoch: Epoch) {
-		self.current_babe_epoch.put(relay_parent, epoch);
+		self.current_babe_epoch.insert(relay_parent, epoch);
 	}
 
 	pub(crate) fn on_chain_votes(
 		&mut self,
 		relay_parent: &Hash,
 	) -> Option<&Option<ScrapedOnChainVotes>> {
-		self.on_chain_votes.get(relay_parent)
+		self.on_chain_votes.get(relay_parent).map(|v| &*v)
 	}
 
 	pub(crate) fn cache_on_chain_votes(
@@ -343,14 +340,14 @@ impl RequestResultCache {
 		relay_parent: Hash,
 		scraped: Option<ScrapedOnChainVotes>,
 	) {
-		self.on_chain_votes.put(relay_parent, scraped);
+		self.on_chain_votes.insert(relay_parent, scraped);
 	}
 
 	pub(crate) fn pvfs_require_precheck(
 		&mut self,
 		relay_parent: &Hash,
 	) -> Option<&Vec<ValidationCodeHash>> {
-		self.pvfs_require_precheck.get(relay_parent)
+		self.pvfs_require_precheck.get(relay_parent).map(|v| &*v)
 	}
 
 	pub(crate) fn cache_pvfs_require_precheck(
@@ -358,14 +355,14 @@ impl RequestResultCache {
 		relay_parent: Hash,
 		pvfs: Vec<ValidationCodeHash>,
 	) {
-		self.pvfs_require_precheck.put(relay_parent, pvfs);
+		self.pvfs_require_precheck.insert(relay_parent, pvfs);
 	}
 
 	pub(crate) fn validation_code_hash(
 		&mut self,
 		key: (Hash, ParaId, OccupiedCoreAssumption),
 	) -> Option<&Option<ValidationCodeHash>> {
-		self.validation_code_hash.get(&key)
+		self.validation_code_hash.get(&key).map(|v| &*v)
 	}
 
 	pub(crate) fn cache_validation_code_hash(
@@ -373,22 +370,22 @@ impl RequestResultCache {
 		key: (Hash, ParaId, OccupiedCoreAssumption),
 		value: Option<ValidationCodeHash>,
 	) {
-		self.validation_code_hash.put(key, value);
+		self.validation_code_hash.insert(key, value);
 	}
 
 	pub(crate) fn version(&mut self, relay_parent: &Hash) -> Option<&u32> {
-		self.version.get(relay_parent)
+		self.version.get(relay_parent).map(|v| &*v)
 	}
 
 	pub(crate) fn cache_version(&mut self, key: Hash, value: u32) {
-		self.version.put(key, value);
+		self.version.insert(key, value);
 	}
 
 	pub(crate) fn disputes(
 		&mut self,
 		relay_parent: &Hash,
 	) -> Option<&Vec<(SessionIndex, CandidateHash, DisputeState<BlockNumber>)>> {
-		self.disputes.get(relay_parent)
+		self.disputes.get(relay_parent).map(|v| &*v)
 	}
 
 	pub(crate) fn cache_disputes(
@@ -396,14 +393,14 @@ impl RequestResultCache {
 		relay_parent: Hash,
 		value: Vec<(SessionIndex, CandidateHash, DisputeState<BlockNumber>)>,
 	) {
-		self.disputes.put(relay_parent, value);
+		self.disputes.insert(relay_parent, value);
 	}
 
 	pub(crate) fn unapplied_slashes(
 		&mut self,
 		relay_parent: &Hash,
 	) -> Option<&Vec<(SessionIndex, CandidateHash, vstaging::slashing::PendingSlashes)>> {
-		self.unapplied_slashes.get(relay_parent)
+		self.unapplied_slashes.get(relay_parent).map(|v| &*v)
 	}
 
 	pub(crate) fn cache_unapplied_slashes(
@@ -411,14 +408,14 @@ impl RequestResultCache {
 		relay_parent: Hash,
 		value: Vec<(SessionIndex, CandidateHash, vstaging::slashing::PendingSlashes)>,
 	) {
-		self.unapplied_slashes.put(relay_parent, value);
+		self.unapplied_slashes.insert(relay_parent, value);
 	}
 
 	pub(crate) fn key_ownership_proof(
 		&mut self,
 		key: (Hash, ValidatorId),
 	) -> Option<&Option<vstaging::slashing::OpaqueKeyOwnershipProof>> {
-		self.key_ownership_proof.get(&key)
+		self.key_ownership_proof.get(&key).map(|v| &*v)
 	}
 
 	pub(crate) fn cache_key_ownership_proof(
@@ -426,7 +423,7 @@ impl RequestResultCache {
 		key: (Hash, ValidatorId),
 		value: Option<vstaging::slashing::OpaqueKeyOwnershipProof>,
 	) {
-		self.key_ownership_proof.put(key, value);
+		self.key_ownership_proof.insert(key, value);
 	}
 
 	// This request is never cached, hence always returns `None`.
@@ -441,7 +438,7 @@ impl RequestResultCache {
 		&mut self,
 		key: (Hash, ParaId),
 	) -> Option<&Option<vstaging::BackingState>> {
-		self.staging_para_backing_state.get(&key)
+		self.staging_para_backing_state.get(&key).map(|v| &*v)
 	}
 
 	pub(crate) fn cache_staging_para_backing_state(
@@ -449,14 +446,14 @@ impl RequestResultCache {
 		key: (Hash, ParaId),
 		value: Option<vstaging::BackingState>,
 	) {
-		self.staging_para_backing_state.put(key, value);
+		self.staging_para_backing_state.insert(key, value);
 	}
 
 	pub(crate) fn staging_async_backing_params(
 		&mut self,
 		key: &Hash,
 	) -> Option<&vstaging::AsyncBackingParams> {
-		self.staging_async_backing_params.get(key)
+		self.staging_async_backing_params.get(key).map(|v| &*v)
 	}
 
 	pub(crate) fn cache_staging_async_backing_params(
@@ -464,7 +461,7 @@ impl RequestResultCache {
 		key: Hash,
 		value: vstaging::AsyncBackingParams,
 	) {
-		self.staging_async_backing_params.put(key, value);
+		self.staging_async_backing_params.insert(key, value);
 	}
 }
 
diff --git a/polkadot/node/network/availability-distribution/Cargo.toml b/polkadot/node/network/availability-distribution/Cargo.toml
index b81145688b6..60fe8fb9bd7 100644
--- a/polkadot/node/network/availability-distribution/Cargo.toml
+++ b/polkadot/node/network/availability-distribution/Cargo.toml
@@ -20,7 +20,7 @@ sp-keystore = { path = "../../../../substrate/primitives/keystore" }
 thiserror = "1.0.31"
 rand = "0.8.5"
 derive_more = "0.99.17"
-lru = "0.11.0"
+schnellru = "0.2.1"
 fatality = "0.0.6"
 
 [dev-dependencies]
diff --git a/polkadot/node/network/availability-distribution/src/requester/session_cache.rs b/polkadot/node/network/availability-distribution/src/requester/session_cache.rs
index e9311fb2203..8a48e19c282 100644
--- a/polkadot/node/network/availability-distribution/src/requester/session_cache.rs
+++ b/polkadot/node/network/availability-distribution/src/requester/session_cache.rs
@@ -14,10 +14,10 @@
 // You should have received a copy of the GNU General Public License
 // along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
 
-use std::{collections::HashSet, num::NonZeroUsize};
+use std::collections::HashSet;
 
-use lru::LruCache;
 use rand::{seq::SliceRandom, thread_rng};
+use schnellru::{ByLength, LruMap};
 
 use polkadot_node_subsystem::overseer;
 use polkadot_node_subsystem_util::runtime::RuntimeInfo;
@@ -37,7 +37,7 @@ pub struct SessionCache {
 	/// Note: Performance of fetching is really secondary here, but we need to ensure we are going
 	/// to get any existing cache entry, before fetching new information, as we should not mess up
 	/// the order of validators in `SessionInfo::validator_groups`.
-	session_info_cache: LruCache<SessionIndex, SessionInfo>,
+	session_info_cache: LruMap<SessionIndex, SessionInfo>,
 }
 
 /// Localized session information, tailored for the needs of availability distribution.
@@ -83,7 +83,7 @@ impl SessionCache {
 	pub fn new() -> Self {
 		SessionCache {
 			// We need to cache the current and the last session the most:
-			session_info_cache: LruCache::new(NonZeroUsize::new(2).unwrap()),
+			session_info_cache: LruMap::new(ByLength::new(2)),
 		}
 	}
 
@@ -115,7 +115,7 @@ impl SessionCache {
 			gum::trace!(target: LOG_TARGET, session_index, "Calling `with_info`");
 			let r = with_info(&info);
 			gum::trace!(target: LOG_TARGET, session_index, "Storing session info in lru!");
-			self.session_info_cache.put(session_index, info);
+			self.session_info_cache.insert(session_index, info);
 			Ok(Some(r))
 		} else {
 			Ok(None)
@@ -142,7 +142,7 @@ impl SessionCache {
 	/// will be put at the beginning of the group.
 	pub fn report_bad(&mut self, report: BadValidators) -> Result<()> {
 		let available_sessions = self.session_info_cache.iter().map(|(k, _)| *k).collect();
-		let session = self.session_info_cache.get_mut(&report.session_index).ok_or(
+		let session = self.session_info_cache.get(&report.session_index).ok_or(
 			Error::NoSuchCachedSession {
 				available_sessions,
 				missing_session: report.session_index,
diff --git a/polkadot/node/network/availability-recovery/Cargo.toml b/polkadot/node/network/availability-recovery/Cargo.toml
index 9dc4d05353e..d6d79d3fc24 100644
--- a/polkadot/node/network/availability-recovery/Cargo.toml
+++ b/polkadot/node/network/availability-recovery/Cargo.toml
@@ -7,7 +7,7 @@ license.workspace = true
 
 [dependencies]
 futures = "0.3.21"
-lru = "0.11.0"
+schnellru = "0.2.1"
 rand = "0.8.5"
 fatality = "0.0.6"
 thiserror = "1.0.31"
diff --git a/polkadot/node/network/availability-recovery/src/lib.rs b/polkadot/node/network/availability-recovery/src/lib.rs
index fb0cdb72057..99f42f4bf9f 100644
--- a/polkadot/node/network/availability-recovery/src/lib.rs
+++ b/polkadot/node/network/availability-recovery/src/lib.rs
@@ -35,8 +35,8 @@ use futures::{
 	stream::{FuturesUnordered, StreamExt},
 	task::{Context, Poll},
 };
-use lru::LruCache;
 use rand::seq::SliceRandom;
+use schnellru::{ByLength, LruMap};
 
 use fatality::Nested;
 use polkadot_erasure_coding::{
@@ -82,10 +82,7 @@ const LOG_TARGET: &str = "parachain::availability-recovery";
 const N_PARALLEL: usize = 50;
 
 // Size of the LRU cache where we keep recovered data.
-const LRU_SIZE: NonZeroUsize = match NonZeroUsize::new(16) {
-	Some(cap) => cap,
-	None => panic!("Availability-recovery cache size must be non-zero."),
-};
+const LRU_SIZE: u32 = 16;
 
 const COST_INVALID_REQUEST: Rep = Rep::CostMajor("Peer sent unparsable request");
 
@@ -927,7 +924,7 @@ struct State {
 	live_block: (BlockNumber, Hash),
 
 	/// An LRU cache of recently recovered data.
-	availability_lru: LruCache<CandidateHash, CachedRecovery>,
+	availability_lru: LruMap<CandidateHash, CachedRecovery>,
 }
 
 impl Default for State {
@@ -935,7 +932,7 @@ impl Default for State {
 		Self {
 			ongoing_recoveries: FuturesUnordered::new(),
 			live_block: (0, Hash::default()),
-			availability_lru: LruCache::new(LRU_SIZE),
+			availability_lru: LruMap::new(ByLength::new(LRU_SIZE)),
 		}
 	}
 }
@@ -1152,7 +1149,7 @@ async fn query_chunk_size<Context>(
 
 #[overseer::contextbounds(AvailabilityRecovery, prefix = self::overseer)]
 impl AvailabilityRecoverySubsystem {
-	/// Create a new instance of `AvailabilityRecoverySubsystem` which never requests the  
+	/// Create a new instance of `AvailabilityRecoverySubsystem` which never requests the
 	/// `AvailabilityStoreSubsystem` subsystem.
 	pub fn with_availability_store_skip(
 		req_receiver: IncomingRequestReceiver<request_v1::AvailableDataFetchingRequest>,
@@ -1334,7 +1331,7 @@ impl AvailabilityRecoverySubsystem {
 				output = state.ongoing_recoveries.select_next_some() => {
 					if let Some((candidate_hash, result)) = output {
 						if let Ok(recovery) = CachedRecovery::try_from(result) {
-							state.availability_lru.put(candidate_hash, recovery);
+							state.availability_lru.insert(candidate_hash, recovery);
 						}
 					}
 				}
diff --git a/polkadot/node/network/dispute-distribution/Cargo.toml b/polkadot/node/network/dispute-distribution/Cargo.toml
index 24f597bacad..24ae7ba25bc 100644
--- a/polkadot/node/network/dispute-distribution/Cargo.toml
+++ b/polkadot/node/network/dispute-distribution/Cargo.toml
@@ -22,7 +22,7 @@ sp-application-crypto = { path = "../../../../substrate/primitives/application-c
 sp-keystore = { path = "../../../../substrate/primitives/keystore" }
 thiserror = "1.0.31"
 fatality = "0.0.6"
-lru = "0.11.0"
+schnellru = "0.2.1"
 indexmap = "1.9.1"
 
 [dev-dependencies]
diff --git a/polkadot/node/network/dispute-distribution/src/lib.rs b/polkadot/node/network/dispute-distribution/src/lib.rs
index ad99bc41fa6..071dc3c3343 100644
--- a/polkadot/node/network/dispute-distribution/src/lib.rs
+++ b/polkadot/node/network/dispute-distribution/src/lib.rs
@@ -24,7 +24,7 @@
 //! The sender is responsible for getting our vote out, see [`sender`]. The receiver handles
 //! incoming [`DisputeRequest`]s and offers spam protection, see [`receiver`].
 
-use std::{num::NonZeroUsize, time::Duration};
+use std::time::Duration;
 
 use futures::{channel::mpsc, FutureExt, StreamExt, TryFutureExt};
 
@@ -165,8 +165,7 @@ where
 	) -> Self {
 		let runtime = RuntimeInfo::new_with_config(runtime::Config {
 			keystore: Some(keystore),
-			session_cache_lru_size: NonZeroUsize::new(DISPUTE_WINDOW.get() as usize)
-				.expect("Dispute window can not be 0; qed"),
+			session_cache_lru_size: DISPUTE_WINDOW.get(),
 		});
 		let (tx, sender_rx) = NestingSender::new_root(1);
 		let disputes_sender = DisputeSender::new(tx, metrics.clone());
diff --git a/polkadot/node/network/dispute-distribution/src/receiver/mod.rs b/polkadot/node/network/dispute-distribution/src/receiver/mod.rs
index 827a77281cc..2b3fc45983a 100644
--- a/polkadot/node/network/dispute-distribution/src/receiver/mod.rs
+++ b/polkadot/node/network/dispute-distribution/src/receiver/mod.rs
@@ -15,7 +15,6 @@
 // along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
 
 use std::{
-	num::NonZeroUsize,
 	pin::Pin,
 	task::{Context, Poll},
 	time::Duration,
@@ -161,8 +160,7 @@ where
 	) -> Self {
 		let runtime = RuntimeInfo::new_with_config(runtime::Config {
 			keystore: None,
-			session_cache_lru_size: NonZeroUsize::new(DISPUTE_WINDOW.get() as usize)
-				.expect("Dispute window can not be 0; qed"),
+			session_cache_lru_size: DISPUTE_WINDOW.get(),
 		});
 		Self {
 			runtime,
diff --git a/polkadot/node/overseer/Cargo.toml b/polkadot/node/overseer/Cargo.toml
index 577b12e2620..982ccc7ff24 100644
--- a/polkadot/node/overseer/Cargo.toml
+++ b/polkadot/node/overseer/Cargo.toml
@@ -18,7 +18,7 @@ polkadot-node-metrics = { path = "../metrics" }
 polkadot-primitives = { path = "../../primitives" }
 orchestra = "0.0.5"
 gum = { package = "tracing-gum", path = "../gum" }
-lru = "0.11.0"
+schnellru = "0.2.1"
 sp-core = { path = "../../../substrate/primitives/core" }
 async-trait = "0.1.57"
 tikv-jemalloc-ctl = { version = "0.5.0", optional = true }
diff --git a/polkadot/node/overseer/src/dummy.rs b/polkadot/node/overseer/src/dummy.rs
index 79daba14067..fea96e5dbab 100644
--- a/polkadot/node/overseer/src/dummy.rs
+++ b/polkadot/node/overseer/src/dummy.rs
@@ -19,9 +19,9 @@ use crate::{
 	Overseer, OverseerMetrics, OverseerSignal, OverseerSubsystemContext, SpawnGlue,
 	KNOWN_LEAVES_CACHE_SIZE,
 };
-use lru::LruCache;
 use orchestra::{FromOrchestra, SpawnedSubsystem, Subsystem, SubsystemContext};
 use polkadot_node_subsystem_types::{errors::SubsystemError, messages::*};
+use schnellru::{ByLength, LruMap};
 // Generated dummy messages
 use crate::messages::*;
 
@@ -193,7 +193,7 @@ where
 		.activation_external_listeners(Default::default())
 		.span_per_active_leaf(Default::default())
 		.active_leaves(Default::default())
-		.known_leaves(LruCache::new(KNOWN_LEAVES_CACHE_SIZE))
+		.known_leaves(LruMap::new(ByLength::new(KNOWN_LEAVES_CACHE_SIZE)))
 		.spawner(SpawnGlue(spawner))
 		.metrics(metrics)
 		.supports_parachains(supports_parachains);
diff --git a/polkadot/node/overseer/src/lib.rs b/polkadot/node/overseer/src/lib.rs
index 5655a3ef79c..b8643982323 100644
--- a/polkadot/node/overseer/src/lib.rs
+++ b/polkadot/node/overseer/src/lib.rs
@@ -62,14 +62,13 @@
 use std::{
 	collections::{hash_map, HashMap},
 	fmt::{self, Debug},
-	num::NonZeroUsize,
 	pin::Pin,
 	sync::Arc,
 	time::Duration,
 };
 
 use futures::{channel::oneshot, future::BoxFuture, select, Future, FutureExt, StreamExt};
-use lru::LruCache;
+use schnellru::LruMap;
 
 use client::{BlockImportNotification, BlockchainEvents, FinalityNotification};
 use polkadot_primitives::{Block, BlockNumber, Hash};
@@ -113,10 +112,7 @@ pub use orchestra::{
 
 /// Store 2 days worth of blocks, not accounting for forks,
 /// in the LRU cache. Assumes a 6-second block time.
-pub const KNOWN_LEAVES_CACHE_SIZE: NonZeroUsize = match NonZeroUsize::new(2 * 24 * 3600 / 6) {
-	Some(cap) => cap,
-	None => panic!("Known leaves cache size must be non-zero"),
-};
+pub const KNOWN_LEAVES_CACHE_SIZE: u32 = 2 * 24 * 3600 / 6;
 
 #[cfg(any(target_os = "linux", feature = "jemalloc-allocator"))]
 mod memory_stats;
@@ -632,7 +628,7 @@ pub struct Overseer<SupportsParachains> {
 	pub supports_parachains: SupportsParachains,
 
 	/// An LRU cache for keeping track of relay-chain heads that have already been seen.
-	pub known_leaves: LruCache<Hash, ()>,
+	pub known_leaves: LruMap<Hash, ()>,
 
 	/// Various Prometheus metrics.
 	pub metrics: OverseerMetrics,
@@ -880,9 +876,10 @@ where
 		let span = Arc::new(span);
 		self.span_per_active_leaf.insert(*hash, span.clone());
 
-		let status = if let Some(_) = self.known_leaves.put(*hash, ()) {
+		let status = if self.known_leaves.get(hash).is_some() {
 			LeafStatus::Stale
 		} else {
+			self.known_leaves.insert(*hash, ());
 			LeafStatus::Fresh
 		};
 
diff --git a/polkadot/node/primitives/src/lib.rs b/polkadot/node/primitives/src/lib.rs
index 39278178331..4cebc23ad31 100644
--- a/polkadot/node/primitives/src/lib.rs
+++ b/polkadot/node/primitives/src/lib.rs
@@ -22,7 +22,7 @@
 
 #![deny(missing_docs)]
 
-use std::{num::NonZeroUsize, pin::Pin};
+use std::pin::Pin;
 
 use bounded_vec::BoundedVec;
 use futures::Future;
@@ -143,12 +143,6 @@ impl SessionWindowSize {
 	}
 }
 
-impl From<SessionWindowSize> for NonZeroUsize {
-	fn from(value: SessionWindowSize) -> Self {
-		NonZeroUsize::new(value.get() as usize).expect("SessionWindowSize can't be 0. qed.")
-	}
-}
-
 /// The cumulative weight of a block in a fork-choice rule.
 pub type BlockWeight = u32;
 
diff --git a/polkadot/node/service/Cargo.toml b/polkadot/node/service/Cargo.toml
index f58f4abca36..58be7a88535 100644
--- a/polkadot/node/service/Cargo.toml
+++ b/polkadot/node/service/Cargo.toml
@@ -86,7 +86,7 @@ parity-db = { version = "0.4.8", optional = true }
 codec = { package = "parity-scale-codec", version = "3.6.1" }
 
 async-trait = "0.1.57"
-lru = "0.11.0"
+schnellru = "0.2.1"
 log = "0.4.17"
 is_executable = "1.0.1"
 
diff --git a/polkadot/node/service/src/overseer.rs b/polkadot/node/service/src/overseer.rs
index cb6b80eb83c..1c49577508f 100644
--- a/polkadot/node/service/src/overseer.rs
+++ b/polkadot/node/service/src/overseer.rs
@@ -19,7 +19,6 @@ use polkadot_node_subsystem_types::DefaultSubsystemClient;
 use sc_transaction_pool_api::OffchainTransactionPoolFactory;
 use sp_core::traits::SpawnNamed;
 
-use lru::LruCache;
 use polkadot_availability_distribution::IncomingRequestReceivers;
 use polkadot_node_core_approval_voting::Config as ApprovalVotingConfig;
 use polkadot_node_core_av_store::Config as AvailabilityConfig;
@@ -41,6 +40,7 @@ use polkadot_overseer::{
 	metrics::Metrics as OverseerMetrics, InitializedOverseerBuilder, MetricsTrait, Overseer,
 	OverseerConnector, OverseerHandle, SpawnGlue,
 };
+use schnellru::{ByLength, LruMap};
 
 use polkadot_primitives::runtime_api::ParachainHost;
 use sc_authority_discovery::Service as AuthorityDiscoveryService;
@@ -344,7 +344,7 @@ where
 		.span_per_active_leaf(Default::default())
 		.active_leaves(Default::default())
 		.supports_parachains(runtime_api_client)
-		.known_leaves(LruCache::new(KNOWN_LEAVES_CACHE_SIZE))
+		.known_leaves(LruMap::new(ByLength::new(KNOWN_LEAVES_CACHE_SIZE)))
 		.metrics(metrics)
 		.spawner(spawner);
 
diff --git a/polkadot/node/subsystem-util/Cargo.toml b/polkadot/node/subsystem-util/Cargo.toml
index a7c802a8ca8..87a823da909 100644
--- a/polkadot/node/subsystem-util/Cargo.toml
+++ b/polkadot/node/subsystem-util/Cargo.toml
@@ -19,7 +19,7 @@ thiserror = "1.0.31"
 fatality = "0.0.6"
 gum = { package = "tracing-gum", path = "../gum" }
 derive_more = "0.99.17"
-lru = "0.11.0"
+schnellru = "0.2.1"
 
 polkadot-node-subsystem = { path = "../subsystem" }
 polkadot-node-jaeger = { path = "../jaeger" }
diff --git a/polkadot/node/subsystem-util/src/runtime/mod.rs b/polkadot/node/subsystem-util/src/runtime/mod.rs
index 1f5641e3ea9..a044a64e93a 100644
--- a/polkadot/node/subsystem-util/src/runtime/mod.rs
+++ b/polkadot/node/subsystem-util/src/runtime/mod.rs
@@ -16,9 +16,7 @@
 
 //! Convenient interface to runtime information.
 
-use std::num::NonZeroUsize;
-
-use lru::LruCache;
+use schnellru::{ByLength, LruMap};
 
 use parity_scale_codec::Encode;
 use sp_application_crypto::AppCrypto;
@@ -58,7 +56,7 @@ pub struct Config {
 	pub keystore: Option<KeystorePtr>,
 
 	/// How many sessions should we keep in the cache?
-	pub session_cache_lru_size: NonZeroUsize,
+	pub session_cache_lru_size: u32,
 }
 
 /// Caching of session info.
@@ -69,10 +67,10 @@ pub struct RuntimeInfo {
 	///
 	/// We query this up to a 100 times per block, so caching it here without roundtrips over the
 	/// overseer seems sensible.
-	session_index_cache: LruCache<Hash, SessionIndex>,
+	session_index_cache: LruMap<Hash, SessionIndex>,
 
 	/// Look up cached sessions by `SessionIndex`.
-	session_info_cache: LruCache<SessionIndex, ExtendedSessionInfo>,
+	session_info_cache: LruMap<SessionIndex, ExtendedSessionInfo>,
 
 	/// Key store for determining whether we are a validator and what `ValidatorIndex` we have.
 	keystore: Option<KeystorePtr>,
@@ -101,7 +99,7 @@ impl Default for Config {
 		Self {
 			keystore: None,
 			// Usually we need to cache the current and the last session.
-			session_cache_lru_size: NonZeroUsize::new(2).expect("2 is larger than 0; qed"),
+			session_cache_lru_size: 2,
 		}
 	}
 }
@@ -115,11 +113,8 @@ impl RuntimeInfo {
 	/// Create with more elaborate configuration options.
 	pub fn new_with_config(cfg: Config) -> Self {
 		Self {
-			session_index_cache: LruCache::new(
-				cfg.session_cache_lru_size
-					.max(NonZeroUsize::new(10).expect("10 is larger than 0; qed")),
-			),
-			session_info_cache: LruCache::new(cfg.session_cache_lru_size),
+			session_index_cache: LruMap::new(ByLength::new(cfg.session_cache_lru_size.max(10))),
+			session_info_cache: LruMap::new(ByLength::new(cfg.session_cache_lru_size)),
 			keystore: cfg.keystore,
 		}
 	}
@@ -139,7 +134,7 @@ impl RuntimeInfo {
 			None => {
 				let index =
 					recv_runtime(request_session_index_for_child(parent, sender).await).await?;
-				self.session_index_cache.put(parent, index);
+				self.session_index_cache.insert(parent, index);
 				Ok(index)
 			},
 		}
@@ -172,7 +167,7 @@ impl RuntimeInfo {
 	where
 		Sender: SubsystemSender<RuntimeApiMessage>,
 	{
-		if !self.session_info_cache.contains(&session_index) {
+		if self.session_info_cache.get(&session_index).is_none() {
 			let session_info =
 				recv_runtime(request_session_info(parent, session_index, sender).await)
 					.await?
@@ -181,7 +176,7 @@ impl RuntimeInfo {
 
 			let full_info = ExtendedSessionInfo { session_info, validator_info };
 
-			self.session_info_cache.put(session_index, full_info);
+			self.session_info_cache.insert(session_index, full_info);
 		}
 		Ok(self
 			.session_info_cache
diff --git a/polkadot/roadmap/implementers-guide/src/node/availability/availability-recovery.md b/polkadot/roadmap/implementers-guide/src/node/availability/availability-recovery.md
index 48fb0fb1ca1..ac733c1ce91 100644
--- a/polkadot/roadmap/implementers-guide/src/node/availability/availability-recovery.md
+++ b/polkadot/roadmap/implementers-guide/src/node/availability/availability-recovery.md
@@ -30,7 +30,7 @@ struct State {
     /// A recent block hash for which state should be available.
     live_block_hash: Hash,
     // An LRU cache of recently recovered data.
-    availability_lru: LruCache<CandidateHash, Result<AvailableData, RecoveryError>>,
+    availability_lru: LruMap<CandidateHash, Result<AvailableData, RecoveryError>>,
 }
 
 /// This is a future, which concludes either when a response is received from the recovery tasks,
diff --git a/polkadot/roadmap/implementers-guide/src/node/disputes/dispute-coordinator.md b/polkadot/roadmap/implementers-guide/src/node/disputes/dispute-coordinator.md
index 7692491fde1..f8bfe6506aa 100644
--- a/polkadot/roadmap/implementers-guide/src/node/disputes/dispute-coordinator.md
+++ b/polkadot/roadmap/implementers-guide/src/node/disputes/dispute-coordinator.md
@@ -9,7 +9,7 @@ In particular the dispute-coordinator is responsible for:
 
 - Ensuring that the node is able to raise a dispute in case an invalid candidate
   is found during approval checking.
-- Ensuring that backing and approval votes will be recorded on chain. With these 
+- Ensuring that backing and approval votes will be recorded on chain. With these
   votes on chain we can be certain that appropriate targets for slashing will be
   available for concluded disputes. Also, scraping these votes during a dispute
   is necessary for critical spam prevention measures.
@@ -678,7 +678,7 @@ struct State {
   // It can be a `Vec` if the need to track more arises.
   error: Option<SessionsUnavailable>,
   /// Latest relay blocks that have been successfully scraped.
-  last_scraped_blocks: LruCache<Hash, ()>,
+  last_scraped_blocks: LruMap<Hash, ()>,
 }
 ```
 
-- 
GitLab