diff --git a/apps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.ts b/apps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.ts index b16d400c24..35baef75c6 100644 --- a/apps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.ts +++ b/apps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.ts @@ -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, diff --git a/apps/webapp/app/presenters/v3/SpanPresenter.server.ts b/apps/webapp/app/presenters/v3/SpanPresenter.server.ts index e1bac33dd0..30bf6683db 100644 --- a/apps/webapp/app/presenters/v3/SpanPresenter.server.ts +++ b/apps/webapp/app/presenters/v3/SpanPresenter.server.ts @@ -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: { diff --git a/apps/webapp/app/routes/resources.runs.$runParam.ts b/apps/webapp/app/routes/resources.runs.$runParam.ts index fc4f3a9b0f..733557675e 100644 --- a/apps/webapp/app/routes/resources.runs.$runParam.ts +++ b/apps/webapp/app/routes/resources.runs.$runParam.ts @@ -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, + }; +} diff --git a/apps/webapp/app/v3/services/deleteTaskSchedule.server.ts b/apps/webapp/app/v3/services/deleteTaskSchedule.server.ts index 71a1314585..a696fd1ffa 100644 --- a/apps/webapp/app/v3/services/deleteTaskSchedule.server.ts +++ b/apps/webapp/app/v3/services/deleteTaskSchedule.server.ts @@ -43,7 +43,7 @@ export class DeleteTaskScheduleService extends BaseService { await this._prisma.taskSchedule.delete({ where: { - friendlyId, + id: schedule.id, }, }); } catch (e) { diff --git a/apps/webapp/app/v3/services/triggerScheduledTask.server.ts b/apps/webapp/app/v3/services/triggerScheduledTask.server.ts index 077beb0e2b..af63a15033 100644 --- a/apps/webapp/app/v3/services/triggerScheduledTask.server.ts +++ b/apps/webapp/app/v3/services/triggerScheduledTask.server.ts @@ -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; } diff --git a/internal-packages/database/prisma/migrations/20250206170153_drop_task_schedule_foreign_key_constraints_on_task_run/migration.sql b/internal-packages/database/prisma/migrations/20250206170153_drop_task_schedule_foreign_key_constraints_on_task_run/migration.sql new file mode 100644 index 0000000000..bb7eed1c93 --- /dev/null +++ b/internal-packages/database/prisma/migrations/20250206170153_drop_task_schedule_foreign_key_constraints_on_task_run/migration.sql @@ -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"; \ No newline at end of file diff --git a/internal-packages/database/prisma/schema.prisma b/internal-packages/database/prisma/schema.prisma index d9fbf05194..577c898d4c 100644 --- a/internal-packages/database/prisma/schema.prisma +++ b/internal-packages/database/prisma/schema.prisma @@ -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]) }