Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 19 additions & 3 deletions .oxlintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"plugins": ["typescript", "import", "react"],
"jsPlugins": [
"./oxlint-plugins/no-thrown-unawaited-redirect.mjs",
"./oxlint-plugins/runops-residency.mjs"
"./oxlint-plugins/runops-residency.mjs",
"./oxlint-plugins/prisma-in-filter.mjs"
],
"ignorePatterns": [
"**/dist/**",
Expand All @@ -30,13 +31,21 @@
"no-empty-pattern": "off",
"no-control-regex": "off",
"typescript/no-non-null-asserted-optional-chain": "off",
"no-unused-expressions": ["warn", { "allowShortCircuit": true, "allowTernary": true }],
"no-unused-expressions": [
"warn",
{
"allowShortCircuit": true,
"allowTernary": true
}
],
"typescript/consistent-type-imports": "error",
"import/no-duplicates": "error",
"import/namespace": "off",
"react-hooks/exhaustive-deps": "off",
"react-hooks/rules-of-hooks": "off",
"trigger/no-thrown-unawaited-redirect": "error"
"trigger/no-thrown-unawaited-redirect": "error",
"trigger-prisma/no-unbounded-list-filter": "error",
"trigger-prisma/no-unbounded-list-filter-in-args-helper": "error"
},
"overrides": [
{
Expand All @@ -52,6 +61,13 @@
"trigger-runops/no-control-plane-run-graph-access": "off",
"trigger-runops/no-control-plane-in-runops-slot": "off"
}
},
{
"files": ["**/*.test.ts", "**/*.test.tsx", "**/test/**", "**/tests/**", "**/e2e/**"],
"rules": {
"trigger-prisma/no-unbounded-list-filter": "off",
"trigger-prisma/no-unbounded-list-filter-in-args-helper": "off"
}
}
]
}
6 changes: 6 additions & 0 deletions .server-changes/bounded-list-filter-arity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
area: webapp
type: improvement
---

Database queries that filter on a list of values now reuse cached query plans more consistently, instead of forcing the database to re-plan whenever the list length changes.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
6 changes: 6 additions & 0 deletions .server-changes/remove-prisma-engine-metrics.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
area: webapp
type: improvement
---

Remove the unused query-engine metrics from the metrics endpoint. Database observability continues through the existing OpenTelemetry integration.
Comment thread
ericallam marked this conversation as resolved.
3 changes: 2 additions & 1 deletion apps/webapp/app/models/member.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { getDefaultEnvironmentConcurrencyLimit } from "~/services/platform.v3.se
import { rbac } from "~/services/rbac.server";
import { ssoController } from "~/services/sso.server";

