mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-25 05:26:50 +00:00
fix(scanner): bootstrap pristine usage baseline (#6471)
* fix(ecstore): fence pool metadata replica updates * fix(ecstore): block decommission on unsafe pool metadata * fix(ecstore): block writes after pool metadata save errors * fix(ecstore): latch pool metadata writes before await * fix(scanner): bootstrap pristine usage baseline
This commit is contained in:
@@ -33,7 +33,7 @@ use crate::bucket::{
|
||||
use crate::cache_value::metacache_set::{ListPathRawOptions, list_path_raw};
|
||||
use crate::config::com::{
|
||||
CONFIG_PREFIX, delete_config, read_config_limited_preserve_empty, read_config_limited_preserve_empty_with_metadata,
|
||||
read_config_no_lock_preserve_empty_with_metadata, read_config_preserve_empty, save_config, save_config_with_opts,
|
||||
read_config_no_lock_preserve_empty_with_metadata, read_config_preserve_empty, save_config_with_opts,
|
||||
save_config_with_opts_quiet,
|
||||
};
|
||||
use crate::data_movement;
|
||||
@@ -4848,7 +4848,7 @@ impl ECStore {
|
||||
let mut pool_meta = self.pool_meta.write().await;
|
||||
record_decommission_unresolved_entry(&mut pool_meta, idx, generation, entry)?;
|
||||
}
|
||||
self.save_current_pool_meta()
|
||||
self.save_current_pool_meta(&[idx])
|
||||
.await
|
||||
.map_err(|err| Error::other(format!("decommission unresolved entry ledger save failed: {err}")))
|
||||
}
|
||||
@@ -9038,7 +9038,7 @@ impl ECStore {
|
||||
self.run_guarded_decommission_side_effect(rx, &operation_gate, || {
|
||||
self.check_after_decommission_unfenced(idx, generation)
|
||||
})
|
||||
.await
|
||||
.await
|
||||
}
|
||||
|
||||
async fn check_after_decommission_unfenced(
|
||||
|
||||
@@ -743,7 +743,12 @@ where
|
||||
let backup_seed = load_data_usage_for_bucket_removal(store, DATA_USAGE_OBJ_NAME_PATH.as_str())
|
||||
.await?
|
||||
.map(|(data_usage_info, _)| data_usage_info)
|
||||
.or_else(|| primary_seed.clone());
|
||||
.filter(|data_usage_info| !data_usage_info.usage_snapshot_bootstrap_pending)
|
||||
.or_else(|| {
|
||||
primary_seed
|
||||
.clone()
|
||||
.filter(|data_usage_info| !data_usage_info.usage_snapshot_bootstrap_pending)
|
||||
});
|
||||
remove_bucket_usage_from_object_with_retries_and_publication(
|
||||
store,
|
||||
DATA_USAGE_OBJ_BACKUP_PATH.as_str(),
|
||||
@@ -4646,6 +4651,32 @@ mod tests {
|
||||
assert!(state.backup_object.is_none());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn remove_bucket_usage_does_not_seed_backup_from_pristine_bootstrap_marker() {
|
||||
let marker = DataUsageInfo {
|
||||
last_update: Some(SystemTime::now()),
|
||||
usage_snapshot_converged: Some(false),
|
||||
usage_snapshot_bootstrap_pending: true,
|
||||
..Default::default()
|
||||
};
|
||||
let store = Arc::new(UsageCasStore {
|
||||
state: Mutex::new(UsageCasState {
|
||||
object: Some((serde_json::to_vec(&marker).expect("bootstrap marker should encode"), 1)),
|
||||
..Default::default()
|
||||
}),
|
||||
});
|
||||
|
||||
remove_bucket_usage_from_backend_with_store(store.as_ref(), "bucket-a")
|
||||
.await
|
||||
.expect("bucket removal should preserve the pending primary without creating a backup");
|
||||
|
||||
let state = store.state.lock().await;
|
||||
assert!(state.backup_object.is_none());
|
||||
let saved = serde_json::from_slice::<DataUsageInfo>(&state.object.as_ref().expect("pending primary should remain").0)
|
||||
.expect("pending primary should decode");
|
||||
assert!(saved.usage_snapshot_bootstrap_pending);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn remove_bucket_usage_migrates_legacy_snapshot_without_hiding_other_buckets() {
|
||||
let mut legacy = data_usage_info_for_test("bucket-a", 2, 84, SystemTime::now());
|
||||
|
||||
Reference in New Issue
Block a user