diff --git a/test-fixtures/src/bin/cache.rs b/test-fixtures/src/bin/cache.rs index 6a1846b6..0b8dabf6 100644 --- a/test-fixtures/src/bin/cache.rs +++ b/test-fixtures/src/bin/cache.rs @@ -64,6 +64,7 @@ fn main() { run_test!(test_implicit_cancel_of_pending); run_test!(test_explicit_cancel); run_test!(test_collapse_across_vary); + run_test!(test_collapse_from_later_vary); run_test!(test_stream_back); run_test!(test_stream_back_fixed); @@ -1202,6 +1203,66 @@ fn test_collapse_across_vary() { assert!(!txn1.pending().unwrap()); } +fn test_collapse_from_later_vary() { + // Per issue #626: cache contains two stale variants under Vary: Header-A. + // Txn1 (foo), Txn2 (bar), Txn3 (bar) dispatch; Txn1 and Txn2 receive GoGet, + // Txn3 waits on Txn2. Txn1 completes with an *empty* vary rule, which can in + // principle fulfill Txn3. Txn2 is then abandoned. Txn3 should get Found. + let key = new_key(); + let header_a = HeaderName::from_static("header-a"); + + let b = insert(key.clone(), Duration::ZERO) + .header(&header_a, "foo") + .vary_by([&header_a]) + .execute() + .unwrap(); + b.finish().unwrap(); + + let b = insert(key.clone(), Duration::ZERO) + .header(&header_a, "bar") + .vary_by([&header_a]) + .execute() + .unwrap(); + b.finish().unwrap(); + + let txn1 = Transaction::lookup(key.clone()) + .header(&header_a, "foo") + .execute() + .unwrap(); + + let txn2 = Transaction::lookup(key.clone()) + .header(&header_a, "bar") + .execute() + .unwrap(); + + let pending_txn3 = Transaction::lookup(key.clone()) + .header(&header_a, "bar") + .execute_async() + .unwrap(); + assert!(pending_txn3.pending().unwrap(), "txn3 should be waiting on txn2"); + assert!(txn1.must_insert_or_update()); + assert!(txn2.must_insert_or_update()); + + let mut writer = txn1.insert(Duration::from_secs(120)).execute().unwrap(); + writer.write_all(b"the-response").unwrap(); + writer.finish().unwrap(); + + // Abandon Txn2 to verify that Txn3 falls back to + // Txn1's wildcard entry instead of spawning a duplicate origin fetch. + txn2.cancel_insert_or_update().unwrap(); + + // Txn3 must resolve as Found from Txn1's empty-vary insert. + let txn3 = pending_txn3.wait().unwrap(); + assert!( + txn3.found().is_some(), + "txn3 should receive the cached entry from txn1's empty-vary insert" + ); + assert!( + !txn3.must_insert_or_update(), + "txn3 must not be issued a GoGet" + ); +} + fn test_stream_back() { let key = new_key();