mirror of
https://github.com/rustfs/rustfs.git
synced 2026-09-07 12:35:54 +00:00
test(heal): preserve MRF replay identity matrix (#7398)
Add an MRF pipeline regression that replays a single authoritative journal containing same-object records that differ by kind and erasure-set scope while a stale legacy mirror is present. The test proves restart replay admits each authoritative responsibility independently and never merges the stale mirror epoch. Co-authored-by: zhi22915 <qiuzgang@gmail.com>
This commit is contained in:
@@ -359,6 +359,53 @@ async fn authoritative_journal_is_not_merged_with_legacy_mirror() {
|
||||
}));
|
||||
}
|
||||
|
||||
/// The authoritative journal carries the full replay responsibility identity.
|
||||
/// A stale legacy mirror must not collapse same-object records that differ by
|
||||
/// kind or erasure-set scope after restart.
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
|
||||
#[serial]
|
||||
async fn authoritative_journal_replay_preserves_kind_and_scope_identity() {
|
||||
let (disk_paths, storage) = heal_env().await;
|
||||
register_local_disks(&disk_paths, "mrf-authoritative-identity-test").await;
|
||||
|
||||
let mut authoritative = scoped_journal_record(3, "identity-bucket", "same-object", None, 0, 3, 7);
|
||||
authoritative.extend(scoped_journal_record(3, "identity-bucket", "same-object", None, 0, 3, 8));
|
||||
authoritative.extend(journal_record(2, "identity-bucket", "same-object", None, 0));
|
||||
authoritative.extend(journal_record(1, "identity-bucket", "same-object", Some([4u8; 16]), 0));
|
||||
let stale_legacy = journal_record(3, "identity-bucket", "stale-legacy-object", None, 0);
|
||||
write_journal_path_to_disks(&disk_paths, SCOPED_JOURNAL_REL, &authoritative);
|
||||
write_journal_path_to_disks(&disk_paths, JOURNAL_REL, &stale_legacy);
|
||||
|
||||
let manager = make_manager(storage);
|
||||
let replayed = mrf_queue::replay_journal_once(&manager).await;
|
||||
assert_eq!(
|
||||
replayed, 4,
|
||||
"all authoritative kind/scope identities must decode before manager admission"
|
||||
);
|
||||
|
||||
let snapshot = manager.operations_snapshot().await;
|
||||
assert_eq!(
|
||||
snapshot.queued_by_source.mrf, 4,
|
||||
"same-object MRF replay must retain distinct kind and scope responsibilities"
|
||||
);
|
||||
assert_eq!(
|
||||
snapshot.queued_by_priority.normal, 2,
|
||||
"the two scoped partial-write records must remain independently queued"
|
||||
);
|
||||
assert_eq!(
|
||||
snapshot.queued_by_priority.high, 1,
|
||||
"metadata corruption must not merge with object repair responsibility"
|
||||
);
|
||||
assert_eq!(
|
||||
snapshot.queued_by_priority.urgent, 1,
|
||||
"decode-failure repair must not merge with object repair responsibility"
|
||||
);
|
||||
assert!(disk_paths.iter().all(|path| {
|
||||
!Path::new(path).join(META_BUCKET).join(JOURNAL_REL).exists()
|
||||
&& !Path::new(path).join(META_BUCKET).join(SCOPED_JOURNAL_REL).exists()
|
||||
}));
|
||||
}
|
||||
|
||||
/// If replay reaches a full heal-manager queue, the old journal remains the
|
||||
/// durable restart anchor until a later consumer flush publishes the pending
|
||||
/// successor snapshot.
|
||||
|
||||
Reference in New Issue
Block a user