Many imorovments
This commit is contained in:
+72
-24
@@ -79,6 +79,13 @@ pub struct ThumbnailReady {
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub rgba: Vec<u8>,
|
||||
/// Whether these pixels came off local disk rather than the server.
|
||||
///
|
||||
/// The grid paints both identically, so this exists solely for
|
||||
/// reachability: a store hit is evidence about the *cache*, not the
|
||||
/// network, and treating one as proof of connectivity clears offline mode
|
||||
/// before a single request has been attempted.
|
||||
pub from_cache: bool,
|
||||
}
|
||||
|
||||
/// Capture metadata read from the same header the thumbnail needed.
|
||||
@@ -330,9 +337,7 @@ pub fn spawn_sidecar_writes(
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
let rt = match tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
let rt = match crate::net_runtime::build()
|
||||
{
|
||||
Ok(rt) => rt,
|
||||
Err(e) => {
|
||||
@@ -560,9 +565,7 @@ fn run_scan(
|
||||
}
|
||||
let catalog = Catalog::open(&catalog_path).map_err(ScanFailure::local)?;
|
||||
|
||||
let rt = tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
let rt = crate::net_runtime::build()
|
||||
.map_err(ScanFailure::local)?;
|
||||
|
||||
rt.block_on(async {
|
||||
@@ -887,9 +890,7 @@ pub fn spawn_pin_fetch(
|
||||
return;
|
||||
}
|
||||
|
||||
let rt = match tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
let rt = match crate::net_runtime::build()
|
||||
{
|
||||
Ok(rt) => rt,
|
||||
Err(e) => {
|
||||
@@ -1055,9 +1056,7 @@ pub fn spawn_full_fetch(
|
||||
}
|
||||
}
|
||||
|
||||
let rt = match tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
let rt = match crate::net_runtime::build()
|
||||
{
|
||||
Ok(rt) => rt,
|
||||
Err(e) => {
|
||||
@@ -1160,6 +1159,7 @@ pub fn spawn_thumbnails(
|
||||
width,
|
||||
height,
|
||||
rgba,
|
||||
from_cache: true,
|
||||
});
|
||||
}
|
||||
// A corrupt stored blob is a miss, not a failure.
|
||||
@@ -1202,9 +1202,7 @@ pub fn spawn_thumbnails(
|
||||
return;
|
||||
}
|
||||
|
||||
let rt = match tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
let rt = match crate::net_runtime::build()
|
||||
{
|
||||
Ok(rt) => rt,
|
||||
Err(e) => {
|
||||
@@ -1392,6 +1390,7 @@ async fn fetch_one(
|
||||
width: preview.width,
|
||||
height: preview.height,
|
||||
rgba: preview.rgba,
|
||||
from_cache: false,
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -1675,9 +1674,7 @@ pub fn spawn_sweep(
|
||||
return;
|
||||
}
|
||||
|
||||
let rt = match tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
let rt = match crate::net_runtime::build()
|
||||
{
|
||||
Ok(rt) => rt,
|
||||
Err(e) => {
|
||||
@@ -2169,9 +2166,7 @@ mod tests {
|
||||
// The ordering guarantee is what lets a caller pair results back to
|
||||
// their inputs; without it a lane's dates could be attributed to the
|
||||
// wrong images.
|
||||
let rt = tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
let rt = crate::net_runtime::build()
|
||||
.unwrap();
|
||||
|
||||
let out = rt.block_on(async {
|
||||
@@ -2195,9 +2190,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn join_all_of_nothing_completes() {
|
||||
let rt = tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
let rt = crate::net_runtime::build()
|
||||
.unwrap();
|
||||
let out: Vec<i32> =
|
||||
rt.block_on(async { futures_join_all(Vec::<std::future::Ready<i32>>::new()).await });
|
||||
@@ -2636,6 +2629,61 @@ mod tests {
|
||||
assert!(out[2] > out[0], "channel order survived the round trip");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_store_hit_is_not_evidence_the_server_is_reachable() {
|
||||
// The regression this guards: store hits were delivered as the same
|
||||
// `Ready` the network path sends, and the UI took any `Ready` as proof
|
||||
// of connectivity. A mostly-cached window then declared "back online"
|
||||
// against a server that was down — clearing the banner and kicking off
|
||||
// a sweep that immediately failed, on every scroll.
|
||||
let dir = std::env::temp_dir().join(format!("dr-ui-provenance-{}", std::process::id()));
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
|
||||
let rgba: Vec<u8> = std::iter::repeat_n([10u8, 20, 30, 255], 8 * 8)
|
||||
.flatten()
|
||||
.collect();
|
||||
let mut store = ThumbStore::open(&dir).unwrap();
|
||||
let bytes = dr_thumbs::encode_rgba(8, 8, &rgba).unwrap();
|
||||
store
|
||||
.put(99, dr_thumbs::ThumbSize::Grid, &dr_thumbs::Thumbnail {
|
||||
width: 8,
|
||||
height: 8,
|
||||
bytes,
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
// The split in `spawn_thumbnails`: a hit decodes off local disk and is
|
||||
// reported with `from_cache` set, which is what the reachability gate
|
||||
// keys on.
|
||||
let stored = store.get(99, dr_thumbs::ThumbSize::Grid).unwrap().expect("stored");
|
||||
let (width, height, rgba) = dr_thumbs::decode_rgba(&stored.bytes).unwrap();
|
||||
let hit = ThumbnailReady {
|
||||
row: 0,
|
||||
width,
|
||||
height,
|
||||
rgba,
|
||||
from_cache: true,
|
||||
};
|
||||
|
||||
assert!(
|
||||
hit.from_cache,
|
||||
"a thumbnail read from the store must not be mistaken for a fetch"
|
||||
);
|
||||
|
||||
// And the mechanism it feeds: an offline tracker must survive it.
|
||||
let mut reach = dr_sync::Reachability::new();
|
||||
let now = std::time::Instant::now();
|
||||
reach.mark_unreachable("network error".into(), now);
|
||||
|
||||
if !hit.from_cache {
|
||||
reach.mark_reachable(now);
|
||||
}
|
||||
assert!(
|
||||
reach.is_offline(),
|
||||
"replaying cached thumbnails must leave offline mode intact"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn thumbs_live_beside_the_catalog_not_in_the_cache() {
|
||||
// They sync to the server and are shared with other clients, so a
|
||||
|
||||
Reference in New Issue
Block a user