Skip to content

Commit 4440134

Browse files
committed
fix(webapp): show the runs-list error state on detail pages too
Route the runs list on the task, scheduled, agent, webhook, and error-group pages through the same capped ClickHouse pool as the main runs list, and let a resource-limit failure surface the in-panel error state instead of being swallowed to an empty list or bubbling to a full-page error.
1 parent a0cd5a5 commit 4440134

6 files changed

Lines changed: 64 additions & 33 deletions

File tree

  • apps/webapp/app
    • presenters/v3
    • routes
      • _app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.agents.$agentParam
      • _app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.errors.$fingerprint
      • _app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.tasks.scheduled.$taskParam
      • _app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.tasks.standard.$taskParam
      • _app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.webhooks.$webhookParam

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,8 @@ export class ErrorGroupPresenter extends BasePresenter {
8787
constructor(
8888
private readonly replica: PrismaClientOrTransaction,
8989
private readonly logsClickhouse: ClickHouse,
90-
private readonly clickhouse: ClickHouse
90+
private readonly clickhouse: ClickHouse,
91+
private readonly runsListClickhouse: ClickHouse
9192
) {
9293
super(undefined, replica);
9394
}
@@ -409,7 +410,7 @@ export class ErrorGroupPresenter extends BasePresenter {
409410
columns?: RunColumnsSelect;
410411
}
411412
): Promise<NextRunList | undefined> {
412-
const runListPresenter = new NextRunListPresenter(this.replica, this.clickhouse);
413+
const runListPresenter = new NextRunListPresenter(this.replica, this.runsListClickhouse);
413414

414415
const result = await runListPresenter.call(organizationId, environmentId, {
415416
userId: options.userId,

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.agents.$agentParam/route.tsx

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import * as Property from "~/components/primitives/PropertyTable";
2323
import { Spinner } from "~/components/primitives/Spinner";
2424
import { TabButton, TabContainer } from "~/components/primitives/Tabs";
2525
import { RunsListErrorState } from "~/components/runs/v3/RunsListErrorState";
26+
import { RunsListQueryError } from "~/services/runsRepository/runsRepository.server";
2627
import { TimeFilter, timeFilterFromTo } from "~/components/runs/v3/SharedFilters";
2728
import { TaskRunsTable } from "~/components/runs/v3/TaskRunsTable";
2829
import { SessionsTable } from "~/components/sessions/v1/SessionsTable";
@@ -93,10 +94,10 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
9394
const directionRaw = url.searchParams.get("direction") ?? undefined;
9495
const direction = directionRaw ? DirectionSchema.parse(directionRaw) : undefined;
9596

96-
const clickhouse = await clickhouseFactory.getClickhouseForOrganization(
97-
project.organizationId,
98-
"standard"
99-
);
97+
const [clickhouse, runsListClickhouse] = await Promise.all([
98+
clickhouseFactory.getClickhouseForOrganization(project.organizationId, "standard"),
99+
clickhouseFactory.getClickhouseForOrganization(project.organizationId, "runsList"),
100+
]);
100101

101102
const presenter = new AgentDetailPresenter($replica, clickhouse);
102103
const agent = await presenter.findAgent({
@@ -155,7 +156,7 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
155156
})
156157
.catch(() => ({ data: [], statuses: [] }) satisfies AgentActivity);
157158

158-
const runList = new NextRunListPresenter($replica, clickhouse)
159+
const runList = new NextRunListPresenter($replica, runsListClickhouse)
159160
.call(project.organizationId, environment.id, {
160161
userId,
161162
projectId: project.id,
@@ -167,7 +168,12 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
167168
direction,
168169
columns: getRunColumnsForSelect(request),
169170
})
170-
.catch(() => null);
171+
.catch((error) => {
172+
if (error instanceof RunsListQueryError) {
173+
throw error;
174+
}
175+
return null;
176+
});
171177

172178
const sessionList = new SessionListPresenter($replica, clickhouse)
173179
.call(project.organizationId, environment.id, {
@@ -342,7 +348,7 @@ export default function Page() {
342348
<>
343349
<RunsDisplayOptions sampleFilters={{ tasks: agent.slug, rootOnly: "false" }} />
344350
<Suspense fallback={null}>
345-
<TypedAwait resolve={runList} errorElement={null}>
351+
<TypedAwait resolve={runList} errorElement={<></>}>
346352
{(list) => (list ? <ListPagination list={list} /> : null)}
347353
</TypedAwait>
348354
</Suspense>

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.errors.$fingerprint/route.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,12 +255,18 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
255255
const directionRaw = url.searchParams.get("direction") ?? undefined;
256256
const direction = directionRaw ? DirectionSchema.parse(directionRaw) : undefined;
257257

258-
const [logsClickhouseClient, clickhouseClient] = await Promise.all([
258+
const [logsClickhouseClient, clickhouseClient, runsListClickhouseClient] = await Promise.all([
259259
clickhouseFactory.getClickhouseForOrganization(environment.organizationId, "logs"),
260260
clickhouseFactory.getClickhouseForOrganization(environment.organizationId, "standard"),
261+
clickhouseFactory.getClickhouseForOrganization(environment.organizationId, "runsList"),
261262
]);
262263

263-
const presenter = new ErrorGroupPresenter($replica, logsClickhouseClient, clickhouseClient);
264+
const presenter = new ErrorGroupPresenter(
265+
$replica,
266+
logsClickhouseClient,
267+
clickhouseClient,
268+
runsListClickhouseClient
269+
);
264270

265271
const detailPromise = presenter
266272
.call(project.organizationId, environment.id, {

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.tasks.scheduled.$taskParam/route.tsx

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ import { TabButton, TabContainer } from "~/components/primitives/Tabs";
6161
import { useToast } from "~/components/primitives/Toast";
6262
import { EnabledStatus } from "~/components/runs/v3/EnabledStatus";
6363
import { RunsListErrorState } from "~/components/runs/v3/RunsListErrorState";
64+
import { RunsListQueryError } from "~/services/runsRepository/runsRepository.server";
6465
import type { TaskRunListSearchFilters } from "~/components/runs/v3/RunFilters";
6566
import { ScheduleTypeIcon, scheduleTypeName } from "~/components/runs/v3/ScheduleType";
6667
import { TimeFilter, timeFilterFromTo } from "~/components/runs/v3/SharedFilters";
@@ -144,10 +145,10 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
144145
const directionRaw = url.searchParams.get("direction") ?? undefined;
145146
const direction = directionRaw ? DirectionSchema.parse(directionRaw) : undefined;
146147

147-
const clickhouse = await clickhouseFactory.getClickhouseForOrganization(
148-
project.organizationId,
149-
"standard"
150-
);
148+
const [clickhouse, runsListClickhouse] = await Promise.all([
149+
clickhouseFactory.getClickhouseForOrganization(project.organizationId, "standard"),
150+
clickhouseFactory.getClickhouseForOrganization(project.organizationId, "runsList"),
151+
]);
151152

152153
const taskPresenter = new TaskDetailPresenter($replica, clickhouse);
153154
const task = await taskPresenter.findTask({
@@ -212,7 +213,7 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
212213
})
213214
.catch(() => null);
214215

215-
const runList = new NextRunListPresenter($replica, clickhouse)
216+
const runList = new NextRunListPresenter($replica, runsListClickhouse)
216217
.call(project.organizationId, environment.id, {
217218
userId,
218219
projectId: project.id,
@@ -225,7 +226,12 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
225226
includeHasAnyRuns: true,
226227
columns: getRunColumnsForSelect(request),
227228
})
228-
.catch(() => null);
229+
.catch((error) => {
230+
if (error instanceof RunsListQueryError) {
231+
throw error;
232+
}
233+
return null;
234+
});
229235

230236
return typeddefer({
231237
task,
@@ -376,7 +382,7 @@ export default function Page() {
376382
) : null}
377383
<RunsDisplayOptions sampleFilters={{ tasks: task.slug, rootOnly: "false" }} />
378384
<Suspense fallback={null}>
379-
<TypedAwait resolve={runList} errorElement={null}>
385+
<TypedAwait resolve={runList} errorElement={<></>}>
380386
{(list) => (list ? <ListPagination list={list} /> : null)}
381387
</TypedAwait>
382388
</Suspense>

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.tasks.standard.$taskParam/route.tsx

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import {
3131
import { Spinner } from "~/components/primitives/Spinner";
3232
import { TextLink } from "~/components/primitives/TextLink";
3333
import { RunsListErrorState } from "~/components/runs/v3/RunsListErrorState";
34+
import { RunsListQueryError } from "~/services/runsRepository/runsRepository.server";
3435
import { TimeFilter, timeFilterFromTo } from "~/components/runs/v3/SharedFilters";
3536
import {
3637
QUEUE_METRIC_COLORS,
@@ -104,10 +105,10 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
104105
const direction = directionRaw ? DirectionSchema.parse(directionRaw) : undefined;
105106
const versions = url.searchParams.getAll("versions").filter((v) => v.length > 0);
106107

107-
const clickhouse = await clickhouseFactory.getClickhouseForOrganization(
108-
project.organizationId,
109-
"standard"
110-
);
108+
const [clickhouse, runsListClickhouse] = await Promise.all([
109+
clickhouseFactory.getClickhouseForOrganization(project.organizationId, "standard"),
110+
clickhouseFactory.getClickhouseForOrganization(project.organizationId, "runsList"),
111+
]);
111112

112113
const presenter = new TaskDetailPresenter($replica, clickhouse);
113114
const task = await presenter.findTask({
@@ -154,7 +155,7 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
154155
})
155156
.catch(() => ({ data: [], statuses: [] }) satisfies TaskActivity);
156157

157-
const runList = new NextRunListPresenter($replica, clickhouse)
158+
const runList = new NextRunListPresenter($replica, runsListClickhouse)
158159
.call(project.organizationId, environment.id, {
159160
userId,
160161
projectId: project.id,
@@ -168,7 +169,12 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
168169
includeHasAnyRuns: true,
169170
columns: getRunColumnsForSelect(request),
170171
})
171-
.catch(() => null);
172+
.catch((error) => {
173+
if (error instanceof RunsListQueryError) {
174+
throw error;
175+
}
176+
return null;
177+
});
172178

173179
return typeddefer({
174180
task,
@@ -272,7 +278,7 @@ export default function Page() {
272278
) : null}
273279
<RunsDisplayOptions sampleFilters={{ tasks: task.slug, rootOnly: "false" }} />
274280
<Suspense fallback={null}>
275-
<TypedAwait resolve={runList} errorElement={null}>
281+
<TypedAwait resolve={runList} errorElement={<></>}>
276282
{(list) => (list ? <ListPagination list={list} /> : null)}
277283
</TypedAwait>
278284
</Suspense>

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.webhooks.$webhookParam/route.tsx

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import { PulsingDot } from "~/components/primitives/PulsingDot";
3030
import { Spinner } from "~/components/primitives/Spinner";
3131
import { TabButton, TabContainer } from "~/components/primitives/Tabs";
3232
import { RunsListErrorState } from "~/components/runs/v3/RunsListErrorState";
33+
import { RunsListQueryError } from "~/services/runsRepository/runsRepository.server";
3334
import { TimeFilter, timeFilterFromTo } from "~/components/runs/v3/SharedFilters";
3435
import { TaskRunsTable } from "~/components/runs/v3/TaskRunsTable";
3536
import { DeliveriesTable } from "~/components/webhookDeliveries/v1/DeliveriesTable";
@@ -117,10 +118,10 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
117118
const runsDirectionRaw = url.searchParams.get("runsDirection") ?? undefined;
118119
const runsDirection = runsDirectionRaw ? DirectionSchema.parse(runsDirectionRaw) : undefined;
119120

120-
const clickhouse = await clickhouseFactory.getClickhouseForOrganization(
121-
project.organizationId,
122-
"standard"
123-
);
121+
const [clickhouse, runsListClickhouse] = await Promise.all([
122+
clickhouseFactory.getClickhouseForOrganization(project.organizationId, "standard"),
123+
clickhouseFactory.getClickhouseForOrganization(project.organizationId, "runsList"),
124+
]);
124125

125126
const presenter = new WebhookDetailPresenter($replica, clickhouse);
126127
const webhook = await presenter.findWebhook({
@@ -157,7 +158,7 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
157158
})
158159
.catch(() => ({ data: [], statuses: [] }) satisfies WebhookActivity);
159160

160-
const runList = new NextRunListPresenter($replica, clickhouse)
161+
const runList = new NextRunListPresenter($replica, runsListClickhouse)
161162
.call(project.organizationId, environment.id, {
162163
userId,
163164
projectId: project.id,
@@ -168,7 +169,12 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
168169
cursor: runsCursor,
169170
direction: runsDirection,
170171
})
171-
.catch(() => null);
172+
.catch((error) => {
173+
if (error instanceof RunsListQueryError) {
174+
throw error;
175+
}
176+
return null;
177+
});
172178

173179
const deliveriesList = presenter
174180
.listDeliveries({
@@ -330,7 +336,7 @@ export default function Page() {
330336
</Suspense>
331337
) : (
332338
<Suspense fallback={null}>
333-
<TypedAwait resolve={runList} errorElement={<RunsListErrorState />}>
339+
<TypedAwait resolve={runList} errorElement={<></>}>
334340
{(list) =>
335341
list ? (
336342
<ListPagination
@@ -483,7 +489,7 @@ function WebhookContentArea({
483489
</Suspense>
484490
) : (
485491
<Suspense fallback={<TableLoading />}>
486-
<TypedAwait resolve={runList} errorElement={<TableLoading />}>
492+
<TypedAwait resolve={runList} errorElement={<RunsListErrorState />}>
487493
{(list) =>
488494
list ? (
489495
<div className="h-full overflow-y-auto scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600">

0 commit comments

Comments
 (0)