mirror of
https://github.com/rustfs/rustfs.git
synced 2026-09-03 06:37:34 +08:00
fix(scanner): fence legacy usage floor fallback
Co-Authored-By: heihutu <heihutu@gmail.com>
This commit is contained in:
@@ -1347,6 +1347,7 @@ pub(super) async fn persisted_usage_floor_for_startup(
|
||||
};
|
||||
for primary_path in [DATA_USAGE_OBJ_NAME_PATH.as_str(), LEGACY_DATA_USAGE_OBJ_NAME_PATH.as_str()] {
|
||||
let backup_path = format!("{primary_path}.bkp");
|
||||
let is_v2_path = primary_path == DATA_USAGE_OBJ_NAME_PATH.as_str();
|
||||
let primary_epoch = match read_config_with_revision(storeapi.clone(), primary_path).await {
|
||||
Ok((Some(data), _)) => {
|
||||
let usage = serde_json::from_slice::<DataUsageInfo>(&data).map_err(|err| {
|
||||
@@ -1365,8 +1366,15 @@ pub(super) async fn persisted_usage_floor_for_startup(
|
||||
None
|
||||
} else {
|
||||
let epoch = usage.scanner_epoch.unwrap_or_default();
|
||||
update_floor(&mut floor, &usage, primary_path)?;
|
||||
Some(epoch)
|
||||
// A legacy snapshot may be structurally valid but older
|
||||
// than an incomplete v2 snapshot left by a newer leader.
|
||||
// Do not let that candidate regress the startup floor.
|
||||
if !is_v2_path && invalid_baseline_epoch.is_some_and(|fenced_epoch| epoch < fenced_epoch) {
|
||||
None
|
||||
} else {
|
||||
update_floor(&mut floor, &usage, primary_path)?;
|
||||
Some(epoch)
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok((None, _)) => None,
|
||||
|
||||
@@ -2038,6 +2038,34 @@ async fn scanner_usage_floor_rejects_backup_older_than_incomplete_v2_primary() {
|
||||
assert!(err.to_string().contains("no authoritative baseline"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn scanner_usage_floor_rejects_older_legacy_primary_after_incomplete_v2_primary() {
|
||||
let store = Arc::new(MemoryConfigStore::default());
|
||||
let primary = DataUsageInfo {
|
||||
scanner_epoch: Some(7),
|
||||
scanner_cycle: Some(100),
|
||||
usage_snapshot_complete: false,
|
||||
..Default::default()
|
||||
};
|
||||
let mut legacy = complete_usage_with_bucket_count(Some(std::time::SystemTime::UNIX_EPOCH), 0);
|
||||
legacy.scanner_epoch = Some(6);
|
||||
legacy.scanner_cycle = Some(103);
|
||||
for (path, usage) in [
|
||||
(DATA_USAGE_OBJ_NAME_PATH.as_str(), primary),
|
||||
(LEGACY_DATA_USAGE_OBJ_NAME_PATH.as_str(), legacy),
|
||||
] {
|
||||
store.objects.lock().await.insert(
|
||||
memory_config_key(RUSTFS_META_BUCKET, path),
|
||||
serde_json::to_vec(&usage).expect("usage snapshot should encode"),
|
||||
);
|
||||
}
|
||||
|
||||
let err = persisted_usage_floor_for_startup(store, true)
|
||||
.await
|
||||
.expect_err("an older legacy baseline must not cross the incomplete v2 epoch fence");
|
||||
assert!(err.to_string().contains("no authoritative baseline"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn scanner_leadership_fencing_recovers_incomplete_v2_primary_from_backup() {
|
||||
let store = Arc::new(MemoryConfigStore::default());
|
||||
|
||||
Reference in New Issue
Block a user