From 47d29613a7776dd93caca8f7fea2f7567f62ba34 Mon Sep 17 00:00:00 2001 From: Bojan Serafimov Date: Wed, 19 Oct 2022 17:16:02 -0400 Subject: [PATCH] Use dashmap in page cache --- Cargo.lock | 14 ++++++++++++ pageserver/Cargo.toml | 1 + pageserver/src/page_cache.rs | 42 ++++++++++++++++-------------------- 3 files changed, 33 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 657baf5d80..c66d241545 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -894,6 +894,19 @@ dependencies = [ "syn", ] +[[package]] +name = "dashmap" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc" +dependencies = [ + "cfg-if", + "hashbrown", + "lock_api", + "once_cell", + "parking_lot_core 0.9.3", +] + [[package]] name = "data-encoding" version = "2.3.2" @@ -2141,6 +2154,7 @@ dependencies = [ "criterion", "crossbeam-utils", "daemonize", + "dashmap", "etcd_broker", "fail", "futures", diff --git a/pageserver/Cargo.toml b/pageserver/Cargo.toml index 2139e24ee2..c259c83a13 100644 --- a/pageserver/Cargo.toml +++ b/pageserver/Cargo.toml @@ -67,6 +67,7 @@ remote_storage = { path = "../libs/remote_storage" } workspace_hack = { version = "0.1", path = "../workspace_hack" } close_fds = "0.3.2" walkdir = "2.3.2" +dashmap = "5.4.0" [dev-dependencies] criterion = "0.4" diff --git a/pageserver/src/page_cache.rs b/pageserver/src/page_cache.rs index d2fe06697e..d9ce484146 100644 --- a/pageserver/src/page_cache.rs +++ b/pageserver/src/page_cache.rs @@ -36,8 +36,9 @@ //! mapping is automatically removed and the slot is marked free. //! +use dashmap::mapref::entry::Entry; +use dashmap::DashMap; use std::{ - collections::{hash_map::Entry, HashMap}, convert::TryInto, sync::{ atomic::{AtomicU8, AtomicUsize, Ordering}, @@ -168,18 +169,11 @@ impl Slot { pub struct PageCache { /// This contains the mapping from the cache key to buffer slot that currently /// contains the page, if any. - /// - /// TODO: This is protected by a single lock. If that becomes a bottleneck, - /// this HashMap can be replaced with a more concurrent version, there are - /// plenty of such crates around. - /// - /// If you add support for caching different kinds of objects, each object kind - /// can have a separate mapping map, next to this field. - materialized_page_map: RwLock>>, + materialized_page_map: DashMap>, - ephemeral_page_map: RwLock>, + ephemeral_page_map: DashMap<(u64, u32), usize>, - immutable_page_map: RwLock>, + immutable_page_map: DashMap<(u64, u32), usize>, /// The actual buffers with their metadata. slots: Box<[Slot]>, @@ -616,7 +610,7 @@ impl PageCache { fn search_mapping(&self, cache_key: &mut CacheKey) -> Option { match cache_key { CacheKey::MaterializedPage { hash_key, lsn } => { - let map = self.materialized_page_map.read().unwrap(); + let map = &self.materialized_page_map; let versions = map.get(hash_key)?; let version_idx = match versions.binary_search_by_key(lsn, |v| v.lsn) { @@ -629,11 +623,11 @@ impl PageCache { Some(version.slot_idx) } CacheKey::EphemeralPage { file_id, blkno } => { - let map = self.ephemeral_page_map.read().unwrap(); + let map = &self.ephemeral_page_map; Some(*map.get(&(*file_id, *blkno))?) } CacheKey::ImmutableFilePage { file_id, blkno } => { - let map = self.immutable_page_map.read().unwrap(); + let map = &self.immutable_page_map; Some(*map.get(&(*file_id, *blkno))?) } } @@ -646,7 +640,7 @@ impl PageCache { fn search_mapping_for_write(&self, key: &CacheKey) -> Option { match key { CacheKey::MaterializedPage { hash_key, lsn } => { - let map = self.materialized_page_map.read().unwrap(); + let map = &self.materialized_page_map; let versions = map.get(hash_key)?; if let Ok(version_idx) = versions.binary_search_by_key(lsn, |v| v.lsn) { @@ -656,11 +650,11 @@ impl PageCache { } } CacheKey::EphemeralPage { file_id, blkno } => { - let map = self.ephemeral_page_map.read().unwrap(); + let map = &self.ephemeral_page_map; Some(*map.get(&(*file_id, *blkno))?) } CacheKey::ImmutableFilePage { file_id, blkno } => { - let map = self.immutable_page_map.read().unwrap(); + let map = &self.immutable_page_map; Some(*map.get(&(*file_id, *blkno))?) } } @@ -675,7 +669,7 @@ impl PageCache { hash_key: old_hash_key, lsn: old_lsn, } => { - let mut map = self.materialized_page_map.write().unwrap(); + let map = &self.materialized_page_map; if let Entry::Occupied(mut old_entry) = map.entry(old_hash_key.clone()) { let versions = old_entry.get_mut(); @@ -690,12 +684,12 @@ impl PageCache { } } CacheKey::EphemeralPage { file_id, blkno } => { - let mut map = self.ephemeral_page_map.write().unwrap(); + let map = &self.ephemeral_page_map; map.remove(&(*file_id, *blkno)) .expect("could not find old key in mapping"); } CacheKey::ImmutableFilePage { file_id, blkno } => { - let mut map = self.immutable_page_map.write().unwrap(); + let map = &self.immutable_page_map; map.remove(&(*file_id, *blkno)) .expect("could not find old key in mapping"); } @@ -713,8 +707,8 @@ impl PageCache { hash_key: new_key, lsn: new_lsn, } => { - let mut map = self.materialized_page_map.write().unwrap(); - let versions = map.entry(new_key.clone()).or_default(); + let map = &self.materialized_page_map; + let mut versions = map.entry(new_key.clone()).or_default(); match versions.binary_search_by_key(new_lsn, |v| v.lsn) { Ok(version_idx) => Some(versions[version_idx].slot_idx), Err(version_idx) => { @@ -730,7 +724,7 @@ impl PageCache { } } CacheKey::EphemeralPage { file_id, blkno } => { - let mut map = self.ephemeral_page_map.write().unwrap(); + let map = &self.ephemeral_page_map; match map.entry((*file_id, *blkno)) { Entry::Occupied(entry) => Some(*entry.get()), Entry::Vacant(entry) => { @@ -740,7 +734,7 @@ impl PageCache { } } CacheKey::ImmutableFilePage { file_id, blkno } => { - let mut map = self.immutable_page_map.write().unwrap(); + let map = &self.immutable_page_map; match map.entry((*file_id, *blkno)) { Entry::Occupied(entry) => Some(*entry.get()), Entry::Vacant(entry) => {