chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,174 @@
|
||||
import { ScheduleEngine } from "@internal/schedule-engine";
|
||||
import type { TriggerScheduledTaskErrorType } from "@internal/schedule-engine";
|
||||
import { stringifyIO } from "@trigger.dev/core/v3";
|
||||
import { prisma } from "~/db.server";
|
||||
import { env } from "~/env.server";
|
||||
import { devPresence } from "~/presenters/v3/DevPresence.server";
|
||||
import { logger } from "~/services/logger.server";
|
||||
import { singleton } from "~/utils/singleton";
|
||||
import { OutOfEntitlementError, TriggerTaskService } from "./services/triggerTask.server";
|
||||
import { meter, tracer } from "./tracer.server";
|
||||
import { workerQueue } from "~/services/worker.server";
|
||||
import { ServiceValidationError } from "./services/common.server";
|
||||
import { isV3Disabled } from "./engineDeprecation.server";
|
||||
|
||||
export const scheduleEngine = singleton("ScheduleEngine", createScheduleEngine);
|
||||
|
||||
export type { ScheduleEngine };
|
||||
|
||||
async function isDevEnvironmentConnectedHandler(environmentId: string) {
|
||||
const environment = await prisma.runtimeEnvironment.findFirst({
|
||||
where: {
|
||||
id: environmentId,
|
||||
},
|
||||
select: {
|
||||
currentSession: {
|
||||
select: {
|
||||
disconnectedAt: true,
|
||||
},
|
||||
},
|
||||
project: {
|
||||
select: {
|
||||
engine: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!environment) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (environment.project.engine === "V1") {
|
||||
const v3Disconnected = !environment.currentSession || environment.currentSession.disconnectedAt;
|
||||
|
||||
return !v3Disconnected;
|
||||
}
|
||||
|
||||
const v4Connected = await devPresence.isConnected(environmentId);
|
||||
|
||||
return v4Connected;
|
||||
}
|
||||
|
||||
function createScheduleEngine() {
|
||||
const engine = new ScheduleEngine({
|
||||
prisma,
|
||||
logLevel: env.SCHEDULE_ENGINE_LOG_LEVEL,
|
||||
redis: {
|
||||
host: env.SCHEDULE_WORKER_REDIS_HOST ?? "localhost",
|
||||
port: env.SCHEDULE_WORKER_REDIS_PORT ?? 6379,
|
||||
username: env.SCHEDULE_WORKER_REDIS_USERNAME,
|
||||
password: env.SCHEDULE_WORKER_REDIS_PASSWORD,
|
||||
keyPrefix: "schedule:",
|
||||
enableAutoPipelining: true,
|
||||
...(env.SCHEDULE_WORKER_REDIS_TLS_DISABLED === "true" ? {} : { tls: {} }),
|
||||
},
|
||||
worker: {
|
||||
concurrency: env.SCHEDULE_WORKER_CONCURRENCY_LIMIT,
|
||||
workers: env.SCHEDULE_WORKER_CONCURRENCY_WORKERS,
|
||||
tasksPerWorker: env.SCHEDULE_WORKER_CONCURRENCY_TASKS_PER_WORKER,
|
||||
pollIntervalMs: env.SCHEDULE_WORKER_POLL_INTERVAL,
|
||||
shutdownTimeoutMs: env.SCHEDULE_WORKER_SHUTDOWN_TIMEOUT_MS,
|
||||
disabled: env.SCHEDULE_WORKER_ENABLED === "0",
|
||||
},
|
||||
distributionWindow: {
|
||||
seconds: env.SCHEDULE_WORKER_DISTRIBUTION_WINDOW_SECONDS,
|
||||
},
|
||||
tracer,
|
||||
meter,
|
||||
onTriggerScheduledTask: async ({
|
||||
taskIdentifier,
|
||||
environment,
|
||||
payload,
|
||||
scheduleInstanceId,
|
||||
scheduleId,
|
||||
exactScheduleTime,
|
||||
}) => {
|
||||
try {
|
||||
// v3 (engine V1) shutdown: skip firing schedules for V1 projects so the
|
||||
// cron doesn't keep doing trigger work just to be rejected. Return success
|
||||
// so the schedule engine treats it as handled and doesn't retry. v4 is
|
||||
// unaffected.
|
||||
if (isV3Disabled() && environment.project.engine === "V1") {
|
||||
logger.debug("[ScheduleEngine] Skipping scheduled fire for shut-down v3 project", {
|
||||
taskIdentifier,
|
||||
scheduleId,
|
||||
});
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
// This will trigger either v1 or v2 depending on the engine of the project
|
||||
const triggerService = new TriggerTaskService();
|
||||
|
||||
const payloadPacket = await stringifyIO(payload);
|
||||
|
||||
logger.debug("Triggering scheduled task", {
|
||||
taskIdentifier,
|
||||
environment,
|
||||
payload,
|
||||
scheduleInstanceId,
|
||||
scheduleId,
|
||||
exactScheduleTime,
|
||||
});
|
||||
|
||||
const result = await triggerService.call(
|
||||
taskIdentifier,
|
||||
environment,
|
||||
{ payload: payloadPacket.data, options: { payloadType: payloadPacket.dataType } },
|
||||
{
|
||||
customIcon: "scheduled",
|
||||
scheduleId,
|
||||
scheduleInstanceId,
|
||||
queueTimestamp: exactScheduleTime,
|
||||
overrideCreatedAt: exactScheduleTime,
|
||||
triggerSource: "schedule",
|
||||
triggerAction: "trigger",
|
||||
}
|
||||
);
|
||||
|
||||
return { success: !!result };
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
let errorType: TriggerScheduledTaskErrorType = "SYSTEM_ERROR";
|
||||
|
||||
if (
|
||||
error instanceof ServiceValidationError &&
|
||||
errorMessage.includes("queue size limit for this environment has been reached")
|
||||
) {
|
||||
errorType = "QUEUE_LIMIT";
|
||||
} else if (error instanceof OutOfEntitlementError) {
|
||||
// The org is out of entitlements. This is an expected outcome, not a
|
||||
// system error, so the engine logs it as a warning rather than
|
||||
// reporting it as an error.
|
||||
errorType = "OUT_OF_ENTITLEMENTS";
|
||||
}
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: errorMessage,
|
||||
errorType,
|
||||
};
|
||||
}
|
||||
},
|
||||
isDevEnvironmentConnectedHandler: isDevEnvironmentConnectedHandler,
|
||||
onRegisterScheduleInstance: removeDeprecatedWorkerQueueItem,
|
||||
});
|
||||
|
||||
return engine;
|
||||
}
|
||||
|
||||
async function removeDeprecatedWorkerQueueItem(instanceId: string) {
|
||||
// We need to dequeue the instance from the existing workerQueue
|
||||
try {
|
||||
await workerQueue.dequeue(`scheduled-task-instance:${instanceId}`);
|
||||
|
||||
logger.debug("Removed deprecated worker queue item", {
|
||||
instanceId,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error("Error dequeuing scheduled task instance from deprecated queue", {
|
||||
instanceId,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user