import { boundedIn } from "@trigger.dev/database";
export const INVITE_NOT_FOUND = "Invite not found";
export const INVITE_BLOCKED_DIRECTORY_MANAGED =
"Membership for this organization is managed by Directory Sync, so invites can't be accepted.";
Expand Down Expand Up @@ -134,7 +135,7 @@ export async function inviteMembers({
const existingMembers = await prisma.orgMember.findMany({
where: {
organizationId: org.id,
user: { email: { in: [...uniqueEmails] } },
user: { email: { in: boundedIn([...uniqueEmails]) } },
},
select: { user: { select: { email: true } } },
});
Expand Down
3 changes: 2 additions & 1 deletion apps/webapp/app/models/vercelIntegration.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
} from "~/v3/vercel/vercelProjectIntegrationSchema";
import { EnvironmentVariablesRepository } from "~/v3/environmentVariables/environmentVariablesRepository.server";
import { isReservedForExternalSync } from "~/v3/environmentVariableRules.server";
import { boundedIn } from "@trigger.dev/database";
import {
callVercelWithRecovery,
wrapVercelCallWithRecovery,
Expand Down Expand Up @@ -1415,7 +1416,7 @@ export class VercelIntegrationRepository {
variable: {
projectId: params.projectId,
key: {
in: varsToSync.map((v) => v.key),
in: boundedIn(varsToSync.map((v) => v.key)),
},
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type { AuthenticatedEnvironment } from "~/services/apiAuth.server";
import { runStore as defaultRunStore } from "~/v3/runStore.server";
import { BasePresenter } from "./basePresenter.server";

import { boundedIn } from "@trigger.dev/database";
/**
* Run-ops read-through wiring. All optional; absent (or `splitEnabled` falsy) collapses `call` to
* passthrough. `legacyReplica` is a READ REPLICA handle only — there is NO legacy-primary field.
Expand Down Expand Up @@ -114,7 +115,7 @@ export class ApiBatchResultsPresenter extends BasePresenter {

const taskRuns = await this.runStore.findRuns(
{
where: { id: { in: taskRunIds } },
where: { id: { in: boundedIn(taskRunIds) } },
select: memberRunSelect,
},
this._prisma
Expand Down Expand Up @@ -181,7 +182,7 @@ export class ApiBatchResultsPresenter extends BasePresenter {
const taskRunIds = batchRun.items.map((item) => item.taskRunId);

const newRows = (await newClient.taskRun.findMany({
where: { id: { in: taskRunIds } },
where: { id: { in: boundedIn(taskRunIds) } },
select: memberRunSelect,
})) as TaskRunWithAttempts[];
const runsById = new Map(newRows.map((run) => [run.id, run]));
Expand All @@ -193,7 +194,7 @@ export class ApiBatchResultsPresenter extends BasePresenter {
);
if (legacyCandidateIds.length > 0) {
const legacyRows = (await legacyReplica.taskRun.findMany({
where: { id: { in: legacyCandidateIds } },
where: { id: { in: boundedIn(legacyCandidateIds) } },
select: memberRunSelect,
})) as TaskRunWithAttempts[];
for (const run of legacyRows) {
Expand Down
9 changes: 7 additions & 2 deletions apps/webapp/app/presenters/v3/ApiRunListPresenter.server.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { MachinePresetName, parsePacket, RunStatus } from "@trigger.dev/core/v3";
import { type Project, type RuntimeEnvironment, type TaskRunStatus } from "@trigger.dev/database";
import {
type Project,
type RuntimeEnvironment,
type TaskRunStatus,
boundedIn,
} from "@trigger.dev/database";
import assertNever from "assert-never";
import { z } from "zod";
import type { API_VERSIONS } from "~/api/versions";
Expand Down Expand Up @@ -208,7 +213,7 @@ export class ApiRunListPresenter extends BasePresenter {
where: {
projectId: project.id,
slug: {
in: searchParams["filter[env]"],
in: boundedIn(searchParams["filter[env]"]),
},
},
});
Expand Down
4 changes: 2 additions & 2 deletions apps/webapp/app/presenters/v3/BatchListPresenter.server.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type BatchTaskRunStatus } from "@trigger.dev/database";
import { type BatchTaskRunStatus, boundedIn } from "@trigger.dev/database";
import { type RunOpsPrismaClient } from "@internal/run-ops-database";
import parse from "parse-duration";
import { type PrismaClientOrTransaction } from "~/db.server";
Expand Down Expand Up @@ -263,7 +263,7 @@ export class BatchListPresenter extends BasePresenter {
: {}),
...(friendlyId ? { friendlyId } : {}),
...(statuses && statuses.length > 0
? { status: { in: statuses }, batchVersion: { not: "v1" } }
? { status: { in: boundedIn(statuses) }, batchVersion: { not: "v1" } }
: {}),
...(createdAtGte !== undefined || createdAtLte !== undefined
? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type { SyncEnvVarsMapping, EnvSlug } from "~/v3/vercel/vercelProjectInteg
import { VercelIntegrationService } from "~/services/vercelIntegration.server";
import { loadEnvironmentVariablesEnvironments } from "./environmentVariablesEnvironments.server";

import { boundedIn } from "@trigger.dev/database";
type Result = Awaited<ReturnType<EnvironmentVariablesPresenter["call"]>>;
export type EnvironmentVariableWithSetValues = Result["environmentVariables"][number];

Expand Down Expand Up @@ -72,7 +73,7 @@ export class EnvironmentVariablesPresenter {
},
where: {
environmentId: {
in: environmentIds,
in: boundedIn(environmentIds),
},
},
},
Expand Down Expand Up @@ -103,7 +104,7 @@ export class EnvironmentVariablesPresenter {
? await this.#replicaClient.user.findMany({
where: {
id: {
in: Array.from(userIds),
in: boundedIn(Array.from(userIds)),
},
},
select: {
Expand Down
10 changes: 7 additions & 3 deletions apps/webapp/app/presenters/v3/ErrorsListPresenter.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ const errorsListGranularity = new TimeGranularity([
{ max: "3 months", granularity: "1w" },
{ max: "Infinity", granularity: "30d" },
]);
import { type ErrorGroupStatus, type PrismaClientOrTransaction } from "@trigger.dev/database";
import {
type ErrorGroupStatus,
type PrismaClientOrTransaction,
boundedIn,
} from "@trigger.dev/database";
import { type Direction } from "~/components/ListPagination";
import { timeFilterFromTo } from "~/components/runs/v3/SharedFilters";
import { findDisplayableEnvironment } from "~/models/runtimeEnvironment.server";
Expand Down Expand Up @@ -457,7 +461,7 @@ export class ErrorsListPresenter extends BasePresenter {

if (statuses.includes("UNRESOLVED")) {
const excluded = await this.replica.errorGroupState.findMany({
where: { environmentId, status: { in: excludedStatuses } },
where: { environmentId, status: { in: boundedIn(excludedStatuses) } },
select: { taskIdentifier: true, errorFingerprint: true },
});
if (excluded.length === 0) {
Expand All @@ -470,7 +474,7 @@ export class ErrorsListPresenter extends BasePresenter {
}

const included = await this.replica.errorGroupState.findMany({
where: { environmentId, status: { in: statuses } },
where: { environmentId, status: { in: boundedIn(statuses) } },
select: { taskIdentifier: true, errorFingerprint: true },
});
if (included.length === 0) {
Expand Down
3 changes: 2 additions & 1 deletion apps/webapp/app/presenters/v3/PlaygroundPresenter.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { findCurrentWorkerFromEnvironment } from "~/v3/models/workerDeployment.s
import { runStore } from "~/v3/runStore.server";
import { isFinalRunStatus } from "~/v3/taskStatus";

import { boundedIn } from "@trigger.dev/database";
export type PlaygroundAgent = {
slug: string;
filePath: string;
Expand Down Expand Up @@ -135,7 +136,7 @@ export class PlaygroundPresenter {
const runsById = new Map<string, { friendlyId: string; status: TaskRunStatus }>();
if (runIds.length > 0) {
const runs = await runStore.findRuns({
where: { id: { in: runIds } },
where: { id: { in: boundedIn(runIds) } },
select: { id: true, friendlyId: true, status: true },
});
for (const run of runs) {
Expand Down
8 changes: 4 additions & 4 deletions apps/webapp/app/presenters/v3/QueueListPresenter.server.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { RunEngine } from "@internal/run-engine";
import type { Prisma } from "@trigger.dev/database";
import { TaskQueueType } from "@trigger.dev/database";
import { TaskQueueType, boundedIn } from "@trigger.dev/database";
import { type PrismaClientOrTransaction } from "~/db.server";
import { type AuthenticatedEnvironment } from "~/services/apiAuth.server";
import { clickhouseFactory } from "~/services/clickhouse/clickhouseFactoryInstance.server";
Expand Down Expand Up @@ -289,7 +289,7 @@ export class QueueListPresenter extends BasePresenter {
// AND keeps the search's name filter intact alongside the exclusion (a spread
// would overwrite one name condition with the other).
tailQueues = await this._replica.taskQueue.findMany({
where: { AND: [where, { name: { notIn: excludedNames } }] },
where: { AND: [where, { name: { notIn: boundedIn(excludedNames) } }] },
select: queueListSelect,
orderBy: {
orderableName: "asc",
Expand Down Expand Up @@ -321,7 +321,7 @@ export class QueueListPresenter extends BasePresenter {
return [];
}
const queues = await this._replica.taskQueue.findMany({
where: { AND: [where, { name: { in: names } }] },
where: { AND: [where, { name: { in: boundedIn(names) } }] },
select: queueListSelect,
});
const byName = new Map(queues.map((queue) => [queue.name, queue]));
Expand Down Expand Up @@ -401,7 +401,7 @@ export class QueueListPresenter extends BasePresenter {
const overriddenByIds = queues.map((q) => q.concurrencyLimitOverriddenBy).filter(Boolean);
const overriddenByUsers = await this._replica.user.findMany({
where: {
id: { in: overriddenByIds },
id: { in: boundedIn(overriddenByIds) },
},
});

Expand Down
4 changes: 2 additions & 2 deletions apps/webapp/app/presenters/v3/RegionsPresenter.server.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type WorkloadType } from "@trigger.dev/database";
import { type WorkloadType, boundedIn } from "@trigger.dev/database";
import { type Project } from "~/models/project.server";
import { type User } from "~/models/user.server";
import { FEATURE_FLAG } from "~/v3/featureFlags";
Expand Down Expand Up @@ -87,7 +87,7 @@ export class RegionsPresenter extends BasePresenter {
: // Hide hidden unless they're allowed to use them
project.allowedWorkerQueues.length > 0
? {
masterQueue: { in: project.allowedWorkerQueues },
masterQueue: { in: boundedIn(project.allowedWorkerQueues) },
}
: defaultVisibilityFilter(hasComputeAccess),
orderBy: {
Expand Down
6 changes: 3 additions & 3 deletions apps/webapp/app/presenters/v3/ScheduleListPresenter.server.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type RuntimeEnvironmentType, type ScheduleType } from "@trigger.dev/database";
import { type RuntimeEnvironmentType, type ScheduleType, boundedIn } from "@trigger.dev/database";
import { type ScheduleListFilters } from "~/components/runs/v3/ScheduleFilters";
import { displayableEnvironment } from "~/models/runtimeEnvironment.server";
import { getTaskIdentifiers } from "~/models/task.server";
Expand Down Expand Up @@ -164,7 +164,7 @@ export class ScheduleListPresenter extends BasePresenter {
const totalCount = await this._replica.taskSchedule.count({
where: {
projectId: project.id,
taskIdentifier: tasks ? { in: tasks } : undefined,
taskIdentifier: tasks ? { in: boundedIn(tasks) } : undefined,
instances: {
some: {
environmentId,
Expand Down Expand Up @@ -227,7 +227,7 @@ export class ScheduleListPresenter extends BasePresenter {
},
where: {
projectId: project.id,
taskIdentifier: tasks ? { in: tasks } : undefined,
taskIdentifier: tasks ? { in: boundedIn(tasks) } : undefined,
instances: {
some: {
environmentId,
Expand Down
8 changes: 6 additions & 2 deletions apps/webapp/app/presenters/v3/SessionListPresenter.server.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { type Span } from "@opentelemetry/api";
import { type ClickHouse } from "@internal/clickhouse";
import { type PrismaClient, type PrismaClientOrTransaction } from "@trigger.dev/database";
import {
type PrismaClient,
type PrismaClientOrTransaction,
boundedIn,
} from "@trigger.dev/database";
import { type Direction } from "~/components/ListPagination";
import { timeFilters } from "~/components/runs/v3/SharedFilters";
import { findDisplayableEnvironment } from "~/models/runtimeEnvironment.server";
Expand Down Expand Up @@ -188,7 +192,7 @@ export class SessionListPresenter {
? runStore.findRuns(
{
where: {
id: { in: currentRunIds },
id: { in: boundedIn(currentRunIds) },
projectId,
runtimeEnvironmentId: environmentId,
},
Expand Down
4 changes: 2 additions & 2 deletions apps/webapp/app/presenters/v3/SessionPresenter.server.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { type Span } from "@opentelemetry/api";
import { type PrismaClientOrTransaction } from "@trigger.dev/database";
import { type PrismaClientOrTransaction, boundedIn } from "@trigger.dev/database";
import { env } from "~/env.server";
import { findDisplayableEnvironment } from "~/models/runtimeEnvironment.server";
import { chatSnapshotStorageKey } from "~/services/realtime/chatSnapshot.server";
Expand Down Expand Up @@ -90,7 +90,7 @@ export class SessionPresenter {
return runIds.length > 0
? runStore.findRuns(
{
where: { id: { in: runIds } },
where: { id: { in: boundedIn(runIds) } },
select: { id: true, friendlyId: true, status: true },
},
this.replica
Expand Down
3 changes: 2 additions & 1 deletion apps/webapp/app/presenters/v3/TestTaskPresenter.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
type RuntimeEnvironmentType,
type TaskRunStatus,
type TaskRunTemplate,
boundedIn,
} from "@trigger.dev/database";
import { inferSchema } from "@jsonhero/schema-infer";
import parse from "parse-duration";
Expand Down Expand Up @@ -401,7 +402,7 @@ export class TestTaskPresenter {
return this.runStore.findRuns(
{
where: {
id: { in: ids },
id: { in: boundedIn(ids) },
payloadType: { in: ["application/json", "application/super+json"] },
},
select: RECENT_RUNS_SELECT,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
type RunEngineVersion,
type RuntimeEnvironmentType,
type WaitpointStatus,
boundedIn,
} from "@trigger.dev/database";
import { type Direction } from "~/components/ListPagination";
import { type PrismaClientOrTransaction } from "~/db.server";
Expand Down Expand Up @@ -186,7 +187,7 @@ export class WaitpointListPresenter extends BasePresenter {
type: "MANUAL",
...(cursor ? { id: direction === "forward" ? { lt: cursor } : { gt: cursor } } : {}),
...(id ? { friendlyId: id } : {}),
...(statusesToFilter.length ? { status: { in: statusesToFilter } } : {}),
...(statusesToFilter.length ? { status: { in: boundedIn(statusesToFilter) } } : {}),
...(filterOutputIsError !== undefined ? { outputIsError: filterOutputIsError } : {}),
...(idempotencyKey
? { OR: [{ idempotencyKey }, { inactiveIdempotencyKey: idempotencyKey }] }
Expand Down
Loading
Loading