Skip to content

Commit 52ab1a5

Browse files
committed
Assert cache lookup purge indexing
1 parent e214795 commit 52ab1a5

3 files changed

Lines changed: 42 additions & 9 deletions

File tree

docs/cache-backends.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,7 @@ fluxheim --config /etc/fluxheim/fluxheim.toml cache-lookup \
445445
--require-object \
446446
--expect-tier disk \
447447
--expect-status 200 \
448+
--expect-purge-indexed \
448449
--expect-freshness-state fresh
449450
```
450451

@@ -468,7 +469,8 @@ Host header. For release scripts, `cache-lookup --require-object` fails when
468469
the selected key has no cached object, repeated `--expect-tier memory|disk`
469470
flags fail when no matching object is present in an allowed storage tier,
470471
repeated `--expect-status` flags fail when no matching object has an allowed
471-
cached HTTP status, and repeated
472+
cached HTTP status, `--expect-purge-indexed` fails when no matching object is
473+
reachable through the bounded purge index, and repeated
472474
`--expect-freshness-state fresh|stale|expired` flags fail when none of the
473475
matching objects has an allowed freshness state.
474476

docs/versioning-plan.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -430,9 +430,10 @@ Stable scope:
430430
bounded safe request headers such as `Accept-Language` and
431431
`Accept-Encoding`, so operators can debug negotiated `Vary` variants
432432
without contacting upstreams. `cache-lookup` can fail closed for deploy
433-
checks with `--require-object`, `--expect-tier`, `--expect-status`, and
434-
`--expect-freshness-state fresh|stale|expired`, so scripts can assert that
435-
warm objects exist and are in an acceptable serving state without
433+
checks with `--require-object`, `--expect-tier`, `--expect-status`,
434+
`--expect-purge-indexed`, and `--expect-freshness-state
435+
fresh|stale|expired`, so scripts can assert that warm objects exist, are
436+
purge-index reachable, and are in an acceptable serving state without
436437
contacting upstreams;
437438
- cache warm/import/export workflows for deploys and repository mirrors,
438439
including clear failure accounting and no hidden best-effort misses.

src/cli.rs

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,10 @@ pub enum CliCommand {
248248
/// Required cached-object storage tier. May be repeated: memory, disk.
249249
#[arg(long = "expect-tier", value_name = "TIER")]
250250
expect_tiers: Vec<String>,
251+
252+
/// Require at least one matching cached object to be present in the purge index.
253+
#[arg(long)]
254+
expect_purge_indexed: bool,
251255
},
252256
}
253257

@@ -477,6 +481,7 @@ fn run_command(
477481
expect_freshness_states,
478482
expect_statuses,
479483
expect_tiers,
484+
expect_purge_indexed,
480485
} => run_cache_lookup_command(CacheLookupOptions {
481486
config_path,
482487
host: host.clone(),
@@ -488,6 +493,7 @@ fn run_command(
488493
expect_freshness_states: expect_freshness_states.clone(),
489494
expect_statuses: expect_statuses.clone(),
490495
expect_tiers: expect_tiers.clone(),
496+
expect_purge_indexed: *expect_purge_indexed,
491497
}),
492498
}
493499
}
@@ -533,6 +539,7 @@ struct CacheLookupOptions<'a> {
533539
expect_freshness_states: Vec<String>,
534540
expect_statuses: Vec<u16>,
535541
expect_tiers: Vec<String>,
542+
expect_purge_indexed: bool,
536543
}
537544

538545
#[cfg(feature = "cache")]
@@ -870,6 +877,7 @@ fn run_cache_lookup_command(
870877
&expected_states,
871878
&options.expect_statuses,
872879
&expected_tiers,
880+
options.expect_purge_indexed,
873881
)?;
874882

875883
println!("cache object lookup:");
@@ -973,6 +981,7 @@ fn validate_cache_lookup_expectations(
973981
expected_states: &[crate::cache::CacheObjectFreshnessState],
974982
expected_statuses: &[u16],
975983
expected_tiers: &[crate::cache::CacheObjectTier],
984+
expect_purge_indexed: bool,
976985
) -> Result<(), Box<dyn Error + Send + Sync>> {
977986
if require_object && lookup.objects.is_empty() {
978987
return Err("cache-lookup expected at least one cached object, found none".into());
@@ -1051,6 +1060,9 @@ fn validate_cache_lookup_expectations(
10511060
return Err(format!("cache-lookup expected tier {expected}, found {found}").into());
10521061
}
10531062
}
1063+
if expect_purge_indexed && !lookup.objects.iter().any(|object| object.purge_indexed) {
1064+
return Err("cache-lookup expected at least one purge-indexed object, found none".into());
1065+
}
10541066
Ok(())
10551067
}
10561068

@@ -1098,13 +1110,15 @@ fn run_cache_lookup_command(
10981110
expect_freshness_states,
10991111
expect_statuses,
11001112
expect_tiers,
1113+
expect_purge_indexed,
11011114
} = options;
11021115
let _ = (config_path, host, headers, method, path, query);
11031116
let _ = (
11041117
require_object,
11051118
expect_freshness_states,
11061119
expect_statuses,
11071120
expect_tiers,
1121+
expect_purge_indexed,
11081122
);
11091123
Err("cache-lookup requires the proxy and cache features".into())
11101124
}
@@ -2785,7 +2799,7 @@ mod tests {
27852799
let tiers = super::parse_cache_lookup_tiers(&["memory".to_owned()]).unwrap();
27862800

27872801
assert!(
2788-
super::validate_cache_lookup_expectations(&lookup, true, &states, &[200], &tiers)
2802+
super::validate_cache_lookup_expectations(&lookup, true, &states, &[200], &tiers, true)
27892803
.is_ok()
27902804
);
27912805
assert!(
@@ -2794,14 +2808,15 @@ mod tests {
27942808
false,
27952809
&[crate::cache::CacheObjectFreshnessState::Fresh],
27962810
&[],
2797-
&[]
2811+
&[],
2812+
false
27982813
)
27992814
.unwrap_err()
28002815
.to_string()
28012816
.contains("expected freshness state fresh, found stale")
28022817
);
28032818
assert!(
2804-
super::validate_cache_lookup_expectations(&lookup, false, &[], &[404], &[])
2819+
super::validate_cache_lookup_expectations(&lookup, false, &[], &[404], &[], false)
28052820
.unwrap_err()
28062821
.to_string()
28072822
.contains("expected status 404, found 200")
@@ -2812,7 +2827,8 @@ mod tests {
28122827
false,
28132828
&[],
28142829
&[],
2815-
&[crate::cache::CacheObjectTier::Disk]
2830+
&[crate::cache::CacheObjectTier::Disk],
2831+
false
28162832
)
28172833
.unwrap_err()
28182834
.to_string()
@@ -2837,12 +2853,26 @@ mod tests {
28372853
true,
28382854
&[],
28392855
&[],
2840-
&[]
2856+
&[],
2857+
false
28412858
)
28422859
.unwrap_err()
28432860
.to_string()
28442861
.contains("expected at least one cached object")
28452862
);
2863+
assert!(
2864+
super::validate_cache_lookup_expectations(
2865+
&cache_lookup_without_objects(),
2866+
false,
2867+
&[],
2868+
&[],
2869+
&[],
2870+
true
2871+
)
2872+
.unwrap_err()
2873+
.to_string()
2874+
.contains("expected at least one purge-indexed object")
2875+
);
28462876
}
28472877

28482878
#[cfg(all(feature = "cache", feature = "proxy"))]

0 commit comments

Comments
 (0)