///|
/// Compares the transition evidence from two replay reports.
pub fn[S] compare_reports(
left : ReplayReport[S],
right : ReplayReport[S],
) -> Divergence? {
if left.initial_sequence != right.initial_sequence {
return Some({
sequence: if left.initial_sequence < right.initial_sequence {
left.initial_sequence
} else {
right.initial_sequence
},
kind: LengthMismatch,
left_hash: left.final_state_hash,
right_hash: right.final_state_hash,
message: "replays start from different sequence boundaries",
})
}
let common = if left.state_hashes.length() < right.state_hashes.length() {
left.state_hashes.length()
} else {
right.state_hashes.length()
}
for index = 0; index < common; index = index + 1 {
if left.state_hashes[index] != right.state_hashes[index] {
return Some({
sequence: left.initial_sequence + index,
kind: StateMismatch,
left_hash: left.state_hashes[index],
right_hash: right.state_hashes[index],
message: "state fingerprints diverge",
})
}
}
if left.status != right.status {
return Some({
sequence: left.initial_sequence + common,
kind: StatusMismatch,
left_hash: left.final_state_hash,
right_hash: right.final_state_hash,
message: "replay completion statuses differ",
})
}
if left.state_hashes.length() != right.state_hashes.length() {
return Some({
sequence: left.initial_sequence + common,
kind: LengthMismatch,
left_hash: left.final_state_hash,
right_hash: right.final_state_hash,
message: "replays applied different numbers of transitions",
})
}
None
}
///|
/// Replays one journal through old and new reducer versions.
///
/// Compatibility means both versions completed and every state boundary has
/// the same fingerprint.
pub fn[P, S] compare_reducer_versions(
journal : Journal[P],
initial_state : S,
payload_fingerprint : (P) -> String,
state_fingerprint : (S) -> String,
old_reducer : (S, JournalEvent[P]) -> Transition[S],
new_reducer : (S, JournalEvent[P]) -> Transition[S],
) -> MigrationReport[S] {
let old_report = replay(
journal, initial_state, payload_fingerprint, state_fingerprint, old_reducer,
)
let new_report = replay(
journal, initial_state, payload_fingerprint, state_fingerprint, new_reducer,
)
let first_divergence = compare_reports(old_report, new_report)
let compatible = first_divergence is None &&
old_report.status == Completed &&
new_report.status == Completed
{ compatible, old_report, new_report, first_divergence }
}