fix: honor compaction cancellation and replay identity
This commit is contained in:
@@ -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(
|
||||||
|
|||||||
@@ -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"),
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user