Skip to content

Commit 53c3fc4

Browse files
authored
Ignore wf canceled error in partial parallel execution (#50)
* fix: ignore wf canceled error in partial parallel execution * fix: disallow cancel and api in failure function
1 parent c534220 commit 53c3fc4

File tree

3 files changed

+72
-2
lines changed

3 files changed

+72
-2
lines changed

src/context/auto-executor.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { LazyCallStep, type BaseLazyStep } from "./steps";
55
import { getHeaders } from "../workflow-requests";
66
import type { WorkflowLogger } from "../logger";
77
import { NO_CONCURRENCY } from "../constants";
8+
import { QstashError } from "@upstash/qstash";
89

910
export class AutoExecutor {
1011
private context: WorkflowContext;
@@ -227,7 +228,10 @@ export class AutoExecutor {
227228
);
228229
await this.submitStepsToQStash([resultStep], [parallelStep]);
229230
} catch (error) {
230-
if (error instanceof WorkflowAbort) {
231+
if (
232+
error instanceof WorkflowAbort ||
233+
(error instanceof QstashError && error.status === 400)
234+
) {
231235
throw error;
232236
}
233237
throw new WorkflowError(

src/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ export type WorkflowServeOptions<
188188
failureFunction?: (failureData: {
189189
context: Omit<
190190
WorkflowContext<TInitialPayload>,
191-
"run" | "sleepUntil" | "sleep" | "call" | "waitForEvent" | "notify"
191+
"run" | "sleepUntil" | "sleep" | "call" | "waitForEvent" | "notify" | "cancel" | "api"
192192
>;
193193
failStatus: number;
194194
failResponse: string;

src/workflow-requests.test.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -749,6 +749,72 @@ describe("Workflow Requests", () => {
749749
}
750750
);
751751

752+
test(
753+
"should omit if triggerRouteFunction (with partial parallel step execution) gets can't publish to canceled workflow error",
754+
async () => {
755+
const workflowRunId = `wfr-${nanoid()}`;
756+
const context = new WorkflowContext({
757+
qstashClient,
758+
workflowRunId: workflowRunId,
759+
initialPayload: undefined,
760+
headers: new Headers({}) as Headers,
761+
steps: [
762+
{
763+
stepId: 0,
764+
concurrent: 1,
765+
stepName: "init",
766+
stepType: "Initial",
767+
targetStep: 1,
768+
},
769+
{
770+
stepId: 0,
771+
concurrent: 2,
772+
stepName: "sleeping",
773+
stepType: "SleepFor",
774+
targetStep: 1,
775+
},
776+
],
777+
url: WORKFLOW_ENDPOINT,
778+
});
779+
780+
const debug = new WorkflowLogger({ logLevel: "INFO", logOutput: "console" });
781+
const spy = spyOn(debug, "log");
782+
783+
await triggerFirstInvocation(context, 3, false, debug);
784+
expect(spy).toHaveBeenCalledTimes(1);
785+
786+
await workflowClient.cancel({ ids: [workflowRunId] });
787+
788+
const result = await triggerRouteFunction({
789+
onStep: async () => {
790+
await Promise.all([context.sleep("sleeping", 10), context.sleep("sleeping", 10)]);
791+
},
792+
onCleanup: async () => {
793+
throw new Error("shouldn't come here.");
794+
},
795+
onCancel: async () => {
796+
throw new Error("shouldn't come here.");
797+
},
798+
debug,
799+
});
800+
801+
expect(result.isOk()).toBeTrue();
802+
// @ts-expect-error value will be set since stepFinish isOk
803+
expect(result.value).toBe("workflow-was-finished");
804+
805+
expect(spy).toHaveBeenCalledTimes(2);
806+
expect(spy).toHaveBeenLastCalledWith("WARN", "RESPONSE_WORKFLOW", {
807+
message: "tried to append to a cancelled workflow. exiting without publishing.",
808+
name: "QstashError",
809+
errorMessage:
810+
'[{"error":"failed to publish to url: can not append to a a cancelled workflow"}]',
811+
});
812+
},
813+
{
814+
timeout: 10000,
815+
}
816+
);
817+
752818
test(
753819
"should omit the error if the workflow is created with the same id",
754820
async () => {

0 commit comments

Comments
 (0)