feat(planner): implement cancellation for planning calculations
- Refactor leave-planner.worker.ts to support cancellation of ongoing calculations. - Introduce `calculatePlanningAsyncInternal` for asynchronous planning calculations. - Add cancellation handling in the worker message event. - Update planning-date.ts with new utility functions for date manipulation. - Extend translation-service.ts and translation files (en.json, de.json, es.json, fr.json) to include new strings related to planning horizon and cancellation. - Create documentation for the cancellation feature in planner-cancellation.md. - Add unit tests for cancellation functionality in leave-planner-cancellation.spec.ts and leave-planner.worker.spec.ts. #57 #58
This commit is contained in:
1 parent
5c35a59805
commit
2edf2004f5
31 files changed
+2275
-304
No files matched your search
@@ -15,7 +15,7 @@ import type {
|
||||
} from './leave-planner.service';
|
||||
import {
|
||||
analyzeCounterfactualInternal,
|
||||
calculatePlanningInternal,
|
||||
calculatePlanningAsyncInternal,
|
||||
explainIncompleteCheckpoint,
|
||||
historicalCounterfactualReasons,
|
||||
type CounterfactualBaselineOptions,
|
||||
@@ -38,6 +38,7 @@ type WorkerRequest =
|
||||
request: WorkerPlanningRequest;
|
||||
}
|
||||
| { type: 'trace-ack'; count: number }
|
||||
| { type: 'cancel' }
|
||||
| {
|
||||
type: 'counterfactual';
|
||||
request: PlanningSnapshot;
|
||||
@@ -120,6 +121,7 @@ interface TraceDelivery {
|
||||
}
|
||||
|
||||
let activeTraceDelivery: TraceDelivery | undefined;
|
||||
let activeCalculation: { cancelled: boolean } | undefined;
|
||||
|
||||
function traceStatusIndex(trace: PlanningTrace): {
|
||||
statuses: Map<string, TraceCandidateStatus>;
|
||||
@@ -272,8 +274,12 @@ function pumpTraceUpdates(delivery: TraceDelivery): void {
|
||||
finishTraceDelivery(delivery);
|
||||
}
|
||||
|
||||
worker.onmessage = (event: MessageEvent<WorkerRequest>) => {
|
||||
worker.onmessage = async (event: MessageEvent<WorkerRequest>) => {
|
||||
try {
|
||||
if (event.data?.type === 'cancel') {
|
||||
if (activeCalculation) activeCalculation.cancelled = true;
|
||||
return;
|
||||
}
|
||||
if (event.data?.type === 'trace-ack') {
|
||||
const delivery = activeTraceDelivery;
|
||||
if (!delivery || !Number.isInteger(event.data.count) || event.data.count <= 0) return;
|
||||
@@ -282,6 +288,8 @@ worker.onmessage = (event: MessageEvent<WorkerRequest>) => {
|
||||
return;
|
||||
}
|
||||
if (event.data?.type === 'plan') {
|
||||
const calculation = { cancelled: false };
|
||||
activeCalculation = calculation;
|
||||
let artifacts: PlanningArtifacts = {};
|
||||
let outcomePosted = false;
|
||||
let outcome: PlanningOutcome;
|
||||
@@ -294,7 +302,7 @@ worker.onmessage = (event: MessageEvent<WorkerRequest>) => {
|
||||
};
|
||||
activeTraceDelivery = traceDelivery;
|
||||
try {
|
||||
outcome = calculatePlanningInternal(
|
||||
outcome = await calculatePlanningAsyncInternal(
|
||||
{
|
||||
...event.data.request,
|
||||
progress: (stage: string) =>
|
||||
@@ -331,6 +339,7 @@ worker.onmessage = (event: MessageEvent<WorkerRequest>) => {
|
||||
explanationError,
|
||||
finalState,
|
||||
} satisfies WorkerResponse),
|
||||
() => calculation.cancelled,
|
||||
);
|
||||
} catch (error) {
|
||||
if (activeTraceDelivery === traceDelivery) activeTraceDelivery = undefined;
|
||||
@@ -341,6 +350,8 @@ worker.onmessage = (event: MessageEvent<WorkerRequest>) => {
|
||||
message: error instanceof Error ? error.message : String(error),
|
||||
} satisfies WorkerResponse);
|
||||
return;
|
||||
} finally {
|
||||
if (activeCalculation === calculation) activeCalculation = undefined;
|
||||
}
|
||||
if (!outcomePosted)
|
||||
worker.postMessage({ type: 'result', outcome } satisfies WorkerResponse);
|
||||
|
||||
Reference in new issue
Block a user