4f95c9de4e
* v3: cancel subtasks when parent task runs are cancelled * v3: recover from server rate limiting errors in a more reliable way - Changing from sliding window to token bucket in the API rate limiter, to help smooth out traffic - Adding spans to the API Client core & SDK functions - Added waiting spans when retrying in the API Client - Retrying in the API Client now respects the x-ratelimit-reset - Retrying ApiError’s in tasks now respects the x-ratelimit-reset - Added AbortTaskRunError that when thrown will stop retries - Added idempotency keys SDK functions and automatically injecting the run ID when inside a task - Added the ability to configure ApiRequestOptions (retries only for now) globally and on specific calls - Implement the maxAttempts TaskRunOption (it wasn’t doing anything before) * Adding some docs about the request options * Fix type error * Remove context propagation through graphile jobs * Remove logger * only select a subset of task run columns * limit columns selected in batchTrigger as well * added idempotency doc * allow scoped idempotency keys, and fixed an issue with the unique index on BatchTaskRun and TaskRun * Removed old cancel task run children code
153 lines
4.7 KiB
TypeScript
153 lines
4.7 KiB
TypeScript
import { BatchTriggerTaskRequestBody, logger } from "@trigger.dev/core/v3";
|
|
import { AuthenticatedEnvironment } from "~/services/apiAuth.server";
|
|
import { generateFriendlyId } from "../friendlyIdentifiers";
|
|
import { BaseService, ServiceValidationError } from "./baseService.server";
|
|
import { TriggerTaskService } from "./triggerTask.server";
|
|
import { batchTaskRunItemStatusForRunStatus } from "~/models/taskRun.server";
|
|
import { isFinalAttemptStatus, isFinalRunStatus } from "../taskStatus";
|
|
|
|
export type BatchTriggerTaskServiceOptions = {
|
|
idempotencyKey?: string;
|
|
triggerVersion?: string;
|
|
traceContext?: Record<string, string | undefined>;
|
|
spanParentAsLink?: boolean;
|
|
};
|
|
|
|
export class BatchTriggerTaskService extends BaseService {
|
|
public async call(
|
|
taskId: string,
|
|
environment: AuthenticatedEnvironment,
|
|
body: BatchTriggerTaskRequestBody,
|
|
options: BatchTriggerTaskServiceOptions = {}
|
|
) {
|
|
return await this.traceWithEnv("call()", environment, async (span) => {
|
|
span.setAttribute("taskId", taskId);
|
|
|
|
const existingBatch = options.idempotencyKey
|
|
? await this._prisma.batchTaskRun.findUnique({
|
|
where: {
|
|
runtimeEnvironmentId_taskIdentifier_idempotencyKey: {
|
|
runtimeEnvironmentId: environment.id,
|
|
idempotencyKey: options.idempotencyKey,
|
|
taskIdentifier: taskId,
|
|
},
|
|
},
|
|
include: {
|
|
items: {
|
|
include: {
|
|
taskRun: {
|
|
select: {
|
|
friendlyId: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
})
|
|
: undefined;
|
|
|
|
if (existingBatch) {
|
|
span.setAttribute("batchId", existingBatch.friendlyId);
|
|
return {
|
|
batch: existingBatch,
|
|
runs: existingBatch.items.map((item) => item.taskRun.friendlyId),
|
|
};
|
|
}
|
|
|
|
const dependentAttempt = body?.dependentAttempt
|
|
? await this._prisma.taskRunAttempt.findUnique({
|
|
where: { friendlyId: body.dependentAttempt },
|
|
include: {
|
|
taskRun: {
|
|
select: {
|
|
id: true,
|
|
status: true,
|
|
},
|
|
},
|
|
},
|
|
})
|
|
: undefined;
|
|
|
|
if (
|
|
dependentAttempt &&
|
|
(isFinalAttemptStatus(dependentAttempt.status) ||
|
|
isFinalRunStatus(dependentAttempt.taskRun.status))
|
|
) {
|
|
logger.debug("Dependent attempt or run is in a terminal state", {
|
|
dependentAttempt: dependentAttempt,
|
|
});
|
|
|
|
if (isFinalAttemptStatus(dependentAttempt.status)) {
|
|
throw new ServiceValidationError(
|
|
`Cannot batch trigger ${taskId} as the parent attempt has a status of ${dependentAttempt.status}`
|
|
);
|
|
} else {
|
|
throw new ServiceValidationError(
|
|
`Cannot batch trigger ${taskId} as the parent run has a status of ${dependentAttempt.taskRun.status}`
|
|
);
|
|
}
|
|
}
|
|
|
|
const batch = await this._prisma.batchTaskRun.create({
|
|
data: {
|
|
friendlyId: generateFriendlyId("batch"),
|
|
runtimeEnvironmentId: environment.id,
|
|
idempotencyKey: options.idempotencyKey,
|
|
taskIdentifier: taskId,
|
|
dependentTaskAttemptId: dependentAttempt?.id,
|
|
},
|
|
});
|
|
|
|
const triggerTaskService = new TriggerTaskService();
|
|
|
|
const runs: string[] = [];
|
|
let index = 0;
|
|
|
|
for (const item of body.items) {
|
|
try {
|
|
const run = await triggerTaskService.call(
|
|
taskId,
|
|
environment,
|
|
{
|
|
...item,
|
|
options: {
|
|
...item.options,
|
|
dependentBatch: dependentAttempt?.id ? batch.friendlyId : undefined, // Only set dependentBatch if dependentAttempt is set which means batchTriggerAndWait was called
|
|
},
|
|
},
|
|
{
|
|
triggerVersion: options.triggerVersion,
|
|
traceContext: options.traceContext,
|
|
spanParentAsLink: options.spanParentAsLink,
|
|
batchId: batch.friendlyId,
|
|
}
|
|
);
|
|
|
|
if (run) {
|
|
await this._prisma.batchTaskRunItem.create({
|
|
data: {
|
|
batchTaskRunId: batch.id,
|
|
taskRunId: run.id,
|
|
status: batchTaskRunItemStatusForRunStatus(run.status),
|
|
},
|
|
});
|
|
|
|
runs.push(run.friendlyId);
|
|
}
|
|
|
|
index++;
|
|
} catch (error) {
|
|
logger.error("[BatchTriggerTaskService] Error triggering task", {
|
|
taskId,
|
|
error,
|
|
});
|
|
}
|
|
}
|
|
|
|
span.setAttribute("batchId", batch.friendlyId);
|
|
|
|
return { batch, runs };
|
|
});
|
|
}
|
|
}
|