Skip to content

Commit b2d5bf8

Browse files
committed
fix(webapp): remove unawaited task list metrics promises
`TaskListPresenter.call` returned three deferred promises — `activity`, `runningStats` and `durations` — but its only caller, `UnifiedTaskListPresenter`, reads just `tasks` and `runningStats`. The `activity` and `durations` promises were never awaited, read or given a rejection handler, so a failing query became an unhandled promise rejection, and the two ClickHouse round-trips behind them ran on every task list page load for results nothing rendered. `UnifiedTaskListPresenter` builds its own hourly activity query for the chart the page actually shows, so the daily activity and average duration data had no consumer left. Remove both fields and the calls that feed them, plus the `getDailyTaskActivity` and `getAverageDurations` methods on `ClickHouseEnvironmentMetricsRepository` (and the dead `TaskActivity` / `DailyTaskActivity` / `AverageDurations` types), which had no other callers. `runningStats` is kept and is unaffected: it is consumed by `UnifiedTaskListPresenter`, which is why its rejections were already handled. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PtQ34usZSzrSALPVBmoLYA
1 parent be45cf9 commit b2d5bf8

3 files changed

Lines changed: 11 additions & 158 deletions

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
area: webapp
3+
type: fix
4+
---
5+
6+
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

apps/webapp/app/presenters/v3/TaskListPresenter.server.ts

