fix: honor compaction cancellation and replay identity

This commit is contained in:
2026-10-02 05:38:42 +09:00
parent 88338229a1
commit d10783c0d6
4 changed files with 47 additions and 4 deletions
+4
View File
@@ -5291,6 +5291,10 @@ impl<C: LlmClient + 'static, St: Store> Worker<C, St> {
info!(new_segment_id = %new_segment_id, "Manual compaction succeeded"); info!(new_segment_id = %new_segment_id, "Manual compaction succeeded");
Ok(ManualCompactResult::Compacted { new_segment_id }) Ok(ManualCompactResult::Compacted { new_segment_id })
} }
Err(WorkerError::CompactCancelled) => {
info!("Manual compaction was cancelled");
Err(WorkerError::CompactCancelled)
}
Err(e) => { Err(e) => {
warn!(error = %e, "Manual compaction failed"); warn!(error = %e, "Manual compaction failed");
self.alert( self.alert(
+5 -1
View File
@@ -1736,7 +1736,11 @@ async fn manual_compact_cancel_clears_progress_before_returning_idle() {
Event::CompactionProgress { compaction: None } => { Event::CompactionProgress { compaction: None } => {
saw_interrupted = true; saw_interrupted = true;
} }
Event::Error { .. } => saw_cancel_error = true, Event::Error { .. }
| Event::Alert(protocol::Alert {
level: protocol::AlertLevel::Error,
..
}) => saw_cancel_error = true,
Event::WorkerState { snapshot } Event::WorkerState { snapshot }
if snapshot.catalog_status() == protocol::WorkerStatus::Idle => if snapshot.catalog_status() == protocol::WorkerStatus::Idle =>
{ {
@@ -376,6 +376,35 @@ Deno.test("durable compaction failure reconciles live alert and error in both vi
assertEquals(merged.find((line) => line.kind === "error")?.entryId, "failed-entry"); assertEquals(merged.find((line) => line.kind === "error")?.entryId, "failed-entry");
}); });
Deno.test("reapplying one committed run_error entry is idempotent in both views", () => {
const committed = (eventId: string): ConsoleEventInput => ({
eventId,
event: {
event: "session_entry_committed",
data: {
entry: {
entry_id: "same-run-error",
timestamp: 10,
provenance: "legacy_unknown",
kind: "run_error",
message: "mid-run compaction failed: summary unavailable",
failure: "compaction",
},
},
},
});
const projection = projectConsole([
committed("live-delivery"),
committed("snapshot-replay"),
]);
const errors = projection.lines.filter((line) => line.kind === "error");
assertEquals(errors.length, 1);
assertEquals(errors[0].entryId, "same-run-error");
assertEquals(projectConsoleLines(projection.lines, "normal").length, 1);
assertEquals(projectConsoleLines(projection.lines, "overview").length, 1);
});
Deno.test("snapshot restores compaction terminal without reviving transition progress", () => { Deno.test("snapshot restores compaction terminal without reviving transition progress", () => {
const projection = projectConsole([{ const projection = projectConsole([{
eventId: "snapshot", eventId: "snapshot",
@@ -1287,15 +1287,19 @@ function hasDurableRunFailure(
function appendDurableRunFailure( function appendDurableRunFailure(
projection: ConsoleProjection, projection: ConsoleProjection,
eventId: string, eventId: string,
entryId: string | undefined,
message: string, message: string,
failure: string | undefined, failure: string | undefined,
): void { ): void {
if (
entryId && projection.lines.some((line) => line.entryId === entryId)
) return;
projection.lines = projection.lines.filter((line) => { projection.lines = projection.lines.filter((line) => {
if (isDurableRunFailure(line)) return true; if (isDurableRunFailure(line)) return true;
return !(line.kind === "error" && runFailureMessagesMatch(line.body, message)); return !(line.kind === "error" && runFailureMessagesMatch(line.body, message));
}); });
projection.lines.push( projection.lines.push({
line( ...line(
eventId, eventId,
"error", "error",
failure === "compaction" ? "Compaction failed" : "Run error", failure === "compaction" ? "Compaction failed" : "Run error",
@@ -1304,7 +1308,8 @@ function appendDurableRunFailure(
false, false,
true, true,
), ),
); entryId,
});
} }
function appendAlertLine( function appendAlertLine(
@@ -2198,6 +2203,7 @@ function applySessionEntry(
appendDurableRunFailure( appendDurableRunFailure(
projection, projection,
eventId, eventId,
stringField(value, "entry_id"),
stringField(value, "message") ?? "Worker run failed.", stringField(value, "message") ?? "Worker run failed.",
stringField(value, "failure"), stringField(value, "failure"),
); );