Remove schedule constraints from TaskRun to prevent db load issues when a schedule or instance is deleted (#1674)
This commit is contained in:
@@ -39,6 +39,7 @@ const commonRunSelect = {
|
||||
idempotencyKey: true,
|
||||
isTest: true,
|
||||
depth: true,
|
||||
scheduleId: true,
|
||||
lockedToVersion: {
|
||||
select: {
|
||||
version: true,
|
||||
@@ -71,7 +72,6 @@ export class ApiRetrieveRunPresenter extends BasePresenter {
|
||||
include: {
|
||||
attempts: true,
|
||||
lockedToVersion: true,
|
||||
schedule: true,
|
||||
tags: true,
|
||||
batch: {
|
||||
select: {
|
||||
@@ -157,20 +157,7 @@ export class ApiRetrieveRunPresenter extends BasePresenter {
|
||||
output: $output,
|
||||
outputPresignedUrl: $outputPresignedUrl,
|
||||
error: ApiRetrieveRunPresenter.apiErrorFromError(taskRun.error),
|
||||
schedule: taskRun.schedule
|
||||
? {
|
||||
id: taskRun.schedule.friendlyId,
|
||||
externalId: taskRun.schedule.externalId ?? undefined,
|
||||
deduplicationKey: taskRun.schedule.userProvidedDeduplicationKey
|
||||
? taskRun.schedule.deduplicationKey
|
||||
: undefined,
|
||||
generator: {
|
||||
type: "CRON" as const,
|
||||
expression: taskRun.schedule.generatorExpression,
|
||||
description: taskRun.schedule.generatorDescription,
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
schedule: await resolveSchedule(taskRun),
|
||||
// We're removing attempts from the API
|
||||
attemptCount: taskRun.attempts.length,
|
||||
attempts: [],
|
||||
@@ -320,6 +307,33 @@ export class ApiRetrieveRunPresenter extends BasePresenter {
|
||||
}
|
||||
}
|
||||
|
||||
async function resolveSchedule(run: CommonRelatedRun) {
|
||||
if (!run.scheduleId) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const schedule = await prisma.taskSchedule.findFirst({
|
||||
where: {
|
||||
id: run.scheduleId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!schedule) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
id: schedule.friendlyId,
|
||||
externalId: schedule.externalId ?? undefined,
|
||||
deduplicationKey: schedule.userProvidedDeduplicationKey ? schedule.deduplicationKey : undefined,
|
||||
generator: {
|
||||
type: "CRON" as const,
|
||||
expression: schedule.generatorExpression,
|
||||
description: schedule.generatorDescription,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async function createCommonRunStructure(run: CommonRelatedRun) {
|
||||
const metadata = await parsePacket({
|
||||
data: run.metadata ?? undefined,
|
||||
|
||||
@@ -102,14 +102,7 @@ export class SpanPresenter extends BasePresenter {
|
||||
queue: true,
|
||||
concurrencyKey: true,
|
||||
//schedule
|
||||
schedule: {
|
||||
select: {
|
||||
friendlyId: true,
|
||||
generatorExpression: true,
|
||||
timezone: true,
|
||||
generatorDescription: true,
|
||||
},
|
||||
},
|
||||
scheduleId: true,
|
||||
//usage
|
||||
baseCostInCents: true,
|
||||
costInCents: true,
|
||||
@@ -281,14 +274,7 @@ export class SpanPresenter extends BasePresenter {
|
||||
sdkVersion: run.lockedToVersion?.sdkVersion,
|
||||
isTest: run.isTest,
|
||||
environmentId: run.runtimeEnvironment.id,
|
||||
schedule: run.schedule
|
||||
? {
|
||||
friendlyId: run.schedule.friendlyId,
|
||||
generatorExpression: run.schedule.generatorExpression,
|
||||
description: run.schedule.generatorDescription,
|
||||
timezone: run.schedule.timezone,
|
||||
}
|
||||
: undefined,
|
||||
schedule: await this.resolveSchedule(run.scheduleId ?? undefined),
|
||||
queue: {
|
||||
name: run.queue,
|
||||
isCustomQueue: !run.queue.startsWith("task/"),
|
||||
@@ -323,6 +309,35 @@ export class SpanPresenter extends BasePresenter {
|
||||
};
|
||||
}
|
||||
|
||||
async resolveSchedule(scheduleId?: string) {
|
||||
if (!scheduleId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const schedule = await this._replica.taskSchedule.findFirst({
|
||||
where: {
|
||||
id: scheduleId,
|
||||
},
|
||||
select: {
|
||||
friendlyId: true,
|
||||
generatorExpression: true,
|
||||
timezone: true,
|
||||
generatorDescription: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!schedule) {
|
||||
return;
|
||||
}
|
||||
|
||||
return {
|
||||
friendlyId: schedule.friendlyId,
|
||||
generatorExpression: schedule.generatorExpression,
|
||||
description: schedule.generatorDescription,
|
||||
timezone: schedule.timezone,
|
||||
};
|
||||
}
|
||||
|
||||
async getSpan(runFriendlyId: string, spanId: string) {
|
||||
const run = await this._prisma.taskRun.findFirst({
|
||||
select: {
|
||||
|
||||
@@ -54,14 +54,7 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
|
||||
queue: true,
|
||||
concurrencyKey: true,
|
||||
//schedule
|
||||
schedule: {
|
||||
select: {
|
||||
friendlyId: true,
|
||||
generatorExpression: true,
|
||||
timezone: true,
|
||||
generatorDescription: true,
|
||||
},
|
||||
},
|
||||
scheduleId: true,
|
||||
//usage
|
||||
baseCostInCents: true,
|
||||
costInCents: true,
|
||||
@@ -212,14 +205,7 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
|
||||
sdkVersion: run.lockedToVersion?.sdkVersion,
|
||||
isTest: run.isTest,
|
||||
environmentId: run.runtimeEnvironment.id,
|
||||
schedule: run.schedule
|
||||
? {
|
||||
friendlyId: run.schedule.friendlyId,
|
||||
generatorExpression: run.schedule.generatorExpression,
|
||||
description: run.schedule.generatorDescription,
|
||||
timezone: run.schedule.timezone,
|
||||
}
|
||||
: undefined,
|
||||
schedule: await resolveSchedule(run.scheduleId ?? undefined),
|
||||
queue: {
|
||||
name: run.queue,
|
||||
isCustomQueue: !run.queue.startsWith("task/"),
|
||||
@@ -240,3 +226,32 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
|
||||
context: JSON.stringify(context, null, 2),
|
||||
});
|
||||
};
|
||||
|
||||
async function resolveSchedule(scheduleId?: string) {
|
||||
if (!scheduleId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const schedule = await $replica.taskSchedule.findFirst({
|
||||
where: {
|
||||
id: scheduleId,
|
||||
},
|
||||
select: {
|
||||
friendlyId: true,
|
||||
generatorExpression: true,
|
||||
timezone: true,
|
||||
generatorDescription: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!schedule) {
|
||||
return;
|
||||
}
|
||||
|
||||
return {
|
||||
friendlyId: schedule.friendlyId,
|
||||
generatorExpression: schedule.generatorExpression,
|
||||
description: schedule.generatorDescription,
|
||||
timezone: schedule.timezone,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ export class DeleteTaskScheduleService extends BaseService {
|
||||
|
||||
await this._prisma.taskSchedule.delete({
|
||||
where: {
|
||||
friendlyId,
|
||||
id: schedule.id,
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
|
||||
@@ -32,6 +32,16 @@ export class TriggerScheduledTaskService extends BaseService {
|
||||
return;
|
||||
}
|
||||
|
||||
if (instance.environment.organization.deletedAt) {
|
||||
logger.debug("Organization is deleted, disabling schedule", {
|
||||
instanceId,
|
||||
scheduleId: instance.taskSchedule.friendlyId,
|
||||
organizationId: instance.environment.organization.id,
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
let shouldTrigger = true;
|
||||
|
||||
@@ -40,23 +50,6 @@ export class TriggerScheduledTaskService extends BaseService {
|
||||
}
|
||||
|
||||
if (!instance.taskSchedule.active) {
|
||||
shouldTrigger = false;
|
||||
} else if (instance.environment.organization.deletedAt) {
|
||||
logger.debug("Organization is deleted, disabling schedule", {
|
||||
instanceId,
|
||||
scheduleId: instance.taskSchedule.friendlyId,
|
||||
organizationId: instance.environment.organization.id,
|
||||
});
|
||||
|
||||
await this._prisma.taskSchedule.update({
|
||||
where: {
|
||||
id: instance.taskSchedule.id,
|
||||
},
|
||||
data: {
|
||||
active: false,
|
||||
},
|
||||
});
|
||||
|
||||
shouldTrigger = false;
|
||||
}
|
||||
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
-- DropForeignKey
|
||||
ALTER TABLE
|
||||
"TaskRun" DROP CONSTRAINT IF EXISTS "TaskRun_scheduleId_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE
|
||||
"TaskRun" DROP CONSTRAINT IF EXISTS "TaskRun_scheduleInstanceId_fkey";
|
||||
@@ -1735,11 +1735,8 @@ model TaskRun {
|
||||
|
||||
alerts ProjectAlert[]
|
||||
|
||||
scheduleInstance TaskScheduleInstance? @relation(fields: [scheduleInstanceId], references: [id], onDelete: SetNull)
|
||||
scheduleInstanceId String?
|
||||
|
||||
schedule TaskSchedule? @relation(fields: [scheduleId], references: [id], onDelete: SetNull)
|
||||
scheduleId String?
|
||||
scheduleId String?
|
||||
|
||||
sourceBulkActionItems BulkActionItem[] @relation("SourceActionItemRun")
|
||||
destinationBulkActionItems BulkActionItem[] @relation("DestinationActionItemRun")
|
||||
@@ -2452,8 +2449,6 @@ model TaskSchedule {
|
||||
|
||||
active Boolean @default(true)
|
||||
|
||||
runs TaskRun[]
|
||||
|
||||
@@unique([projectId, deduplicationKey])
|
||||
}
|
||||
|
||||
@@ -2486,8 +2481,6 @@ model TaskScheduleInstance {
|
||||
lastScheduledTimestamp DateTime?
|
||||
nextScheduledTimestamp DateTime?
|
||||
|
||||
runs TaskRun[]
|
||||
|
||||
//you can only have a schedule attached to each environment once
|
||||
@@unique([taskScheduleId, environmentId])
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user