huskies: merge 538_bug_done_archived_sweep_never_fires_because_stage_done_projection_uses_utc_now_instead_of_real_merged_at_timestamp

This commit is contained in:
dave
2026-04-11 13:25:51 +00:00
parent 5d193bb568
commit 4ab723f40b
5 changed files with 130 additions and 4 deletions
+19
View File
@@ -99,6 +99,10 @@ pub struct PipelineItemCrdt {
/// Used for timeout-based reclaim: if a node crashes, other nodes can
/// reclaim the item after the timeout expires.
pub claimed_at: LwwRegisterCrdt<f64>,
/// Unix timestamp (seconds) when the item was merged to master.
/// Written once when the item transitions to `5_done`. Used by the
/// sweep loop to determine when to promote to `6_archived`.
pub merged_at: LwwRegisterCrdt<f64>,
}
/// CRDT node that holds a single peer's presence entry.
@@ -131,6 +135,9 @@ pub struct PipelineItemView {
pub claimed_by: Option<String>,
/// Unix timestamp when the item was claimed.
pub claimed_at: Option<f64>,
/// Unix timestamp (seconds) when the item was merged to master.
/// `None` for items that were never in `5_done` or for legacy items.
pub merged_at: Option<f64>,
}
/// A snapshot of a single node presence entry derived from the CRDT document.
@@ -413,6 +420,7 @@ pub fn write_item(
depends_on: Option<&str>,
claimed_by: Option<&str>,
claimed_at: Option<f64>,
merged_at: Option<f64>,
) {
let Some(state_mutex) = get_crdt() else {
return;
@@ -468,6 +476,11 @@ pub fn write_item(
s.crdt.doc.items[idx].claimed_at.set(ca)
});
}
if let Some(ma) = merged_at {
apply_and_persist(&mut state, |s| {
s.crdt.doc.items[idx].merged_at.set(ma)
});
}
// Broadcast a CrdtEvent if the stage actually changed.
let stage_changed = old_stage.as_deref() != Some(stage);
@@ -496,6 +509,7 @@ pub fn write_item(
"depends_on": depends_on.unwrap_or(""),
"claimed_by": claimed_by.unwrap_or(""),
"claimed_at": claimed_at.unwrap_or(0.0),
"merged_at": merged_at.unwrap_or(0.0),
})
.into();
@@ -1083,6 +1097,10 @@ fn extract_item_view(item: &PipelineItemCrdt) -> Option<PipelineItemView> {
JsonValue::Number(n) if n > 0.0 => Some(n),
_ => None,
};
let merged_at = match item.merged_at.view() {
JsonValue::Number(n) if n > 0.0 => Some(n),
_ => None,
};
Some(PipelineItemView {
story_id,
@@ -1094,6 +1112,7 @@ fn extract_item_view(item: &PipelineItemCrdt) -> Option<PipelineItemView> {
depends_on,
claimed_by,
claimed_at,
merged_at,
})
}