Skip to content
Merged
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
6 changes: 6 additions & 0 deletions .server-changes/task-list-remove-unused-metrics-queries.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
area: webapp
type: fix
---

The tasks page no longer runs two queries whose results were never displayed, cutting wasted work on every page load and removing a source of hidden server errors
30 changes: 5 additions & 25 deletions apps/webapp/app/presenters/v3/TaskListPresenter.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@ import {
import { $replica } from "~/db.server";
import { clickhouseFactory } from "~/services/clickhouse/clickhouseFactoryInstance.server";
import {
type AverageDurations,
ClickHouseEnvironmentMetricsRepository,
type CurrentRunningStats,
type DailyTaskActivity,
} from "~/services/environmentMetricsRepository.server";
import { singleton } from "~/utils/singleton";
import { findCurrentWorkerFromEnvironment } from "~/v3/models/workerDeployment.server";
Expand All @@ -21,8 +19,6 @@ export type TaskListItem = {
triggerSource: TaskTriggerSource;
};

export type TaskActivity = DailyTaskActivity[string];

export class TaskListPresenter {
constructor(private readonly _replica: PrismaClientOrTransaction) {}

Expand Down Expand Up @@ -55,9 +51,7 @@ export class TaskListPresenter {
if (!currentWorker) {
return {
tasks: [],
activity: Promise.resolve({} as DailyTaskActivity),
runningStats: Promise.resolve({} as CurrentRunningStats),
durations: Promise.resolve({} as AverageDurations),
};
}

Expand Down Expand Up @@ -89,16 +83,10 @@ export class TaskListPresenter {
clickhouse,
});

// IMPORTANT: Don't await these, we want to return the promises
// so we can defer the loading of the data
const activity = environmentMetricsRepository.getDailyTaskActivity({
organizationId,
projectId,
environmentId,
days: 6, // This actually means 7 days, because we want to show the current day too
tasks: slugs,
});

// IMPORTANT: Don't await this, we want to return the promise
// so we can defer the loading of the data. The caller is responsible for
// consuming it — an unconsumed promise here would become an unhandled
// rejection if the underlying query fails.
const runningStats = environmentMetricsRepository.getCurrentRunningStats({
organizationId,
projectId,
Expand All @@ -107,15 +95,7 @@ export class TaskListPresenter {
tasks: slugs,
});

const durations = environmentMetricsRepository.getAverageDurations({
organizationId,
projectId,
environmentId,
days: 6,
tasks: slugs,
});

return { tasks, activity, runningStats, durations };
return { tasks, runningStats };
}
}

Expand Down
133 changes: 0 additions & 133 deletions apps/webapp/app/services/environmentMetricsRepository.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,16 @@ import { type ClickHouse } from "@internal/clickhouse";
import type { TaskRunStatus } from "@trigger.dev/database";
import { QUEUED_STATUSES } from "~/components/runs/v3/TaskRunStatus";

export type DailyTaskActivity = Record<string, ({ day: string } & Record<TaskRunStatus, number>)[]>;
export type CurrentRunningStats = Record<string, { queued: number; running: number }>;
export type AverageDurations = Record<string, number>;

export interface EnvironmentMetricsRepository {
getDailyTaskActivity(options: {
organizationId: string;
projectId: string;
environmentId: string;
days: number;
tasks: string[];
}): Promise<DailyTaskActivity>;

getCurrentRunningStats(options: {
organizationId: string;
projectId: string;
environmentId: string;
days: number;
tasks: string[];
}): Promise<CurrentRunningStats>;

getAverageDurations(options: {
organizationId: string;
projectId: string;
environmentId: string;
days: number;
tasks: string[];
}): Promise<AverageDurations>;
}

export type ClickHouseEnvironmentMetricsRepositoryOptions = {
Expand All @@ -39,45 +21,6 @@ export type ClickHouseEnvironmentMetricsRepositoryOptions = {
export class ClickHouseEnvironmentMetricsRepository implements EnvironmentMetricsRepository {
constructor(private readonly options: ClickHouseEnvironmentMetricsRepositoryOptions) {}

public async getDailyTaskActivity({
organizationId,
projectId,
environmentId,
days,
tasks,
}: {
organizationId: string;
projectId: string;
environmentId: string;
days: number;
tasks: string[];
}): Promise<DailyTaskActivity> {
if (tasks.length === 0) {
return {};
}

const [queryError, activity] = await this.options.clickhouse.taskRuns.getTaskActivity({
organizationId,
projectId,
environmentId,
days,
});

if (queryError) {
throw queryError;
}

return fillInDailyTaskActivity(
activity.map((a) => ({
taskIdentifier: a.task_identifier,
status: a.status as TaskRunStatus,
day: new Date(a.day),
count: BigInt(a.count),
})),
days
);
}

public async getCurrentRunningStats({
organizationId,
projectId,
Expand Down Expand Up @@ -115,82 +58,6 @@ export class ClickHouseEnvironmentMetricsRepository implements EnvironmentMetric
tasks
);
}

public async getAverageDurations({
organizationId,
projectId,
environmentId,
days,
tasks,
}: {
organizationId: string;
projectId: string;
environmentId: string;
days: number;
tasks: string[];
}): Promise<AverageDurations> {
if (tasks.length === 0) {
return {};
}

const [queryError, durations] = await this.options.clickhouse.taskRuns.getAverageDurations({
organizationId,
projectId,
environmentId,
days,
});

if (queryError) {
throw queryError;
}

return Object.fromEntries(durations.map((d) => [d.task_identifier, Number(d.duration)]));
}
}

type TaskActivityResults = Array<{
taskIdentifier: string;
status: TaskRunStatus;
day: Date;
count: bigint;
}>;

function fillInDailyTaskActivity(activity: TaskActivityResults, days: number): DailyTaskActivity {
//today with no time
const today = new Date();
today.setUTCHours(0, 0, 0, 0);

return activity.reduce((acc, a) => {
let existingTask = acc[a.taskIdentifier];

if (!existingTask) {
existingTask = [];
//populate the array with the past 7 days
for (let i = days; i >= 0; i--) {
const day = new Date(today);
day.setUTCDate(today.getDate() - i);
day.setUTCHours(0, 0, 0, 0);

existingTask.push({
day: day.toISOString(),
["COMPLETED_SUCCESSFULLY"]: 0,
} as { day: string } & Record<TaskRunStatus, number>);
}

acc[a.taskIdentifier] = existingTask;
}

const dayString = a.day.toISOString();
const day = existingTask.find((d) => d.day === dayString);

if (!day) {
return acc;
}

day[a.status] = Number(a.count);

return acc;
}, {} as DailyTaskActivity);
}

type CurrentRunningStatsResults = Array<{
Expand Down
Loading