mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-09-08 15:41:15 +02:00
rust: cover the READS scrub reconstruction path (#11027)
This commit is contained in:
@@ -6102,4 +6102,89 @@ mod tests {
|
|||||||
assert!(vif.expire_at_sec >= before + ttl.to_seconds());
|
assert!(vif.expire_at_sec >= before + ttl.to_seconds());
|
||||||
assert!(vif.expire_at_sec <= before + ttl.to_seconds() + 5);
|
assert!(vif.expire_at_sec <= before + ttl.to_seconds() + 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn scrub_ec_volume_1(
|
||||||
|
service: &VolumeGrpcService,
|
||||||
|
mode: i32,
|
||||||
|
) -> volume_server_pb::ScrubEcVolumeResponse {
|
||||||
|
service
|
||||||
|
.scrub_ec_volume(Request::new(volume_server_pb::ScrubEcVolumeRequest {
|
||||||
|
mode,
|
||||||
|
volume_ids: vec![1],
|
||||||
|
force_deleted_needles_check: false,
|
||||||
|
}))
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.into_inner()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_scrub_ec_volume_reads_mode_reconstructs_missing_shard() {
|
||||||
|
let (service, _tmp) = make_local_service_with_volume("", None);
|
||||||
|
service
|
||||||
|
.volume_ec_shards_generate(Request::new(
|
||||||
|
volume_server_pb::VolumeEcShardsGenerateRequest {
|
||||||
|
volume_id: 1,
|
||||||
|
collection: String::new(),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
service
|
||||||
|
.volume_ec_shards_mount(Request::new(
|
||||||
|
volume_server_pb::VolumeEcShardsMountRequest {
|
||||||
|
volume_id: 1,
|
||||||
|
collection: String::new(),
|
||||||
|
shard_ids: (0..14).collect(),
|
||||||
|
source_disk_type: String::new(),
|
||||||
|
recover_missing_index: false,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Seed the shard-location cache so the scrub skips the master lookup.
|
||||||
|
// The explicit-gRPC-port form targets port 1, so remote reads fail fast
|
||||||
|
// with connection-refused instead of resolving to a live local port.
|
||||||
|
{
|
||||||
|
let store = service.state.store.read().unwrap();
|
||||||
|
let ecv = store.find_ec_volume(VolumeId(1)).unwrap();
|
||||||
|
let mut locs = ecv.shard_locations.write().unwrap();
|
||||||
|
for sid in 0u8..14 {
|
||||||
|
locs.insert(sid, vec!["127.0.0.1:255.1".to_string()]);
|
||||||
|
}
|
||||||
|
*ecv.shard_locations_refresh_time.lock().unwrap() =
|
||||||
|
Some(std::time::Instant::now());
|
||||||
|
}
|
||||||
|
|
||||||
|
// All shards local: FULL is clean.
|
||||||
|
let resp = scrub_ec_volume_1(&service, 2).await;
|
||||||
|
assert!(resp.broken_volume_ids.is_empty(), "{:?}", resp.details);
|
||||||
|
assert_eq!(resp.total_files, 1);
|
||||||
|
|
||||||
|
service
|
||||||
|
.volume_ec_shards_unmount(Request::new(
|
||||||
|
volume_server_pb::VolumeEcShardsUnmountRequest {
|
||||||
|
volume_id: 1,
|
||||||
|
shard_ids: vec![0],
|
||||||
|
encode_ts_ns: 0,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// With a shard unreadable, FULL flags it AND fails the needle...
|
||||||
|
let resp = scrub_ec_volume_1(&service, 2).await;
|
||||||
|
assert_eq!(resp.broken_volume_ids, vec![1]);
|
||||||
|
assert!(resp.broken_shard_infos.iter().any(|s| s.shard_id == 0));
|
||||||
|
assert!(!resp.details.is_empty());
|
||||||
|
|
||||||
|
// ...while READS still reports the shard broken but reconstructs the
|
||||||
|
// interval from the local survivors, so no needle errors surface.
|
||||||
|
let resp = scrub_ec_volume_1(&service, 5).await;
|
||||||
|
assert_eq!(resp.broken_volume_ids, vec![1]);
|
||||||
|
assert!(resp.broken_shard_infos.iter().any(|s| s.shard_id == 0));
|
||||||
|
assert!(resp.details.is_empty(), "{:?}", resp.details);
|
||||||
|
assert_eq!(resp.total_files, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user