Skip to content

Commit f151b7a

Browse files
ericallamTrigger.dev RepoOps
authored andcommitted
fix(webapp): read queue metrics from the shared warehouse for dedicated-datastore orgs
Fix the Queues dashboard showing "No activity" on the queue-metrics widgets for organizations whose data is stored in a dedicated ClickHouse, even though their metrics were being collected. Queue metrics are written to a single shared warehouse, but the dashboard was reading them from the per-org ClickHouse; it now reads from the shared warehouse, so the widgets display the collected data. Mono-RevId: e5375536adcde57f1f1461228669bf6db46f2552
1 parent 96090b4 commit f151b7a

6 files changed

Lines changed: 91 additions & 3 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+
Fix the Queues page showing "No activity" on the queue-metrics charts for some organizations even though their metrics were being collected. Those charts now display the collected data.

apps/webapp/app/routes/api.v1.queues.$queueParam.metrics.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ export const loader = createLoaderApiRoute(
7373
try {
7474
const clickhouse = await clickhouseFactory.getClickhouseForOrganization(
7575
authentication.environment.organizationId,
76-
"query"
76+
"queueMetrics"
7777
);
7878

7979
const ids = {
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { describe, expect, it } from "vitest";
2+
import type { OrganizationDataStoresRegistry } from "~/services/dataStores/organizationDataStoresRegistry.server";
3+
import { ClickhouseFactory, getQueueMetricsClickhouseClient } from "./clickhouseFactory.server";
4+
5+
const ORG_WITH_OVERRIDE = "org_with_dedicated_ch";
6+
const DEDICATED_CH_URL = "http://dedicated-ch.example:8123";
7+
8+
function registryWithOverride(overrides: Record<string, string>): OrganizationDataStoresRegistry {
9+
return {
10+
isLoaded: true,
11+
isReady: Promise.resolve(),
12+
get(organizationId: string, kind: string) {
13+
const url = overrides[organizationId];
14+
if (kind === "CLICKHOUSE" && url) {
15+
return { kind: "CLICKHOUSE", url };
16+
}
17+
return null;
18+
},
19+
} as unknown as OrganizationDataStoresRegistry;
20+
}
21+
22+
describe("ClickhouseFactory queue-metrics routing", () => {
23+
it("reads queue metrics from the shared warehouse even when the org has a dedicated CH override", () => {
24+
const factory = new ClickhouseFactory(
25+
registryWithOverride({ [ORG_WITH_OVERRIDE]: DEDICATED_CH_URL })
26+
);
27+
28+
const client = factory.getClickhouseForOrganizationSync(ORG_WITH_OVERRIDE, "queueMetrics");
29+
30+
expect(client).toBe(getQueueMetricsClickhouseClient());
31+
});
32+
33+
it("routes queue metrics to the shared client identically with or without an org override", () => {
34+
const withOverride = new ClickhouseFactory(
35+
registryWithOverride({ [ORG_WITH_OVERRIDE]: DEDICATED_CH_URL })
36+
);
37+
const withoutOverride = new ClickhouseFactory(registryWithOverride({}));
38+
39+
const overrideClient = withOverride.getClickhouseForOrganizationSync(
40+
ORG_WITH_OVERRIDE,
41+
"queueMetrics"
42+
);
43+
const defaultClient = withoutOverride.getClickhouseForOrganizationSync(
44+
ORG_WITH_OVERRIDE,
45+
"queueMetrics"
46+
);
47+
48+
expect(overrideClient).toBe(defaultClient);
49+
expect(overrideClient).toBe(getQueueMetricsClickhouseClient());
50+
});
51+
52+
it("still honors the per-org override for non-shared client types (events)", () => {
53+
const withOverride = new ClickhouseFactory(
54+
registryWithOverride({ [ORG_WITH_OVERRIDE]: DEDICATED_CH_URL })
55+
);
56+
const withoutOverride = new ClickhouseFactory(registryWithOverride({}));
57+
58+
const overrideEvents = withOverride.getClickhouseForOrganizationSync(
59+
ORG_WITH_OVERRIDE,
60+
"events"
61+
);
62+
const defaultEvents = withoutOverride.getClickhouseForOrganizationSync(
63+
ORG_WITH_OVERRIDE,
64+
"events"
65+
);
66+
67+
expect(overrideEvents).not.toBe(defaultEvents);
68+
});
69+
});

apps/webapp/app/services/clickhouse/clickhouseFactory.server.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,16 @@ export type ClientType =
489489
| "runsList"
490490
| "queueMetrics";
491491

492+
/**
493+
* Client types whose data is written to a single shared warehouse by an org-less writer, so their
494+
* reads must always resolve to that same shared default client and must ignore any per-org
495+
* ClickHouse datastore override (a dedicated CH holds the schema but no rows for these). Add a
496+
* client type here to make it globally-routed. `queueMetrics` is written by the org-less ingestion
497+
* consumer (`getQueueMetricsClickhouseClient`); the replication/engine writer types are NOT here
498+
* because their consumers route writes per-org and so legitimately honor the override on both sides.
499+
*/
500+
const SHARED_WAREHOUSE_CLIENT_TYPES = new Set<ClientType>(["queueMetrics"]);
501+
492502
function buildOrgClickhouseClient(url: string, clientType: ClientType): ClickHouse {
493503
const parsed = new URL(url);
494504
parsed.searchParams.delete("secure");
@@ -655,7 +665,9 @@ export class ClickhouseFactory {
655665
}
656666

657667
getClickhouseForOrganizationSync(organizationId: string, clientType: ClientType): ClickHouse {
658-
const dataStore = this._registry.get(organizationId, "CLICKHOUSE");
668+
const dataStore = SHARED_WAREHOUSE_CLIENT_TYPES.has(clientType)
669+
? null
670+
: this._registry.get(organizationId, "CLICKHOUSE");
659671

660672
if (!dataStore) {
661673
switch (clientType) {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ export async function readWatchQueueDepth(
110110

111111
const clickhouse = await clickhouseFactory.getClickhouseForOrganization(
112112
environment.organizationId,
113-
"query"
113+
"queueMetrics"
114114
);
115115

116116
const bucketMs = DEPTH_FALLBACK_BUCKET_SECONDS * 1000;

apps/webapp/vitest.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export default defineConfig({
1818
"app/runEngine/concerns/**/*.test.ts",
1919
"app/runEngine/services/**/*.test.ts",
2020
"app/services/realtime/**/*.test.ts",
21+
"app/services/clickhouse/**/*.test.ts",
2122
"app/utils/**/*.test.ts",
2223
"app/components/code/**/*.test.ts",
2324
"app/components/runs/**/*.test.ts",

0 commit comments

Comments
 (0)