Skip to content

Commit 70280f5

Browse files
committed
feat(webapp,clickhouse,database): add fast and finalized log projection
1 parent 233beba commit 70280f5

18 files changed

Lines changed: 856 additions & 569 deletions

.env.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ NODE_ENV=development
2323
CLICKHOUSE_URL=http://default:password@localhost:8123
2424
RUN_REPLICATION_CLICKHOUSE_URL=http://default:password@localhost:8123
2525
RUN_REPLICATION_ENABLED=1
26+
# LOGS_SEARCH_PROJECTOR_ENABLED=1
27+
# LOGS_SEARCH_PROJECTOR_PREVIEW_ENABLED=1
28+
# LOGS_SEARCH_PROJECTOR_CLICKHOUSE_URL=http://default:password@localhost:8123
2629
# Store task run spans/traces in ClickHouse so the dashboard trace view is
2730
# populated in local dev. The local stack is ClickHouse-backed (see above), so
2831
# leaving this unset falls back to the "postgres" store and dev run traces show

apps/webapp/app/env.server.ts

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2078,16 +2078,8 @@ const EnvironmentSchema = z
20782078
// Scheduled logs-search projection. Disabled by default. The writer URL must reach both the
20792079
// task_events_v2 source and task_events_search_v2 destination tables.
20802080
LOGS_SEARCH_PROJECTOR_ENABLED: BoolEnv.default(false),
2081-
LOGS_SEARCH_PROJECTOR_CLICKHOUSE_URL: z
2082-
.string()
2083-
.optional()
2084-
.transform((v) => v ?? process.env.EVENTS_CLICKHOUSE_URL ?? process.env.CLICKHOUSE_URL),
2085-
LOGS_SEARCH_PROJECTOR_SAFETY_DELAY_SECONDS: z.coerce
2086-
.number()
2087-
.int()
2088-
.min(60)
2089-
.max(3600)
2090-
.default(120),
2081+
LOGS_SEARCH_PROJECTOR_PREVIEW_ENABLED: BoolEnv.default(false),
2082+
LOGS_SEARCH_PROJECTOR_CLICKHOUSE_URL: z.string().optional(),
20912083
LOGS_SEARCH_PROJECTOR_MAX_WINDOWS_PER_TICK: z.coerce.number().int().min(1).max(20).default(5),
20922084
LOGS_SEARCH_PROJECTOR_MAX_EXECUTION_TIME_SECONDS: z.coerce
20932085
.number()
@@ -2102,14 +2094,6 @@ const EnvironmentSchema = z
21022094
.positive()
21032095
.default(1_500_000_000),
21042096
LOGS_SEARCH_PROJECTOR_MAX_THREADS: z.coerce.number().int().min(1).max(8).default(2),
2105-
LOGS_SEARCH_PROJECTOR_BACKFILL_ENABLED: BoolEnv.default(false),
2106-
LOGS_SEARCH_PROJECTOR_MAX_BACKFILL_RANGE_DAYS: z.coerce
2107-
.number()
2108-
.int()
2109-
.min(1)
2110-
.max(90)
2111-
.default(7),
2112-
LOGS_SEARCH_PROJECTOR_MAX_BACKFILL_AGE_DAYS: z.coerce.number().int().min(1).max(90).default(90),
21132097

21142098
// Logs list pagination tuning.
21152099
LOGS_LIST_DEFAULT_PAGE_SIZE: z.coerce.number().int().positive().default(50),
@@ -2431,6 +2415,14 @@ const EnvironmentSchema = z
24312415
.and(GithubAppEnvSchema)
24322416
.and(S2EnvSchema)
24332417
.superRefine((env, ctx) => {
2418+
if (env.LOGS_SEARCH_PROJECTOR_ENABLED && !env.LOGS_SEARCH_PROJECTOR_CLICKHOUSE_URL) {
2419+
ctx.addIssue({
2420+
code: z.ZodIssueCode.custom,
2421+
path: ["LOGS_SEARCH_PROJECTOR_CLICKHOUSE_URL"],
2422+
message: "Required when LOGS_SEARCH_PROJECTOR_ENABLED is true",
2423+
});
2424+
}
2425+
24342426
const presets = new Set(env.COMPUTE_TEMPLATE_MACHINE_PRESETS);
24352427
for (const required of env.COMPUTE_TEMPLATE_MACHINE_PRESETS_REQUIRED) {
24362428
if (!presets.has(required)) {

apps/webapp/app/routes/admin.api.v1.logs-search-projector.ts

Lines changed: 2 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,13 @@
11
import { type ActionFunctionArgs, type LoaderFunctionArgs, json } from "@remix-run/server-runtime";
22
import { z } from "zod";
3-
import {
4-
LogsSearchProjectorConflictError,
5-
LogsSearchProjectorValidationError,
6-
} from "~/services/logsSearchProjector.server";
3+
import { LogsSearchProjectorConflictError } from "~/services/logsSearchProjector.server";
74
import { getLogsSearchProjector } from "~/services/logsSearchProjectorInstance.server";
85
import { logger } from "~/services/logger.server";
96
import { requireAdminApiRequest } from "~/services/personalAccessToken.server";
107

118
const Body = z.discriminatedUnion("action", [
129
z.object({ action: z.literal("pause") }),
1310
z.object({ action: z.literal("resume") }),
14-
z.object({ action: z.literal("cancelBackfill") }),
15-
z.object({
16-
action: z.literal("startBackfill"),
17-
from: z
18-
.string()
19-
.datetime()
20-
.transform((value) => new Date(value)),
21-
to: z
22-
.string()
23-
.datetime()
24-
.transform((value) => new Date(value)),
25-
}),
2611
]);
2712

2813
export async function loader({ request }: LoaderFunctionArgs) {
@@ -43,20 +28,12 @@ export async function action({ request }: ActionFunctionArgs) {
4328
return json(await logsSearchProjector.pause());
4429
case "resume":
4530
return json(await logsSearchProjector.resume());
46-
case "cancelBackfill":
47-
return json(await logsSearchProjector.cancelBackfill());
48-
case "startBackfill":
49-
return json(await logsSearchProjector.startBackfill(body));
5031
}
5132
} catch (error) {
5233
if (error instanceof LogsSearchProjectorConflictError) {
5334
return json({ error: error.message }, { status: 409 });
5435
}
55-
if (
56-
error instanceof LogsSearchProjectorValidationError ||
57-
error instanceof z.ZodError ||
58-
error instanceof SyntaxError
59-
) {
36+
if (error instanceof z.ZodError || error instanceof SyntaxError) {
6037
return json(
6138
{ error: error instanceof Error ? error.message : String(error) },
6239
{ status: 400 }

0 commit comments

Comments
 (0)