Skip to content

Commit 1583f3e

Browse files
d-csclaude
andcommitted
feat(run-ops): webapp routes — friendlyId reads, resolver auth re-check, co-location writes
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 94eaef6 commit 1583f3e

37 files changed

Lines changed: 1616 additions & 550 deletions

File tree

apps/webapp/app/components/admin/debugRun.tsx

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ function DebugRunData(props: UseDataFunctionReturn<typeof loader>) {
7676

7777
function DebugRunDataEngineV1({
7878
run,
79+
environment,
7980
queueConcurrencyLimit,
8081
queueCurrentConcurrency,
8182
envConcurrencyLimit,
@@ -120,7 +121,7 @@ function DebugRunDataEngineV1({
120121
<Property.Value className="flex items-center gap-2">
121122
<ClipboardField
122123
value={withPrefix(
123-
keys.queueKey(run.runtimeEnvironment, run.queue, run.concurrencyKey ?? undefined)
124+
keys.queueKey(environment, run.queue, run.concurrencyKey ?? undefined)
124125
)}
125126
variant="tertiary/small"
126127
iconButton
@@ -132,7 +133,7 @@ function DebugRunDataEngineV1({
132133
<Property.Value className="flex items-center gap-2">
133134
<ClipboardField
134135
value={`ZRANGE ${withPrefix(
135-
keys.queueKey(run.runtimeEnvironment, run.queue, run.concurrencyKey ?? undefined)
136+
keys.queueKey(environment, run.queue, run.concurrencyKey ?? undefined)
136137
)} 0 -1`}
137138
variant="tertiary/small"
138139
iconButton
@@ -145,7 +146,7 @@ function DebugRunDataEngineV1({
145146
<ClipboardField
146147
value={withPrefix(
147148
keys.queueCurrentConcurrencyKey(
148-
run.runtimeEnvironment,
149+
environment,
149150
run.queue,
150151
run.concurrencyKey ?? undefined
151152
)
@@ -162,7 +163,7 @@ function DebugRunDataEngineV1({
162163
<ClipboardField
163164
value={`SMEMBERS ${withPrefix(
164165
keys.queueCurrentConcurrencyKey(
165-
run.runtimeEnvironment,
166+
environment,
166167
run.queue,
167168
run.concurrencyKey ?? undefined
168169
)
@@ -184,7 +185,7 @@ function DebugRunDataEngineV1({
184185
<ClipboardField
185186
value={withPrefix(
186187
keys.queueReserveConcurrencyKeyFromQueue(
187-
keys.queueKey(run.runtimeEnvironment, run.queue, run.concurrencyKey ?? undefined)
188+
keys.queueKey(environment, run.queue, run.concurrencyKey ?? undefined)
188189
)
189190
)}
190191
variant="tertiary/small"
@@ -199,7 +200,7 @@ function DebugRunDataEngineV1({
199200
<ClipboardField
200201
value={`SMEMBERS ${withPrefix(
201202
keys.queueReserveConcurrencyKeyFromQueue(
202-
keys.queueKey(run.runtimeEnvironment, run.queue, run.concurrencyKey ?? undefined)
203+
keys.queueKey(environment, run.queue, run.concurrencyKey ?? undefined)
203204
)
204205
)}`}
205206
variant="tertiary/small"
@@ -217,7 +218,7 @@ function DebugRunDataEngineV1({
217218
<Property.Label>Queue concurrency limit key</Property.Label>
218219
<Property.Value className="flex items-center gap-2">
219220
<ClipboardField
220-
value={withPrefix(keys.queueConcurrencyLimitKey(run.runtimeEnvironment, run.queue))}
221+
value={withPrefix(keys.queueConcurrencyLimitKey(environment, run.queue))}
221222
variant="tertiary/small"
222223
iconButton
223224
/>
@@ -227,9 +228,7 @@ function DebugRunDataEngineV1({
227228
<Property.Label>GET queue concurrency limit</Property.Label>
228229
<Property.Value className="flex items-center gap-2">
229230
<ClipboardField
230-
value={`GET ${withPrefix(
231-
keys.queueConcurrencyLimitKey(run.runtimeEnvironment, run.queue)
232-
)}`}
231+
value={`GET ${withPrefix(keys.queueConcurrencyLimitKey(environment, run.queue))}`}
233232
variant="tertiary/small"
234233
iconButton
235234
/>
@@ -245,7 +244,7 @@ function DebugRunDataEngineV1({
245244
<Property.Label>Env current concurrency key</Property.Label>
246245
<Property.Value className="flex items-center gap-2">
247246
<ClipboardField
248-
value={withPrefix(keys.envCurrentConcurrencyKey(run.runtimeEnvironment))}
247+
value={withPrefix(keys.envCurrentConcurrencyKey(environment))}
249248
variant="tertiary/small"
250249
iconButton
251250
/>
@@ -255,7 +254,7 @@ function DebugRunDataEngineV1({
255254
<Property.Label>Get env current concurrency</Property.Label>
256255
<Property.Value className="flex items-center gap-2">
257256
<ClipboardField
258-
value={`SMEMBERS ${withPrefix(keys.envCurrentConcurrencyKey(run.runtimeEnvironment))}`}
257+
value={`SMEMBERS ${withPrefix(keys.envCurrentConcurrencyKey(environment))}`}
259258
variant="tertiary/small"
260259
iconButton
261260
/>
@@ -271,7 +270,7 @@ function DebugRunDataEngineV1({
271270
<Property.Label>Env reserve concurrency key</Property.Label>
272271
<Property.Value className="flex items-center gap-2">
273272
<ClipboardField
274-
value={withPrefix(keys.envReserveConcurrencyKey(run.runtimeEnvironment.id))}
273+
value={withPrefix(keys.envReserveConcurrencyKey(environment.id))}
275274
variant="tertiary/small"
276275
iconButton
277276
/>
@@ -281,9 +280,7 @@ function DebugRunDataEngineV1({
281280
<Property.Label>Get env reserve concurrency</Property.Label>
282281
<Property.Value className="flex items-center gap-2">
283282
<ClipboardField
284-
value={`SMEMBERS ${withPrefix(
285-
keys.envReserveConcurrencyKey(run.runtimeEnvironment.id)
286-
)}`}
283+
value={`SMEMBERS ${withPrefix(keys.envReserveConcurrencyKey(environment.id))}`}
287284
variant="tertiary/small"
288285
iconButton
289286
/>
@@ -299,7 +296,7 @@ function DebugRunDataEngineV1({
299296
<Property.Label>Env concurrency limit key</Property.Label>
300297
<Property.Value className="flex items-center gap-2">
301298
<ClipboardField
302-
value={withPrefix(keys.envConcurrencyLimitKey(run.runtimeEnvironment))}
299+
value={withPrefix(keys.envConcurrencyLimitKey(environment))}
303300
variant="tertiary/small"
304301
iconButton
305302
/>
@@ -309,7 +306,7 @@ function DebugRunDataEngineV1({
309306
<Property.Label>GET env concurrency limit</Property.Label>
310307
<Property.Value className="flex items-center gap-2">
311308
<ClipboardField
312-
value={`GET ${withPrefix(keys.envConcurrencyLimitKey(run.runtimeEnvironment))}`}
309+
value={`GET ${withPrefix(keys.envConcurrencyLimitKey(environment))}`}
313310
variant="tertiary/small"
314311
iconButton
315312
/>
@@ -325,7 +322,7 @@ function DebugRunDataEngineV1({
325322
<Property.Label>Shared queue key</Property.Label>
326323
<Property.Value className="flex items-center gap-2">
327324
<ClipboardField
328-
value={`GET ${withPrefix(keys.envSharedQueueKey(run.runtimeEnvironment))}`}
325+
value={`GET ${withPrefix(keys.envSharedQueueKey(environment))}`}
329326
variant="tertiary/small"
330327
iconButton
331328
/>
@@ -336,7 +333,7 @@ function DebugRunDataEngineV1({
336333
<Property.Value className="flex items-center gap-2">
337334
<ClipboardField
338335
value={`ZRANGEBYSCORE ${withPrefix(
339-
keys.envSharedQueueKey(run.runtimeEnvironment)
336+
keys.envSharedQueueKey(environment)
340337
)} -inf ${Date.now()} WITHSCORES`}
341338
variant="tertiary/small"
342339
iconButton

apps/webapp/app/routes/@.runs.$runParam.ts

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { redirect, type LoaderFunctionArgs } from "@remix-run/server-runtime";
22
import { z } from "zod";
33
import { prisma } from "~/db.server";
44
import { runStore } from "~/v3/runStore.server";
5+
import { controlPlaneResolver } from "~/v3/runOpsMigration/controlPlaneResolver.server";
56
import { redirectWithErrorMessage } from "~/models/message.server";
67
import { requireUser } from "~/services/session.server";
78
import { impersonate, rootPath, v3RunPath, v3RunSpanPath } from "~/utils/pathBuilder";
@@ -36,21 +37,7 @@ export async function loader({ params, request }: LoaderFunctionArgs) {
3637
{
3738
select: {
3839
spanId: true,
39-
runtimeEnvironment: {
40-
select: {
41-
slug: true,
42-
},
43-
},
44-
project: {
45-
select: {
46-
slug: true,
47-
organization: {
48-
select: {
49-
slug: true,
50-
},
51-
},
52-
},
53-
},
40+
runtimeEnvironmentId: true,
5441
},
5542
},
5643
prisma
@@ -90,10 +77,18 @@ export async function loader({ params, request }: LoaderFunctionArgs) {
9077
});
9178
}
9279

80+
const environment = await controlPlaneResolver.resolveAuthenticatedEnv(run.runtimeEnvironmentId);
81+
82+
if (!environment) {
83+
return redirectWithErrorMessage(rootPath(), request, "Run doesn't exist", {
84+
ephemeral: false,
85+
});
86+
}
87+
9388
const path = v3RunSpanPath(
94-
{ slug: run.project.organization.slug },
95-
{ slug: run.project.slug },
96-
{ slug: run.runtimeEnvironment.slug },
89+
{ slug: environment.organization.slug },
90+
{ slug: environment.project.slug },
91+
{ slug: environment.slug },
9792
{ friendlyId: runParam },
9893
{ spanId: run.spanId }
9994
);

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,13 @@ import { findProjectBySlug } from "~/models/project.server";
4848
import { findEnvironmentBySlug } from "~/models/runtimeEnvironment.server";
4949
import { type BatchList, BatchListPresenter } from "~/presenters/v3/BatchListPresenter.server";
5050
import { requireUserId } from "~/services/session.server";
51+
import {
52+
$replica,
53+
runOpsNewReplicaClient,
54+
runOpsLegacyReplica,
55+
runOpsSplitReadEnabled,
56+
type PrismaClientOrTransaction,
57+
} from "~/db.server";
5158
import {
5259
docsPath,
5360
EnvironmentParamSchema,
@@ -90,7 +97,12 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
9097
};
9198
const filters = BatchListFilters.parse(s);
9299

93-
const presenter = new BatchListPresenter();
100+
const presenter = new BatchListPresenter(undefined, undefined, {
101+
runOpsNew: runOpsNewReplicaClient as unknown as PrismaClientOrTransaction,
102+
runOpsLegacyReplica: runOpsLegacyReplica as unknown as PrismaClientOrTransaction,
103+
controlPlaneReplica: $replica as unknown as PrismaClientOrTransaction,
104+
splitEnabled: runOpsSplitReadEnabled,
105+
});
94106
const list = await presenter.call({
95107
userId,
96108
projectId: project.id,

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.waitpoints.tokens.$waitpointParam/route.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
4545
}
4646

4747
try {
48-
const presenter = new WaitpointPresenter();
48+
const presenter = new WaitpointPresenter(undefined, undefined, {});
4949
const result = await presenter.call({
5050
friendlyId: waitpointParam,
5151
environmentId: environment.id,

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ import { findProjectBySlug } from "~/models/project.server";
4242
import { findEnvironmentBySlug } from "~/models/runtimeEnvironment.server";
4343
import { WaitpointListPresenter } from "~/presenters/v3/WaitpointListPresenter.server";
4444
import { requireUserId } from "~/services/session.server";
45+
import {
46+
runOpsNewReplicaClient,
47+
runOpsLegacyReplica,
48+
runOpsSplitReadEnabled,
49+
type PrismaClientOrTransaction,
50+
} from "~/db.server";
4551
import { docsPath, EnvironmentParamSchema, v3WaitpointTokenPath } from "~/utils/pathBuilder";
4652

4753
export const meta: MetaFunction = () => {
@@ -88,7 +94,11 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
8894
}
8995

9096
try {
91-
const presenter = new WaitpointListPresenter();
97+
const presenter = new WaitpointListPresenter(undefined, undefined, {
98+
runOpsNew: runOpsNewReplicaClient as unknown as PrismaClientOrTransaction,
99+
runOpsLegacyReplica: runOpsLegacyReplica as unknown as PrismaClientOrTransaction,
100+
splitEnabled: runOpsSplitReadEnabled,
101+
});
92102
const result = await presenter.call({
93103
environment,
94104
...searchParams,

apps/webapp/app/routes/api.v1.batches.$batchId.ts

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { json } from "@remix-run/server-runtime";
22
import { z } from "zod";
3-
import { $replica } from "~/db.server";
43
import { anyResource, createLoaderApiRoute } from "~/services/routeBuilders/apiBuilder.server";
4+
import { runStore } from "~/v3/runStore.server";
55

66
const ParamsSchema = z.object({
77
batchId: z.string(),
@@ -13,14 +13,8 @@ export const loader = createLoaderApiRoute(
1313
allowJWT: true,
1414
corsStrategy: "all",
1515
findResource: (params, auth) => {
16-
return $replica.batchTaskRun.findFirst({
17-
where: {
18-
friendlyId: params.batchId,
19-
runtimeEnvironmentId: auth.environment.id,
20-
},
21-
include: {
22-
errors: true,
23-
},
16+
return runStore.findBatchTaskRunByFriendlyId(params.batchId, auth.environment.id, {
17+
include: { errors: true },
2418
});
2519
},
2620
authorization: {

apps/webapp/app/routes/api.v1.runs.$runFriendlyId.input-streams.wait.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,10 @@ const { action, loader } = createActionApiRoute(
7878
}
7979
}
8080

81-
// Step 1: Create the waitpoint
81+
// Step 1: Create the waitpoint. Co-locate it with the owning run (run-ops split) so a ksuid
82+
// run's input-stream waitpoint lands on the run's DB and its block edge resolves.
8283
const result = await engine.createManualWaitpoint({
84+
runId: run.id,
8385
environmentId: authentication.environment.id,
8486
projectId: authentication.environment.projectId,
8587
idempotencyKey: body.idempotencyKey,

apps/webapp/app/routes/api.v1.runs.$runFriendlyId.session-streams.wait.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,10 @@ const { action, loader } = createActionApiRoute(
9999
}
100100
}
101101

102-
// Step 1: Create the waitpoint.
102+
// Step 1: Create the waitpoint. Co-locate it with the owning run (run-ops split) so a ksuid
103+
// run's session-stream waitpoint lands on the run's DB and its block edge resolves.
103104
const result = await engine.createManualWaitpoint({
105+
runId: run.id,
104106
environmentId: authentication.environment.id,
105107
projectId: authentication.environment.projectId,
106108
idempotencyKey: body.idempotencyKey,

0 commit comments

Comments
 (0)