Skip to content

Commit 1bb5ccc

Browse files
committed
refactor(pager): gate cache-eviction test helpers to test builds
evict_clean_main_pages and its cache-layer counterpart are exercised only by tests, so mark them #[cfg(test)] rather than shipping them in the crate's public build surface. Move the test that exercises them into the unit tests beside Db, per this crate's convention that implementation-detail coverage lives with the code it tests.
1 parent 1b98b9c commit 1bb5ccc

4 files changed

Lines changed: 57 additions & 57 deletions

File tree

src/pager/cache.rs

Lines changed: 19 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -270,42 +270,33 @@ impl PageCache {
270270
/// when compaction replaces the backing file (the cached pages no longer
271271
/// match what is on disk). Pinned entries are left untouched.
272272
pub fn clear_file(&mut self, file: FileKey) {
273-
let keys: Vec<(FileKey, u64)> = self
274-
.map
275-
.keys()
276-
.filter(|(f, _)| *f == file)
277-
.copied()
278-
.collect();
279-
for key in keys {
280-
if self.pins.get(&key).copied().unwrap_or(0) > 0 {
281-
continue;
282-
}
283-
if let Some(idx) = self.map.remove(&key) {
284-
let (prev, next) = {
285-
let node = self.slab[idx].as_ref().expect("indexed node alive");
286-
(node.prev, node.next)
287-
};
288-
if self.hand == Some(idx) {
289-
self.hand = next;
290-
}
291-
self.unlink_node(idx, prev, next);
292-
self.free_node(idx);
293-
}
294-
self.dirty.remove(&key);
295-
}
273+
self.clear_file_entries(file, false);
296274
}
297275

298276
/// Drop every unpinned clean entry for `file`, leaving dirty entries
299277
/// available for a later flush and pinned entries available to readers.
278+
/// Used to force a re-read of the durable bytes without discarding the
279+
/// in-flight writes that have not reached them yet.
280+
#[cfg(test)]
300281
pub fn clear_clean_file(&mut self, file: FileKey) {
282+
self.clear_file_entries(file, true);
283+
}
284+
285+
/// Shared body of the two `clear_*` entry points. Pinned entries are always
286+
/// spared: a reader holds them. `keep_dirty` decides whether an unflushed
287+
/// entry is spared as well, or dropped along with its dirty marker.
288+
fn clear_file_entries(&mut self, file: FileKey, keep_dirty: bool) {
301289
let keys: Vec<(FileKey, u64)> = self
302290
.map
303291
.keys()
304292
.filter(|(f, _)| *f == file)
305293
.copied()
306294
.collect();
307295
for key in keys {
308-
if self.pins.get(&key).copied().unwrap_or(0) > 0 || self.dirty.contains(&key) {
296+
if self.pins.get(&key).copied().unwrap_or(0) > 0 {
297+
continue;
298+
}
299+
if keep_dirty && self.dirty.contains(&key) {
309300
continue;
310301
}
311302
if let Some(idx) = self.map.remove(&key) {
@@ -319,6 +310,9 @@ impl PageCache {
319310
self.unlink_node(idx, prev, next);
320311
self.free_node(idx);
321312
}
313+
if !keep_dirty {
314+
self.dirty.remove(&key);
315+
}
322316
}
323317
}
324318
}
@@ -407,7 +401,7 @@ mod tests {
407401

408402
assert!(c.get((FileKey::Main, 1)).is_none());
409403
assert!(c.get((FileKey::Main, 2)).is_some());
410-
assert!(c.is_dirty((FileKey::Main, 2)));
404+
assert_eq!(c.dirty_for_file(FileKey::Main), vec![2]);
411405
}
412406

413407
#[test]

src/pager/core.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -872,6 +872,11 @@ impl<V: Vfs> Pager<V> {
872872

873873
/// Evict unpinned clean main.db pages so later reads fetch and authenticate
874874
/// durable bytes without discarding in-flight writes.
875+
///
876+
/// Compiled only for the crate's own tests, matching the handle-level
877+
/// accessor that is its sole caller: correctness never depends on whether a
878+
/// page is warm, so nothing in the shipped crate should be steering that.
879+
#[cfg(test)]
875880
pub fn evict_clean_main_pages(&self, _realm_id: crate::RealmId) {
876881
self.inner
877882
.buffer_pool

src/txn/db/misc.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,39 @@ mod tests {
219219
const PAGE: usize = 4096;
220220
const REALM: RealmId = RealmId::new([0xA5; 16]);
221221

222+
#[tokio::test(flavor = "current_thread")]
223+
async fn evict_main_pages_forces_next_read_to_miss_cache() {
224+
let db = Db::open_internal(MemVfs::new(), [9u8; 32], PAGE, REALM)
225+
.await
226+
.unwrap();
227+
{
228+
let mut txn = db.begin_write().await.unwrap();
229+
txn.put(b"hello", b"world").await.unwrap();
230+
txn.commit().await.unwrap();
231+
}
232+
{
233+
let reader = db.begin_read().await.unwrap();
234+
assert_eq!(
235+
reader.get(b"hello").await.unwrap().as_deref(),
236+
Some(b"world".as_slice())
237+
);
238+
}
239+
let before = db.stats().await.unwrap();
240+
241+
db.evict_main_pages(REALM);
242+
243+
let reader = db.begin_read().await.unwrap();
244+
assert_eq!(
245+
reader.get(b"hello").await.unwrap().as_deref(),
246+
Some(b"world".as_slice())
247+
);
248+
let after = db.stats().await.unwrap();
249+
assert!(
250+
after.buffer_pool_misses > before.buffer_pool_misses,
251+
"eviction must force a VFS-backed cache miss: before={before:?}, after={after:?}"
252+
);
253+
}
254+
222255
#[tokio::test(flavor = "current_thread")]
223256
async fn stats_surfaces_malformed_segment_catalog_row() {
224257
let db = Db::open_internal(MemVfs::new(), [9u8; 32], PAGE, REALM)

tests/stats_basic.rs

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -117,38 +117,6 @@ async fn stats_reports_buffer_pool() {
117117
);
118118
}
119119

120-
#[tokio::test]
121-
async fn evict_main_pages_forces_next_read_to_miss_cache() {
122-
let db = fresh_db().await;
123-
{
124-
let mut txn = db.begin_write().await.unwrap();
125-
txn.put(b"hello", b"world").await.unwrap();
126-
txn.commit().await.unwrap();
127-
}
128-
129-
{
130-
let reader = db.begin_read().await.unwrap();
131-
assert_eq!(
132-
reader.get(b"hello").await.unwrap().as_deref(),
133-
Some(b"world".as_slice())
134-
);
135-
}
136-
let before = db.stats().await.unwrap();
137-
138-
db.evict_main_pages(realm());
139-
140-
let reader = db.begin_read().await.unwrap();
141-
assert_eq!(
142-
reader.get(b"hello").await.unwrap().as_deref(),
143-
Some(b"world".as_slice())
144-
);
145-
let after = db.stats().await.unwrap();
146-
assert!(
147-
after.buffer_pool_misses > before.buffer_pool_misses,
148-
"eviction must force a VFS-backed cache miss: before={before:?}, after={after:?}"
149-
);
150-
}
151-
152120
#[tokio::test]
153121
async fn stats_reports_mode() {
154122
let vfs = MemVfs::new();

0 commit comments

Comments
 (0)