From 989ab12b031434c85f1ef736e46fd5a433827de5 Mon Sep 17 00:00:00 2001 From: Mika Cohen Date: Fri, 11 Sep 2026 10:41:26 -0600 Subject: [PATCH 1/2] test(cache): reproduce foyer disk reinsertion corruption --- src/cache/hybrid.rs | 80 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/src/cache/hybrid.rs b/src/cache/hybrid.rs index 48309d7e..614b91fa 100644 --- a/src/cache/hybrid.rs +++ b/src/cache/hybrid.rs @@ -688,6 +688,7 @@ mod tests { //! Cache-level integration tests for `HybridArticleCache` //! use super::*; + use std::sync::Arc; #[test] fn hybrid_cache_name_cold_invalidates_old_disk_formats() { @@ -982,4 +983,83 @@ mod tests { cache.close().await.unwrap(); } + + #[tokio::test(flavor = "multi_thread", worker_threads = 8)] + async fn hybrid_disk_reinsertion_does_not_corrupt_lru() { + const KEYS: u64 = 64; + const WORKERS: u64 = 32; + + let disk_dir = tempfile::tempdir().unwrap(); + let cache = Arc::new( + HybridArticleCache::new(HybridCacheConfig { + memory_capacity: 256 * 1024, + disk_capacity: 128 * 1024 * 1024, + disk_path: disk_dir.path().to_path_buf(), + ttl: Duration::from_secs(60 * 60), + compression: CompressionCodec::None, + shards: 1, + }) + .await + .unwrap(), + ); + + for key in 0..KEYS * 2 { + let message_id = format!(""); + let response = format!( + "220 0 {message_id} article\r\n\r\n{}\r\n.\r\n", + "x".repeat(4096) + ) + .into_bytes(); + cache + .upsert_ingest( + MessageId::from_borrowed(&message_id).unwrap(), + response, + BackendId::from_index(0), + 0.into(), + ) + .await; + } + + tokio::time::sleep(Duration::from_secs(1)).await; + let deadline = tokio::time::Instant::now() + Duration::from_secs(10); + let mut workers = Vec::new(); + + for worker in 0..WORKERS { + let cache = Arc::clone(&cache); + workers.push(tokio::spawn(async move { + let mut state = worker + 1; + while tokio::time::Instant::now() < deadline { + state ^= state << 13; + state ^= state >> 7; + state ^= state << 17; + let key = state % (KEYS * 2); + let message_id = format!(""); + let message_id_ref = MessageId::from_borrowed(&message_id).unwrap(); + + if state % 10 < 7 { + let _ = cache.get(&message_id_ref).await; + } else { + let response = format!( + "220 0 {message_id} article\r\n\r\n{}\r\n.\r\n", + "x".repeat(4096) + ) + .into_bytes(); + cache + .upsert_ingest( + message_id_ref, + response, + BackendId::from_index(0), + 0.into(), + ) + .await; + } + } + })); + } + + for worker in workers { + worker.await.unwrap(); + } + cache.close().await.unwrap(); + } } From 58f40cea99d86afa46e653d902462dec97589e91 Mon Sep 17 00:00:00 2001 From: Mika Cohen Date: Fri, 11 Sep 2026 10:54:21 -0600 Subject: [PATCH 2/2] fix(cache): update foyer dependencies --- Cargo.lock | 74 ++++++++++++++++++++++++++---------------------------- 1 file changed, 35 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5efc76ab..3141983b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -125,6 +125,12 @@ dependencies = [ "syn 3.0.4", ] +[[package]] +name = "asyncband" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2d85fd3d291fabcc40c7232c92c280ec1754fd7b5d7ea769f143222143e179a" + [[package]] name = "atomic" version = "0.6.1" @@ -409,12 +415,6 @@ dependencies = [ "cc", ] -[[package]] -name = "cmsketch" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7ee2cfacbd29706479902b06d75ad8f1362900836aa32799eabc7e004bfd854" - [[package]] name = "cobs" version = "0.3.0" @@ -760,6 +760,12 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4583a4551df46e2792f82ceeac45e850d2e2d5debba0b91f102385cda5b11f06" +[[package]] +name = "datasketches" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c4cf71a36b46dcfc00e5014c0c20ccad2b1b6a008304d7d57d2749b2d41b3d" + [[package]] name = "deadpool" version = "0.13.1" @@ -908,7 +914,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -1030,18 +1036,18 @@ dependencies = [ [[package]] name = "foyer" -version = "0.22.3" +version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0abc0b87814989efa711f9becd9f26969820e2d3905db27d10969c4bd45890" +checksum = "6a59f42276891c0a4ce683fcda1aa1b4fd1065d68116c264e4bf49b9d318a0ed" dependencies = [ "anyhow", + "asyncband", "equivalent", "foyer-common", "foyer-memory", "foyer-storage", "foyer-tokio", "futures-util", - "mea", "mixtrics", "pin-project", "serde", @@ -1050,9 +1056,9 @@ dependencies = [ [[package]] name = "foyer-common" -version = "0.22.3" +version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3db80d5dece93adb7ad709c84578794724a9cba342a7e566c3551c7ec626789" +checksum = "643b47d510032a01e5af70dca288b0c5ec531646c1a8392e793fb5b0c13bef30" dependencies = [ "anyhow", "bincode", @@ -1077,21 +1083,21 @@ dependencies = [ [[package]] name = "foyer-memory" -version = "0.22.3" +version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db907f40a527ca2aa2f40a5f68b32ea58aa70f050cd233518e9ffd402cfba6ce" +checksum = "ae51f5089f3d9025ae77f17ea66d2489ac0f82fbb1d91462807bea174d486d82" dependencies = [ "anyhow", + "asyncband", "bitflags 2.13.1", - "cmsketch", + "datasketches", "equivalent", "foyer-common", "foyer-intrusive-collections", "foyer-tokio", "futures-util", - "hashbrown 0.16.1", - "itertools 0.14.0", - "mea", + "hashbrown 0.17.1", + "itertools 0.15.0", "mixtrics", "parking_lot", "paste", @@ -1102,12 +1108,13 @@ dependencies = [ [[package]] name = "foyer-storage" -version = "0.22.3" +version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1983f1db3d0710e9c9d5fc116d9202dccd41a2d1e032572224f1aff5520aa958" +checksum = "118192f532ba013f0cdc607efc22324b9ed855baed185608af0daa986e835c6b" dependencies = [ "allocator-api2", "anyhow", + "asyncband", "bytes", "core_affinity", "equivalent", @@ -1118,15 +1125,14 @@ dependencies = [ "fs4", "futures-core", "futures-util", - "hashbrown 0.16.1", + "hashbrown 0.17.1", "io-uring", - "itertools 0.14.0", + "itertools 0.15.0", "libc", "lz4", - "mea", "parking_lot", "pin-project", - "rand 0.9.5", + "rand 0.10.2", "serde", "tracing", "twox-hash", @@ -1135,9 +1141,9 @@ dependencies = [ [[package]] name = "foyer-tokio" -version = "0.22.3" +version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6577b05a7ffad0db555aedf00bfe52af818220fc4c1c3a7a12520896fc38627" +checksum = "6741d1133dfaab64d3f8a9290bbfed3685084add28f8b6ee7a6264aa4dc26918" dependencies = [ "tokio", ] @@ -2044,16 +2050,6 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" -[[package]] -name = "mea" -version = "0.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c709842c4ce65cb91e2666ad5319dfc1efc3af0d34f02075eddca9000d9f8afb" -dependencies = [ - "hashbrown 0.17.1", - "slab", -] - [[package]] name = "memchr" version = "2.8.3" @@ -2986,7 +2982,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -3443,7 +3439,7 @@ dependencies = [ "getrandom 0.4.3", "once_cell", "rustix", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -4244,7 +4240,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]]