Files
triggerdotdev--trigger.dev/apps/webapp/app/v3/services/concurrencySystem.server.ts
Chris Arderne c7861be520 chore: activate no-unused-vars and import linters (#4096)
Once this is merged, oxlint is at a pretty sensible baseline.

**Enable `no-unused-vars`, `typescript/consistent-type-imports`, and
`import/no-duplicates` lint rules**

Turns on three previously-disabled oxlint rules across the monorepo and
fixes all violations:

- **`no-unused-vars`** – enabled as an error with standard ignore
patterns: unused function arguments are ignored by default (`args:
"none"`), variables/caught errors/destructured array elements prefixed
with `_` are allowed, and rest siblings are permitted.
- **`typescript/consistent-type-imports`** – enforced as an error; all
type-only imports now use the `import type` syntax.
- **`import/no-duplicates`** – enforced as an error; duplicate import
statements from the same module have been merged.

The remaining commits clean up the violations found across the codebase:
removing unused variables/imports/type aliases, adding `_` prefixes to
intentionally unused bindings, fixing duplicate imports, and converting
value imports to `import type` where appropriate.
2026-07-02 11:37:05 +01:00

211 lines
5.8 KiB
TypeScript

import type { TaskQueue, User } from "@trigger.dev/database";
import { errAsync, fromPromise, okAsync } from "neverthrow";
import type { PrismaClientOrTransaction } from "~/db.server";
import type { AuthenticatedEnvironment } from "~/services/apiAuth.server";
import { removeQueueConcurrencyLimits, updateQueueConcurrencyLimits } from "../runQueue.server";
import { engine } from "../runEngine.server";
export type ConcurrencySystemOptions = {
db: PrismaClientOrTransaction;
reader: PrismaClientOrTransaction;
};
export type QueueInput = string | { type: "task" | "custom"; name: string };
export class ConcurrencySystem {
constructor(private readonly options: ConcurrencySystemOptions) {}
private get db() {
return this.options.db;
}
get queues() {
return {
overrideQueueConcurrencyLimit: (
environment: AuthenticatedEnvironment,
queue: QueueInput,
concurrencyLimit: number,
overriddenBy?: User
) => {
return findQueueFromInput(this.db, environment, queue)
.andThen((queue) =>
overrideQueueConcurrencyLimit(
this.db,
environment,
queue,
concurrencyLimit,
overriddenBy
)
)
.andThen((queue) => syncQueueConcurrencyToEngine(environment, queue))
.andThen((queue) => getQueueStats(environment, queue));
},
resetConcurrencyLimit: (environment: AuthenticatedEnvironment, queue: QueueInput) => {
return findQueueFromInput(this.db, environment, queue)
.andThen((queue) => resetQueueConcurrencyLimit(this.db, queue))
.andThen((queue) => syncQueueConcurrencyToEngine(environment, queue))
.andThen((queue) => getQueueStats(environment, queue));
},
};
}
}
function findQueueFromInput(
db: PrismaClientOrTransaction,
environment: AuthenticatedEnvironment,
queue: QueueInput
) {
if (typeof queue === "string") {
return findQueueByFriendlyId(db, environment, queue);
}
const queueName =
queue.type === "task" ? `task/${queue.name.replace(/^task\//, "")}` : queue.name;
return findQueueByName(db, environment, queueName);
}
function findQueueByFriendlyId(
db: PrismaClientOrTransaction,
environment: AuthenticatedEnvironment,
friendlyId: string
) {
return fromPromise(
db.taskQueue.findFirst({
where: {
runtimeEnvironmentId: environment.id,
friendlyId,
},
}),
(error) => ({
type: "other" as const,
cause: error,
})
).andThen((queue) => {
if (!queue) {
return errAsync({ type: "queue_not_found" as const });
}
return okAsync(queue);
});
}
function findQueueByName(
db: PrismaClientOrTransaction,
environment: AuthenticatedEnvironment,
queue: string
) {
return fromPromise(
db.taskQueue.findFirst({
where: {
runtimeEnvironmentId: environment.id,
name: queue,
},
}),
(error) => ({
type: "other" as const,
cause: error,
})
).andThen((queue) => {
if (!queue) {
return errAsync({ type: "queue_not_found" as const });
}
return okAsync(queue);
});
}
function overrideQueueConcurrencyLimit(
db: PrismaClientOrTransaction,
environment: AuthenticatedEnvironment,
queue: TaskQueue,
concurrencyLimit: number,
overriddenBy?: User
) {
const newConcurrencyLimit = Math.max(
Math.min(concurrencyLimit, environment.maximumConcurrencyLimit),
0
);
const concurrencyLimitBase = queue.concurrencyLimitOverriddenAt
? queue.concurrencyLimitBase
: queue.concurrencyLimit;
return fromPromise(
db.taskQueue.update({
where: {
id: queue.id,
},
data: {
concurrencyLimit: newConcurrencyLimit,
concurrencyLimitBase: concurrencyLimitBase ?? null,
concurrencyLimitOverriddenAt: new Date(),
concurrencyLimitOverriddenBy: overriddenBy?.id ?? null,
},
}),
(error) => ({
type: "queue_update_failed" as const,
cause: error,
})
);
}
function resetQueueConcurrencyLimit(db: PrismaClientOrTransaction, queue: TaskQueue) {
if (queue.concurrencyLimitOverriddenAt === null) {
return errAsync({ type: "queue_not_overridden" as const });
}
const newConcurrencyLimit = queue.concurrencyLimitBase;
return fromPromise(
db.taskQueue.update({
where: { id: queue.id },
data: {
concurrencyLimitOverriddenAt: null,
concurrencyLimit: newConcurrencyLimit,
concurrencyLimitBase: null,
concurrencyLimitOverriddenBy: null,
},
}),
(error) => ({
type: "queue_update_failed" as const,
cause: error,
})
);
}
function syncQueueConcurrencyToEngine(environment: AuthenticatedEnvironment, queue: TaskQueue) {
if (queue.paused) {
// Queue is paused, don't update Redis limits - keep at 0
return okAsync(queue);
}
if (typeof queue.concurrencyLimit === "number") {
return fromPromise(
updateQueueConcurrencyLimits(environment, queue.name, queue.concurrencyLimit),
(error) => ({
type: "sync_queue_concurrency_to_engine_failed" as const,
cause: error,
})
).andThen(() => okAsync(queue));
} else {
return fromPromise(removeQueueConcurrencyLimits(environment, queue.name), (error) => ({
type: "sync_queue_concurrency_to_engine_failed" as const,
cause: error,
})).andThen(() => okAsync(queue));
}
}
function getQueueStats(environment: AuthenticatedEnvironment, queue: TaskQueue) {
return fromPromise(
Promise.all([
engine.lengthOfQueues(environment, [queue.name]),
engine.currentConcurrencyOfQueues(environment, [queue.name]),
]),
(error) => ({
type: "get_queue_stats_failed" as const,
cause: error,
})
).andThen(([queued, running]) =>
okAsync({ queued: queued[queue.name] ?? 0, running: running[queue.name] ?? 0, ...queue })
);
}