Skip to content

Commit e04787b

Browse files
committed
feat(examples): add completion config examples for map and parallel operations
Add comprehensive examples and tests demonstrating CompletionConfig usage: Map examples: - minSuccessful: early completion when target successes reached - toleratedFailureCount: absolute failure count tolerance - toleratedFailurePercentage: percentage-based failure tolerance Parallel examples: - minSuccessful: early completion with parallel branches - toleratedFailureCount: failure count tolerance in parallel execution - toleratedFailurePercentage: failure percentage tolerance in parallel execution Each example includes realistic scenarios with controlled success/failure patterns, comprehensive logging, and corresponding unit tests validating expected behavior. Tests verify completion reasons, success/failure counts, and result collection.
1 parent 64878b8 commit e04787b

File tree

12 files changed

+463
-0
lines changed

12 files changed

+463
-0
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { handler } from "./map-min-successful";
2+
import { createTests } from "../../../utils/test-helper";
3+
4+
createTests({
5+
name: "Map minSuccessful",
6+
functionName: "map-min-successful",
7+
handler,
8+
tests: (runner) => {
9+
it("should complete early when minSuccessful is reached", async () => {
10+
const execution = await runner.run();
11+
const result = execution.getResult() as any;
12+
13+
expect(result.successCount).toBe(2);
14+
expect(result.completionReason).toBe("MIN_SUCCESSFUL_REACHED");
15+
expect(result.results).toHaveLength(2);
16+
expect(result.totalCount).toBe(5);
17+
});
18+
},
19+
});
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import {
2+
DurableContext,
3+
withDurableExecution,
4+
} from "@aws/durable-execution-sdk-js";
5+
import { ExampleConfig } from "../../../types";
6+
import { log } from "../../../utils/logger";
7+
8+
export const config: ExampleConfig = {
9+
name: "Map minSuccessful",
10+
description: "Map operation with minSuccessful completion config",
11+
};
12+
13+
export const handler = withDurableExecution(
14+
async (event: any, context: DurableContext) => {
15+
const items = [1, 2, 3, 4, 5];
16+
17+
log(`Processing ${items.length} items with minSuccessful: 2`);
18+
19+
const results = await context.map(
20+
"min-successful-items",
21+
items,
22+
async (ctx, item, index) => {
23+
return await ctx.step(`process-${index}`, async () => {
24+
// Simulate processing time
25+
await new Promise((resolve) => setTimeout(resolve, 100 * item));
26+
return `Item ${item} processed`;
27+
});
28+
},
29+
{
30+
completionConfig: {
31+
minSuccessful: 2,
32+
},
33+
},
34+
);
35+
36+
log(`Completed with ${results.successCount} successes`);
37+
log(`Completion reason: ${results.completionReason}`);
38+
39+
return {
40+
successCount: results.successCount,
41+
totalCount: results.totalCount,
42+
completionReason: results.completionReason,
43+
results: results.getResults(),
44+
};
45+
},
46+
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { handler } from "./map-tolerated-failure-count";
2+
import { createTests } from "../../../utils/test-helper";
3+
4+
createTests({
5+
name: "Map toleratedFailureCount",
6+
functionName: "map-tolerated-failure-count",
7+
handler,
8+
tests: (runner) => {
9+
it("should complete when failure tolerance is reached", async () => {
10+
const execution = await runner.run();
11+
const result = execution.getResult() as any;
12+
13+
expect(result.failureCount).toBe(2);
14+
expect(result.successCount).toBe(3);
15+
expect(result.completionReason).toBe("ALL_COMPLETED");
16+
expect(result.hasFailure).toBe(true);
17+
expect(result.totalCount).toBe(5);
18+
});
19+
},
20+
});
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import {
2+
DurableContext,
3+
withDurableExecution,
4+
} from "@aws/durable-execution-sdk-js";
5+
import { ExampleConfig } from "../../../types";
6+
import { log } from "../../../utils/logger";
7+
8+
export const config: ExampleConfig = {
9+
name: "Map toleratedFailureCount",
10+
description: "Map operation with toleratedFailureCount completion config",
11+
};
12+
13+
export const handler = withDurableExecution(
14+
async (event: any, context: DurableContext) => {
15+
const items = [1, 2, 3, 4, 5];
16+
17+
log(`Processing ${items.length} items with toleratedFailureCount: 2`);
18+
19+
const results = await context.map(
20+
"failure-count-items",
21+
items,
22+
async (ctx, item, index) => {
23+
return await ctx.step(`process-${index}`, async () => {
24+
// Items 2 and 4 will fail
25+
if (item === 2 || item === 4) {
26+
throw new Error(`Processing failed for item ${item}`);
27+
}
28+
return `Item ${item} processed`;
29+
});
30+
},
31+
{
32+
completionConfig: {
33+
toleratedFailureCount: 2,
34+
},
35+
},
36+
);
37+
38+
log(`Completed with ${results.failureCount} failures`);
39+
log(`Completion reason: ${results.completionReason}`);
40+
41+
return {
42+
successCount: results.successCount,
43+
failureCount: results.failureCount,
44+
totalCount: results.totalCount,
45+
completionReason: results.completionReason,
46+
hasFailure: results.hasFailure,
47+
};
48+
},
49+
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { handler } from "./map-tolerated-failure-percentage";
2+
import { createTests } from "../../../utils/test-helper";
3+
4+
createTests({
5+
name: "Map toleratedFailurePercentage",
6+
functionName: "map-tolerated-failure-percentage",
7+
handler,
8+
tests: (runner) => {
9+
it("should complete with acceptable failure percentage", async () => {
10+
const execution = await runner.run();
11+
const result = execution.getResult() as any;
12+
13+
expect(result.failureCount).toBe(3);
14+
expect(result.successCount).toBe(7);
15+
expect(result.failurePercentage).toBe(30);
16+
expect(result.completionReason).toBe("ALL_COMPLETED");
17+
expect(result.totalCount).toBe(10);
18+
});
19+
},
20+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import {
2+
DurableContext,
3+
withDurableExecution,
4+
} from "@aws/durable-execution-sdk-js";
5+
import { ExampleConfig } from "../../../types";
6+
import { log } from "../../../utils/logger";
7+
8+
export const config: ExampleConfig = {
9+
name: "Map toleratedFailurePercentage",
10+
description:
11+
"Map operation with toleratedFailurePercentage completion config",
12+
};
13+
14+
export const handler = withDurableExecution(
15+
async (event: any, context: DurableContext) => {
16+
const items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
17+
18+
log(`Processing ${items.length} items with toleratedFailurePercentage: 30`);
19+
20+
const results = await context.map(
21+
"failure-percentage-items",
22+
items,
23+
async (ctx, item, index) => {
24+
return await ctx.step(`process-${index}`, async () => {
25+
// Items 3, 6, 9 will fail (30% failure rate)
26+
if (item % 3 === 0) {
27+
throw new Error(`Processing failed for item ${item}`);
28+
}
29+
return `Item ${item} processed`;
30+
});
31+
},
32+
{
33+
completionConfig: {
34+
toleratedFailurePercentage: 30,
35+
},
36+
},
37+
);
38+
39+
log(
40+
`Completed with ${results.failureCount} failures (${((results.failureCount / results.totalCount) * 100).toFixed(1)}%)`,
41+
);
42+
log(`Completion reason: ${results.completionReason}`);
43+
44+
return {
45+
successCount: results.successCount,
46+
failureCount: results.failureCount,
47+
totalCount: results.totalCount,
48+
failurePercentage: Math.round(
49+
(results.failureCount / results.totalCount) * 100,
50+
),
51+
completionReason: results.completionReason,
52+
};
53+
},
54+
);
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { handler } from "./parallel-min-successful";
2+
import { createTests } from "../../../utils/test-helper";
3+
4+
createTests({
5+
name: "Parallel minSuccessful",
6+
functionName: "parallel-min-successful",
7+
handler,
8+
tests: (runner) => {
9+
it("should complete early when minSuccessful is reached", async () => {
10+
const execution = await runner.run();
11+
const result = execution.getResult() as any;
12+
13+
expect(result.successCount).toBe(2);
14+
expect(result.completionReason).toBe("MIN_SUCCESSFUL_REACHED");
15+
expect(result.results).toHaveLength(2);
16+
expect(result.totalCount).toBe(4);
17+
});
18+
},
19+
});
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import {
2+
DurableContext,
3+
withDurableExecution,
4+
} from "@aws/durable-execution-sdk-js";
5+
import { ExampleConfig } from "../../../types";
6+
import { log } from "../../../utils/logger";
7+
8+
export const config: ExampleConfig = {
9+
name: "Parallel minSuccessful",
10+
description: "Parallel execution with minSuccessful completion config",
11+
};
12+
13+
export const handler = withDurableExecution(
14+
async (event: any, context: DurableContext) => {
15+
log("Starting parallel execution with minSuccessful: 2");
16+
17+
const results = await context.parallel(
18+
"min-successful-branches",
19+
[
20+
async (ctx) => {
21+
return await ctx.step("branch-1", async () => {
22+
await new Promise((resolve) => setTimeout(resolve, 100));
23+
return "Branch 1 result";
24+
});
25+
},
26+
async (ctx) => {
27+
return await ctx.step("branch-2", async () => {
28+
await new Promise((resolve) => setTimeout(resolve, 200));
29+
return "Branch 2 result";
30+
});
31+
},
32+
async (ctx) => {
33+
return await ctx.step("branch-3", async () => {
34+
await new Promise((resolve) => setTimeout(resolve, 300));
35+
return "Branch 3 result";
36+
});
37+
},
38+
async (ctx) => {
39+
return await ctx.step("branch-4", async () => {
40+
await new Promise((resolve) => setTimeout(resolve, 400));
41+
return "Branch 4 result";
42+
});
43+
},
44+
],
45+
{
46+
completionConfig: {
47+
minSuccessful: 2,
48+
},
49+
},
50+
);
51+
52+
log(`Completed with ${results.successCount} successes`);
53+
log(`Completion reason: ${results.completionReason}`);
54+
55+
return {
56+
successCount: results.successCount,
57+
totalCount: results.totalCount,
58+
completionReason: results.completionReason,
59+
results: results.getResults(),
60+
};
61+
},
62+
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { handler } from "./parallel-tolerated-failure-count";
2+
import { createTests } from "../../../utils/test-helper";
3+
4+
createTests({
5+
name: "Parallel toleratedFailureCount",
6+
functionName: "parallel-tolerated-failure-count",
7+
handler,
8+
tests: (runner) => {
9+
it("should complete when failure tolerance is reached", async () => {
10+
const execution = await runner.run();
11+
const result = execution.getResult() as any;
12+
13+
expect(result.failureCount).toBe(2);
14+
expect(result.successCount).toBe(3);
15+
expect(result.completionReason).toBe("ALL_COMPLETED");
16+
expect(result.hasFailure).toBe(true);
17+
expect(result.totalCount).toBe(5);
18+
});
19+
},
20+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import {
2+
DurableContext,
3+
withDurableExecution,
4+
} from "@aws/durable-execution-sdk-js";
5+
import { ExampleConfig } from "../../../types";
6+
import { log } from "../../../utils/logger";
7+
8+
export const config: ExampleConfig = {
9+
name: "Parallel toleratedFailureCount",
10+
description:
11+
"Parallel execution with toleratedFailureCount completion config",
12+
};
13+
14+
export const handler = withDurableExecution(
15+
async (event: any, context: DurableContext) => {
16+
log("Starting parallel execution with toleratedFailureCount: 2");
17+
18+
const results = await context.parallel(
19+
"failure-count-branches",
20+
[
21+
async (ctx) => {
22+
return await ctx.step("branch-1", async () => {
23+
return "Branch 1 success";
24+
});
25+
},
26+
async (ctx) => {
27+
return await ctx.step("branch-2", async () => {
28+
throw new Error("Branch 2 failed");
29+
});
30+
},
31+
async (ctx) => {
32+
return await ctx.step("branch-3", async () => {
33+
return "Branch 3 success";
34+
});
35+
},
36+
async (ctx) => {
37+
return await ctx.step("branch-4", async () => {
38+
throw new Error("Branch 4 failed");
39+
});
40+
},
41+
async (ctx) => {
42+
return await ctx.step("branch-5", async () => {
43+
return "Branch 5 success";
44+
});
45+
},
46+
],
47+
{
48+
completionConfig: {
49+
toleratedFailureCount: 2,
50+
},
51+
},
52+
);
53+
54+
log(`Completed with ${results.failureCount} failures`);
55+
log(`Completion reason: ${results.completionReason}`);
56+
57+
return {
58+
successCount: results.successCount,
59+
failureCount: results.failureCount,
60+
totalCount: results.totalCount,
61+
completionReason: results.completionReason,
62+
hasFailure: results.hasFailure,
63+
};
64+
},
65+
);

0 commit comments

Comments
 (0)