Lines changed: 5 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,8 @@ import {
66
import { $replica } from "~/db.server";
77
import { clickhouseFactory } from "~/services/clickhouse/clickhouseFactoryInstance.server";
88
import {
9-
type AverageDurations,
109
ClickHouseEnvironmentMetricsRepository,
1110
type CurrentRunningStats,
12-
type DailyTaskActivity,
1311
} from "~/services/environmentMetricsRepository.server";
1412
import { singleton } from "~/utils/singleton";
1513
import { findCurrentWorkerFromEnvironment } from "~/v3/models/workerDeployment.server";
@@ -21,8 +19,6 @@ export type TaskListItem = {
2119
triggerSource: TaskTriggerSource;
2220
};
2321

24-
export type TaskActivity = DailyTaskActivity[string];
25-
2622
export class TaskListPresenter {
2723
constructor(private readonly _replica: PrismaClientOrTransaction) {}
2824

@@ -55,9 +51,7 @@ export class TaskListPresenter {
5551
if (!currentWorker) {
5652
return {
5753
tasks: [],
58-
activity: Promise.resolve({} as DailyTaskActivity),
5954
runningStats: Promise.resolve({} as CurrentRunningStats),
60-
durations: Promise.resolve({} as AverageDurations),
6155
};
6256
}
6357

@@ -89,16 +83,10 @@ export class TaskListPresenter {
8983
clickhouse,
9084
});
9185

92-
// IMPORTANT: Don't await these, we want to return the promises
93-
// so we can defer the loading of the data
94-
const activity = environmentMetricsRepository.getDailyTaskActivity({
95-
organizationId,
96-
projectId,
97-
environmentId,
98-
days: 6, // This actually means 7 days, because we want to show the current day too
99-
tasks: slugs,
100-
});
101-
86+
// IMPORTANT: Don't await this, we want to return the promise
87+
// so we can defer the loading of the data. The caller is responsible for
88+
// consuming it — an unconsumed promise here would become an unhandled
89+
// rejection if the underlying query fails.
10290
const runningStats = environmentMetricsRepository.getCurrentRunningStats({
10391
organizationId,
10492
projectId,
@@ -107,15 +95,7 @@ export class TaskListPresenter {
10795
tasks: slugs,
10896
});
10997

110-
const durations = environmentMetricsRepository.getAverageDurations({
111-
organizationId,
112-
projectId,
113-
environmentId,
114-
days: 6,
115-
tasks: slugs,
116-
});
117-
118-
return { tasks, activity, runningStats, durations };
98+
return { tasks, runningStats };
11999
}
120100
}
121101

apps/webapp/app/services/environmentMetricsRepository.server.ts

Lines changed: 0 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,16 @@ import { type ClickHouse } from "@internal/clickhouse";
22
import type { TaskRunStatus } from "@trigger.dev/database";
33
import { QUEUED_STATUSES } from "~/components/runs/v3/TaskRunStatus";
44

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

97
export interface EnvironmentMetricsRepository {
10-
getDailyTaskActivity(options: {
11-
organizationId: string;
12-
projectId: string;
13-
environmentId: string;
14-
days: number;
15-
tasks: string[];
16-
}): Promise<DailyTaskActivity>;
17-
188
getCurrentRunningStats(options: {
199
organizationId: string;
2010
projectId: string;
2111
environmentId: string;
2212
days: number;
2313
tasks: string[];
2414
}): Promise<CurrentRunningStats>;
25-
26-
getAverageDurations(options: {
27-
organizationId: string;
28-
projectId: string;
29-
environmentId: string;
30-
days: number;
31-
tasks: string[];
32-
}): Promise<AverageDurations>;
3315
}
3416

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

42-
public async getDailyTaskActivity({
43-
organizationId,
44-
projectId,
45-
environmentId,
46-
days,
47-
tasks,
48-
}: {
49-
organizationId: string;
50-
projectId: string;
51-
environmentId: string;
52-
days: number;
53-
tasks: string[];
54-
}): Promise<DailyTaskActivity> {
55-
if (tasks.length === 0) {
56-
return {};
57-
}
58-
59-
const [queryError, activity] = await this.options.clickhouse.taskRuns.getTaskActivity({
60-
organizationId,
61-
projectId,
62-
environmentId,
63-
days,
64-
});
65-
66-
if (queryError) {
67-
throw queryError;
68-
}
69-
70-
return fillInDailyTaskActivity(
71-
activity.map((a) => ({
72-
taskIdentifier: a.task_identifier,
73-
status: a.status as TaskRunStatus,
74-
day: new Date(a.day),
75-
count: BigInt(a.count),
76-
})),
77-
days
78-
);
79-
}
80-
8124
public async getCurrentRunningStats({
8225
organizationId,
8326
projectId,
@@ -115,82 +58,6 @@ export class ClickHouseEnvironmentMetricsRepository implements EnvironmentMetric
11558
tasks
11659
);
11760
}
118-
119-
public async getAverageDurations({
120-
organizationId,
121-
projectId,
122-
environmentId,
123-
days,
124-
tasks,
125-
}: {
126-
organizationId: string;
127-
projectId: string;
128-
environmentId: string;
129-
days: number;
130-
tasks: string[];
131-
}): Promise<AverageDurations> {
132-
if (tasks.length === 0) {
133-
return {};
134-
}
135-
136-
const [queryError, durations] = await this.options.clickhouse.taskRuns.getAverageDurations({
137-
organizationId,
138-
projectId,
139-
environmentId,
140-
days,
141-
});
142-
143-
if (queryError) {
144-
throw queryError;
145-
}
146-
147-
return Object.fromEntries(durations.map((d) => [d.task_identifier, Number(d.duration)]));
148-
}
149-
}
150-
151-
type TaskActivityResults = Array<{
152-
taskIdentifier: string;
153-
status: TaskRunStatus;
154-
day: Date;
155-
count: bigint;
156-
}>;
157-
158-
function fillInDailyTaskActivity(activity: TaskActivityResults, days: number): DailyTaskActivity {
159-
//today with no time
160-
const today = new Date();
161-
today.setUTCHours(0, 0, 0, 0);
162-
163-
return activity.reduce((acc, a) => {
164-
let existingTask = acc[a.taskIdentifier];
165-
166-
if (!existingTask) {
167-
existingTask = [];
168-
//populate the array with the past 7 days
169-
for (let i = days; i >= 0; i--) {
170-
const day = new Date(today);
171-
day.setUTCDate(today.getDate() - i);
172-
day.setUTCHours(0, 0, 0, 0);
173-
174-
existingTask.push({
175-
day: day.toISOString(),
176-
["COMPLETED_SUCCESSFULLY"]: 0,
177-
} as { day: string } & Record<TaskRunStatus, number>);
178-
}
179-
180-
acc[a.taskIdentifier] = existingTask;
181-
}
182-
183-
const dayString = a.day.toISOString();
184-
const day = existingTask.find((d) => d.day === dayString);
185-
186-
if (!day) {
187-
return acc;
188-
}
189-
190-
day[a.status] = Number(a.count);
191-
192-
return acc;
193-
}, {} as DailyTaskActivity);
19461
}
19562

19663
type CurrentRunningStatsResults = Array<{

0 commit comments

Comments
 (0)