v3: fix dependency trigger and wait (#1030)
* fix dev dependecy resumes * add catalog entry with multiple trigger waits * update docker provider lifecycle command * switch to x for clearing run filters * Revert "fix dev dependecy resumes" This reverts commit b4061f2ae71f21889adcf061928e2e77c436f0ed. * fix dependency resumes, properly this time * add catalog entry for dependency waits in loops * advice in docs re parallel dependency waits * fix link from v3 to v2 docs * move lifecycle command logging to debug only * Removed batchOptions from the trigger options and the docs --------- Co-authored-by: Matt Aitken <matt@mattaitken.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@trigger.dev/sdk": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Remove unimplemented batchOptions
|
||||||
@@ -257,6 +257,7 @@ class DockerTaskOperations implements TaskOperations {
|
|||||||
return await execa("docker", [
|
return await execa("docker", [
|
||||||
"exec",
|
"exec",
|
||||||
containerName,
|
containerName,
|
||||||
|
"busybox",
|
||||||
"wget",
|
"wget",
|
||||||
"-q",
|
"-q",
|
||||||
"-O-",
|
"-O-",
|
||||||
|
|||||||
@@ -409,7 +409,7 @@ class KubernetesTaskOperations implements TaskOperations {
|
|||||||
`for i in $(seq ${retries}); do sleep 1; busybox wget -q -O- 127.0.0.1:8000/${type}?cause=${cause} && break; done`,
|
`for i in $(seq ${retries}); do sleep 1; busybox wget -q -O- 127.0.0.1:8000/${type}?cause=${cause} && break; done`,
|
||||||
];
|
];
|
||||||
|
|
||||||
logger.log("getLifecycleCommand()", { exec });
|
logger.debug("getLifecycleCommand()", { exec });
|
||||||
|
|
||||||
return exec;
|
return exec;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { TrashIcon } from "@heroicons/react/20/solid";
|
import { XMarkIcon } from "@heroicons/react/20/solid";
|
||||||
import { useNavigate } from "@remix-run/react";
|
import { useNavigate } from "@remix-run/react";
|
||||||
import type { TaskRunStatus as TaskRunStatusType } from "@trigger.dev/database";
|
import type { TaskRunStatus as TaskRunStatusType } from "@trigger.dev/database";
|
||||||
import { RuntimeEnvironment, TaskRunAttemptStatus, TaskRunStatus } from "@trigger.dev/database";
|
import { RuntimeEnvironment, TaskRunAttemptStatus, TaskRunStatus } from "@trigger.dev/database";
|
||||||
@@ -247,7 +247,7 @@ export function RunsFilters({ possibleEnvironments, possibleTasks }: RunFiltersP
|
|||||||
|
|
||||||
<TimeFrameFilter from={from} to={to} onRangeChanged={handleTimeFrameChange} />
|
<TimeFrameFilter from={from} to={to} onRangeChanged={handleTimeFrameChange} />
|
||||||
|
|
||||||
<Button variant="minimal/small" onClick={() => clearFilters()} LeadingIcon={TrashIcon} />
|
<Button variant="minimal/small" onClick={() => clearFilters()} LeadingIcon={XMarkIcon} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,12 +4,13 @@ import {
|
|||||||
TaskRunExecution,
|
TaskRunExecution,
|
||||||
TaskRunExecutionResult,
|
TaskRunExecutionResult,
|
||||||
} from "@trigger.dev/core/v3";
|
} from "@trigger.dev/core/v3";
|
||||||
import { $transaction } from "~/db.server";
|
import { $transaction, PrismaClientOrTransaction } from "~/db.server";
|
||||||
import { logger } from "~/services/logger.server";
|
import { logger } from "~/services/logger.server";
|
||||||
import { marqs } from "~/v3/marqs/index.server";
|
import { marqs } from "~/v3/marqs/index.server";
|
||||||
import { socketIo } from "../handleSocketIo.server";
|
import { socketIo } from "../handleSocketIo.server";
|
||||||
import { sharedQueueTasks } from "../marqs/sharedQueueConsumer.server";
|
import { sharedQueueTasks } from "../marqs/sharedQueueConsumer.server";
|
||||||
import { BaseService } from "./baseService.server";
|
import { BaseService } from "./baseService.server";
|
||||||
|
import { TaskRunAttempt } from "@trigger.dev/database";
|
||||||
|
|
||||||
export class ResumeAttemptService extends BaseService {
|
export class ResumeAttemptService extends BaseService {
|
||||||
public async call(
|
public async call(
|
||||||
@@ -24,7 +25,7 @@ export class ResumeAttemptService extends BaseService {
|
|||||||
},
|
},
|
||||||
include: {
|
include: {
|
||||||
taskRun: true,
|
taskRun: true,
|
||||||
taskRunDependency: {
|
dependencies: {
|
||||||
include: {
|
include: {
|
||||||
taskRun: {
|
taskRun: {
|
||||||
include: {
|
include: {
|
||||||
@@ -40,8 +41,12 @@ export class ResumeAttemptService extends BaseService {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
orderBy: {
|
||||||
|
createdAt: "desc",
|
||||||
|
},
|
||||||
|
take: 1,
|
||||||
},
|
},
|
||||||
batchTaskRunDependency: {
|
batchDependencies: {
|
||||||
include: {
|
include: {
|
||||||
items: {
|
items: {
|
||||||
include: {
|
include: {
|
||||||
@@ -61,6 +66,10 @@ export class ResumeAttemptService extends BaseService {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
orderBy: {
|
||||||
|
createdAt: "desc",
|
||||||
|
},
|
||||||
|
take: 1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -78,6 +87,8 @@ export class ResumeAttemptService extends BaseService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let completedAttemptIds: string[] = [];
|
||||||
|
|
||||||
switch (params.type) {
|
switch (params.type) {
|
||||||
case "WAIT_FOR_DURATION": {
|
case "WAIT_FOR_DURATION": {
|
||||||
logger.error(
|
logger.error(
|
||||||
@@ -93,12 +104,10 @@ export class ResumeAttemptService extends BaseService {
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "WAIT_FOR_TASK":
|
case "WAIT_FOR_TASK": {
|
||||||
case "WAIT_FOR_BATCH": {
|
if (attempt.dependencies.length) {
|
||||||
let completedAttemptIds: string[] = [];
|
// We only care about the latest dependency
|
||||||
|
const dependentAttempt = attempt.dependencies[0].taskRun.attempts[0];
|
||||||
if (attempt.taskRunDependency) {
|
|
||||||
const dependentAttempt = attempt.taskRunDependency.taskRun.attempts[0];
|
|
||||||
|
|
||||||
if (!dependentAttempt) {
|
if (!dependentAttempt) {
|
||||||
logger.error("No dependent attempt", { attemptId: attempt.id });
|
logger.error("No dependent attempt", { attemptId: attempt.id });
|
||||||
@@ -106,19 +115,16 @@ export class ResumeAttemptService extends BaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
completedAttemptIds = [dependentAttempt.id];
|
completedAttemptIds = [dependentAttempt.id];
|
||||||
|
} else {
|
||||||
await tx.taskRunAttempt.update({
|
logger.error("No task dependency", { attemptId: attempt.id });
|
||||||
where: {
|
return;
|
||||||
id: attempt.id,
|
}
|
||||||
},
|
break;
|
||||||
data: {
|
}
|
||||||
taskRunDependency: {
|
case "WAIT_FOR_BATCH": {
|
||||||
disconnect: true,
|
if (attempt.batchDependencies) {
|
||||||
},
|
// We only care about the latest batch dependency
|
||||||
},
|
const dependentBatchItems = attempt.batchDependencies[0].items;
|
||||||
});
|
|
||||||
} else if (attempt.batchTaskRunDependency) {
|
|
||||||
const dependentBatchItems = attempt.batchTaskRunDependency.items;
|
|
||||||
|
|
||||||
if (!dependentBatchItems) {
|
if (!dependentBatchItems) {
|
||||||
logger.error("No dependent batch items", { attemptId: attempt.id });
|
logger.error("No dependent batch items", { attemptId: attempt.id });
|
||||||
@@ -126,115 +132,112 @@ export class ResumeAttemptService extends BaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
completedAttemptIds = dependentBatchItems.map((item) => item.taskRun.attempts[0]?.id);
|
completedAttemptIds = dependentBatchItems.map((item) => item.taskRun.attempts[0]?.id);
|
||||||
|
|
||||||
await tx.taskRunAttempt.update({
|
|
||||||
where: {
|
|
||||||
id: attempt.id,
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
batchTaskRunDependency: {
|
|
||||||
disconnect: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
logger.error("No dependencies", { attemptId: attempt.id });
|
logger.error("No batch dependency", { attemptId: attempt.id });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (completedAttemptIds.length === 0) {
|
|
||||||
logger.error("No completed attempt IDs", { attemptId: attempt.id });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const completions: TaskRunExecutionResult[] = [];
|
|
||||||
const executions: TaskRunExecution[] = [];
|
|
||||||
|
|
||||||
for (const completedAttemptId of completedAttemptIds) {
|
|
||||||
const completedAttempt = await tx.taskRunAttempt.findUnique({
|
|
||||||
where: {
|
|
||||||
id: completedAttemptId,
|
|
||||||
taskRun: {
|
|
||||||
lockedAt: {
|
|
||||||
not: null,
|
|
||||||
},
|
|
||||||
lockedById: {
|
|
||||||
not: null,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!completedAttempt) {
|
|
||||||
logger.error("Completed attempt not found", {
|
|
||||||
attemptId: attempt.id,
|
|
||||||
completedAttemptId,
|
|
||||||
});
|
|
||||||
await marqs?.acknowledgeMessage(attempt.taskRunId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const completion = await sharedQueueTasks.getCompletionPayloadFromAttempt(
|
|
||||||
completedAttempt.id
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!completion) {
|
|
||||||
logger.error("Failed to get completion payload", {
|
|
||||||
attemptId: attempt.id,
|
|
||||||
completedAttemptId,
|
|
||||||
});
|
|
||||||
await marqs?.acknowledgeMessage(attempt.taskRunId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
completions.push(completion);
|
|
||||||
|
|
||||||
const executionPayload = await sharedQueueTasks.getExecutionPayloadFromAttempt(
|
|
||||||
completedAttempt.id
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!executionPayload) {
|
|
||||||
logger.error("Failed to get execution payload", {
|
|
||||||
attemptId: attempt.id,
|
|
||||||
completedAttemptId,
|
|
||||||
});
|
|
||||||
await marqs?.acknowledgeMessage(attempt.taskRunId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
executions.push(executionPayload.execution);
|
|
||||||
}
|
|
||||||
|
|
||||||
const updated = await tx.taskRunAttempt.update({
|
|
||||||
where: {
|
|
||||||
id: attempt.id,
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
status: "EXECUTING",
|
|
||||||
taskRun: {
|
|
||||||
update: {
|
|
||||||
data: {
|
|
||||||
status: attempt.number > 1 ? "RETRYING_AFTER_FAILURE" : "EXECUTING",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
socketIo.coordinatorNamespace.emit("RESUME_AFTER_DEPENDENCY", {
|
|
||||||
version: "v1",
|
|
||||||
runId: attempt.taskRunId,
|
|
||||||
attemptId: attempt.id,
|
|
||||||
attemptFriendlyId: attempt.friendlyId,
|
|
||||||
completions,
|
|
||||||
executions,
|
|
||||||
});
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await this.#handleDependencyResume(attempt, completedAttemptIds, tx);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async #handleDependencyResume(
|
||||||
|
attempt: TaskRunAttempt,
|
||||||
|
completedAttemptIds: string[],
|
||||||
|
tx: PrismaClientOrTransaction
|
||||||
|
) {
|
||||||
|
if (completedAttemptIds.length === 0) {
|
||||||
|
logger.error("No completed attempt IDs", { attemptId: attempt.id });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const completions: TaskRunExecutionResult[] = [];
|
||||||
|
const executions: TaskRunExecution[] = [];
|
||||||
|
|
||||||
|
for (const completedAttemptId of completedAttemptIds) {
|
||||||
|
const completedAttempt = await tx.taskRunAttempt.findUnique({
|
||||||
|
where: {
|
||||||
|
id: completedAttemptId,
|
||||||
|
taskRun: {
|
||||||
|
lockedAt: {
|
||||||
|
not: null,
|
||||||
|
},
|
||||||
|
lockedById: {
|
||||||
|
not: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!completedAttempt) {
|
||||||
|
logger.error("Completed attempt not found", {
|
||||||
|
attemptId: attempt.id,
|
||||||
|
completedAttemptId,
|
||||||
|
});
|
||||||
|
await marqs?.acknowledgeMessage(attempt.taskRunId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const completion = await sharedQueueTasks.getCompletionPayloadFromAttempt(
|
||||||
|
completedAttempt.id
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!completion) {
|
||||||
|
logger.error("Failed to get completion payload", {
|
||||||
|
attemptId: attempt.id,
|
||||||
|
completedAttemptId,
|
||||||
|
});
|
||||||
|
await marqs?.acknowledgeMessage(attempt.taskRunId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
completions.push(completion);
|
||||||
|
|
||||||
|
const executionPayload = await sharedQueueTasks.getExecutionPayloadFromAttempt(
|
||||||
|
completedAttempt.id
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!executionPayload) {
|
||||||
|
logger.error("Failed to get execution payload", {
|
||||||
|
attemptId: attempt.id,
|
||||||
|
completedAttemptId,
|
||||||
|
});
|
||||||
|
await marqs?.acknowledgeMessage(attempt.taskRunId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
executions.push(executionPayload.execution);
|
||||||
|
}
|
||||||
|
|
||||||
|
const updated = await tx.taskRunAttempt.update({
|
||||||
|
where: {
|
||||||
|
id: attempt.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
status: "EXECUTING",
|
||||||
|
taskRun: {
|
||||||
|
update: {
|
||||||
|
data: {
|
||||||
|
status: attempt.number > 1 ? "RETRYING_AFTER_FAILURE" : "EXECUTING",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
socketIo.coordinatorNamespace.emit("RESUME_AFTER_DEPENDENCY", {
|
||||||
|
version: "v1",
|
||||||
|
runId: attempt.taskRunId,
|
||||||
|
attemptId: attempt.id,
|
||||||
|
attemptFriendlyId: attempt.friendlyId,
|
||||||
|
completions,
|
||||||
|
executions,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -49,7 +49,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "v2",
|
"name": "v2",
|
||||||
"url": "https://trigger.dev/docs",
|
"url": "https://trigger.dev/docs/documentation",
|
||||||
"version": "v3 (Developer Preview)"
|
"version": "v3 (Developer Preview)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -165,6 +165,45 @@ export const myTask = task({
|
|||||||
|
|
||||||
This is where it gets interesting. You can trigger a task and then wait for the result. This is useful when you need to call a different task and then use the result to continue with your task.
|
This is where it gets interesting. You can trigger a task and then wait for the result. This is useful when you need to call a different task and then use the result to continue with your task.
|
||||||
|
|
||||||
|
<Accordion title="Don't use this in parallel, e.g. with `Promise.all()`">
|
||||||
|
Instead, use `batchTriggerAndWait()` if you can, or a for loop if you can't.
|
||||||
|
|
||||||
|
To control concurrency using batch triggers, you can set `queue.concurrencyLimit` on the child task.
|
||||||
|
|
||||||
|
<CodeGroup>
|
||||||
|
```ts /trigger/batch.ts
|
||||||
|
export const batchTask = task({
|
||||||
|
id: "batch-task",
|
||||||
|
run: async (payload: string) => {
|
||||||
|
const results = await childTask.batchTriggerAndWait({
|
||||||
|
items: [{ payload: "item1" }, { payload: "item2" }],
|
||||||
|
});
|
||||||
|
console.log("Results", results);
|
||||||
|
|
||||||
|
//...do stuff with the results
|
||||||
|
},
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
```ts /trigger/loop.ts
|
||||||
|
export const loopTask = task({
|
||||||
|
id: "loop-task",
|
||||||
|
run: async (payload: string) => {
|
||||||
|
//this will be slower than the batch version
|
||||||
|
//as we have to resume the parent after each iteration
|
||||||
|
for (let i = 0; i < 2; i++) {
|
||||||
|
const result = await childTask.triggerAndWait({ payload: `item${i}` });
|
||||||
|
console.log("Result", result);
|
||||||
|
|
||||||
|
//...do stuff with the result
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
```
|
||||||
|
</CodeGroup>
|
||||||
|
|
||||||
|
</Accordion>
|
||||||
|
|
||||||
```ts /trigger/parent.ts
|
```ts /trigger/parent.ts
|
||||||
export const parentTask = task({
|
export const parentTask = task({
|
||||||
id: "parent-task",
|
id: "parent-task",
|
||||||
@@ -181,6 +220,47 @@ export const parentTask = task({
|
|||||||
|
|
||||||
You can batch trigger a task and wait for all the results. This is useful for the fan-out pattern, where you need to call a task multiple times and then wait for all the results to continue with your task.
|
You can batch trigger a task and wait for all the results. This is useful for the fan-out pattern, where you need to call a task multiple times and then wait for all the results to continue with your task.
|
||||||
|
|
||||||
|
<Accordion title="Don't use this in parallel, e.g. with `Promise.all()`">
|
||||||
|
Instead, pass in all items at once and set an appropriate `maxConcurrency`. Alternatively, use sequentially with a for loop.
|
||||||
|
|
||||||
|
To control concurrency, you can set `queue.concurrencyLimit` on the child task.
|
||||||
|
|
||||||
|
<CodeGroup>
|
||||||
|
```ts /trigger/batch.ts
|
||||||
|
export const batchTask = task({
|
||||||
|
id: "batch-task",
|
||||||
|
run: async (payload: string) => {
|
||||||
|
const results = await childTask.batchTriggerAndWait({
|
||||||
|
items: [{ payload: "item1" }, { payload: "item2" }],
|
||||||
|
});
|
||||||
|
console.log("Results", results);
|
||||||
|
|
||||||
|
//...do stuff with the results
|
||||||
|
},
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
```ts /trigger/loop.ts
|
||||||
|
export const loopTask = task({
|
||||||
|
id: "loop-task",
|
||||||
|
run: async (payload: string) => {
|
||||||
|
//this will be slower than a single batchTriggerAndWait()
|
||||||
|
//as we have to resume the parent after each iteration
|
||||||
|
for (let i = 0; i < 2; i++) {
|
||||||
|
const result = await childTask.batchTriggerAndWait({
|
||||||
|
items: [{ payload: `itemA${i}` }, { payload: `itemB${i}` }],
|
||||||
|
});
|
||||||
|
console.log("Result", result);
|
||||||
|
|
||||||
|
//...do stuff with the result
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
```
|
||||||
|
</CodeGroup>
|
||||||
|
|
||||||
|
</Accordion>
|
||||||
|
|
||||||
```ts /trigger/nested.ts
|
```ts /trigger/nested.ts
|
||||||
export const batchParentTask = task({
|
export const batchParentTask = task({
|
||||||
id: "parent-task",
|
id: "parent-task",
|
||||||
|
|||||||
+2
@@ -0,0 +1,2 @@
|
|||||||
|
-- DropIndex
|
||||||
|
DROP INDEX "TaskRunDependency_dependentAttemptId_key";
|
||||||
+2
@@ -0,0 +1,2 @@
|
|||||||
|
-- DropIndex
|
||||||
|
DROP INDEX "BatchTaskRun_dependentTaskAttemptId_key";
|
||||||
@@ -1686,8 +1686,8 @@ model TaskRunDependency {
|
|||||||
checkpointEventId String? @unique
|
checkpointEventId String? @unique
|
||||||
|
|
||||||
/// An attempt that is dependent on this task run.
|
/// An attempt that is dependent on this task run.
|
||||||
dependentAttempt TaskRunAttempt? @relation("dependentAttempt", fields: [dependentAttemptId], references: [id])
|
dependentAttempt TaskRunAttempt? @relation(fields: [dependentAttemptId], references: [id])
|
||||||
dependentAttemptId String? @unique
|
dependentAttemptId String?
|
||||||
|
|
||||||
/// A batch run that is dependent on this task run
|
/// A batch run that is dependent on this task run
|
||||||
dependentBatchRun BatchTaskRun? @relation("dependentBatchRun", fields: [dependentBatchRunId], references: [id])
|
dependentBatchRun BatchTaskRun? @relation("dependentBatchRun", fields: [dependentBatchRunId], references: [id])
|
||||||
@@ -1751,8 +1751,8 @@ model TaskRunAttempt {
|
|||||||
output String?
|
output String?
|
||||||
outputType String @default("application/json")
|
outputType String @default("application/json")
|
||||||
|
|
||||||
taskRunDependency TaskRunDependency? @relation("dependentAttempt")
|
dependencies TaskRunDependency[]
|
||||||
batchTaskRunDependency BatchTaskRun?
|
batchDependencies BatchTaskRun[]
|
||||||
|
|
||||||
checkpoints Checkpoint[]
|
checkpoints Checkpoint[]
|
||||||
batchTaskRunItems BatchTaskRunItem[]
|
batchTaskRunItems BatchTaskRunItem[]
|
||||||
@@ -1923,7 +1923,7 @@ model BatchTaskRun {
|
|||||||
runtimeEnvironmentId String
|
runtimeEnvironmentId String
|
||||||
|
|
||||||
dependentTaskAttempt TaskRunAttempt? @relation(fields: [dependentTaskAttemptId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
dependentTaskAttempt TaskRunAttempt? @relation(fields: [dependentTaskAttemptId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||||||
dependentTaskAttemptId String? @unique
|
dependentTaskAttemptId String?
|
||||||
|
|
||||||
items BatchTaskRunItem[]
|
items BatchTaskRunItem[]
|
||||||
runDependencies TaskRunDependency[] @relation("dependentBatchRun")
|
runDependencies TaskRunDependency[] @relation("dependentBatchRun")
|
||||||
|
|||||||
@@ -179,12 +179,12 @@ export interface Task<TInput, TOutput = any> {
|
|||||||
trigger: (params: { payload: TInput; options?: TaskRunOptions }) => Promise<InvokeHandle>;
|
trigger: (params: { payload: TInput; options?: TaskRunOptions }) => Promise<InvokeHandle>;
|
||||||
batchTrigger: (params: {
|
batchTrigger: (params: {
|
||||||
items: { payload: TInput; options?: TaskRunOptions }[];
|
items: { payload: TInput; options?: TaskRunOptions }[];
|
||||||
batchOptions?: BatchRunOptions;
|
// batchOptions?: BatchRunOptions;
|
||||||
}) => Promise<InvokeBatchHandle>;
|
}) => Promise<InvokeBatchHandle>;
|
||||||
triggerAndWait: (params: { payload: TInput; options?: TaskRunOptions }) => Promise<TOutput>;
|
triggerAndWait: (params: { payload: TInput; options?: TaskRunOptions }) => Promise<TOutput>;
|
||||||
batchTriggerAndWait: (params: {
|
batchTriggerAndWait: (params: {
|
||||||
items: { payload: TInput; options?: TaskRunOptions }[];
|
items: { payload: TInput; options?: TaskRunOptions }[];
|
||||||
batchOptions?: BatchRunOptions;
|
// batchOptions?: BatchRunOptions;
|
||||||
}) => Promise<BatchResult<TOutput>>;
|
}) => Promise<BatchResult<TOutput>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -114,3 +114,54 @@ export const subtasksWithRetries = task({
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const multipleTriggerWaits = task({
|
||||||
|
id: "multiple-trigger-waits",
|
||||||
|
run: async ({ message = "test" }: { message?: string }) => {
|
||||||
|
await simpleChildTask.triggerAndWait({ payload: { message: `${message} - 1.a` } });
|
||||||
|
await simpleChildTask.triggerAndWait({ payload: { message: `${message} - 2.a` } });
|
||||||
|
|
||||||
|
await simpleChildTask.batchTriggerAndWait({
|
||||||
|
items: [
|
||||||
|
{ payload: { message: `${message} - 3.a` } },
|
||||||
|
{ payload: { message: `${message} - 3.b` } },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
await simpleChildTask.batchTriggerAndWait({
|
||||||
|
items: [
|
||||||
|
{ payload: { message: `${message} - 4.a` } },
|
||||||
|
{ payload: { message: `${message} - 4.b` } },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
hello: "world",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const triggerAndWaitLoops = task({
|
||||||
|
id: "trigger-wait-loops",
|
||||||
|
run: async ({ message = "test" }: { message?: string }) => {
|
||||||
|
for (let i = 0; i < 2; i++) {
|
||||||
|
await simpleChildTask.triggerAndWait({ payload: { message: `${message} - ${i}` } });
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < 2; i++) {
|
||||||
|
await simpleChildTask.batchTriggerAndWait({
|
||||||
|
items: [
|
||||||
|
{ payload: { message: `${message} - ${i}.a` } },
|
||||||
|
{ payload: { message: `${message} - ${i}.b` } },
|
||||||
|
],
|
||||||
|
// batchOptions: { maxConcurrency: 1 },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't do this!
|
||||||
|
// await Promise.all(
|
||||||
|
// [{ message: `${message} - 1` }, { message: `${message} - 2` }].map((payload) =>
|
||||||
|
// simpleChildTask.triggerAndWait({ payload })
|
||||||
|
// )
|
||||||
|
// );
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user