Skip to content
Draft
Show file tree
Hide file tree
Changes from 64 commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
a86635c
chore(run-store): scaffold @internal/run-store package
d-cs Jun 17, 2026
d4c1ff4
feat(run-store): add shared types and the RunStore interface
d-cs Jun 17, 2026
6d7abab
chore(run-store): use .js extensions in index re-exports for Node16 r…
d-cs Jun 17, 2026
010cf17
feat(run-store): add NoopRunStore test double
d-cs Jun 17, 2026
72a7462
feat(run-store): add PostgresRunStore with createRun
d-cs Jun 17, 2026
2e63223
feat(run-store): implement createCancelledRun and createFailedRun
d-cs Jun 17, 2026
f8456c1
feat(run-store): implement attempt lifecycle, cancel, and fail methods
d-cs Jun 17, 2026
f1ab6ae
feat(run-store): implement expiry, dequeue-lock, version, and checkpo…
d-cs Jun 17, 2026
f66bbad
feat(run-store): implement reschedule, debounce, metadata, idempotenc…
d-cs Jun 17, 2026
56ec707
feat(run-store): wire RunStore into run-engine SystemResources and we…
d-cs Jun 17, 2026
01bbc67
fix(run-store): align create-input types with the columns callers act…
d-cs Jun 17, 2026
de52aaa
refactor(run-engine): route run creation through RunStore
d-cs Jun 17, 2026
4826117
fix(run-store): allow optional machinePreset in recordRetryOutcome (l…
d-cs Jun 17, 2026
8650e40
refactor(run-engine): route attempt lifecycle, cancel, and fail write…
d-cs Jun 17, 2026
d530eb1
refactor(run-engine): route expiry and dequeue-lock writes through Ru…
d-cs Jun 17, 2026
4ec5aab
fix(run-store): allow undefined maxDurationInSeconds in lockRunToWork…
d-cs Jun 17, 2026
109c6a7
refactor(run-engine): route checkpoint, delayed, pending-version, and…
d-cs Jun 17, 2026
2fbdc5d
refactor(webapp): route run metadata, idempotency-key, and reschedule…
d-cs Jun 17, 2026
1a5ccdc
refactor(webapp): route tag and realtime-stream appends through RunStore
d-cs Jun 17, 2026
60565cf
fix(run-store): short-circuit expireRunsBatch on an empty runIds array
d-cs Jun 18, 2026
3c22b32
Merge main into run-store-write-adapter
d-cs Jun 18, 2026
76f3494
fix(webapp): inject runStore into UpdateMetadataService
d-cs Jun 18, 2026
c5226a2
feat(run-store): add TaskRun read methods to the run store
d-cs Jun 18, 2026
13d5364
feat(run-store): add full-row read overload to the run store
d-cs Jun 18, 2026
cfa9052
refactor(run-engine): route TaskRun reads through the run store
d-cs Jun 18, 2026
5b74b48
refactor(webapp): route service-layer TaskRun reads through the run s…
d-cs Jun 18, 2026
5683952
refactor(webapp): route presenter TaskRun reads through the run store
d-cs Jun 18, 2026
126b05f
refactor(webapp): route API and loader TaskRun reads through the run …
d-cs Jun 18, 2026
f59abe7
refactor(webapp): hydrate parent-model TaskRun reads through the run …
d-cs Jun 18, 2026
cb12430
chore(scripts): flag recover-stuck-runs raw TaskRun read for table cu…
d-cs Jun 18, 2026
ae57f25
chore(webapp): add server-changes entry for run-store read routing
d-cs Jun 18, 2026
fcc26d4
test(webapp): mock db.server in the new run-store read tests
d-cs Jun 18, 2026
789e107
test(webapp): drop the cancelTaskAttemptDependencies container test
d-cs Jun 18, 2026
e20e451
Merge branch 'main' into runstore-read-path
d-cs Jun 19, 2026
650a081
feat(core): add KSUID run-id minting and an isKsuidId discriminator
d-cs Jun 19, 2026
40aea1b
feat(database): add the task_run_v2 table
d-cs Jun 19, 2026
72af7aa
feat(database): drop incoming foreign keys referencing TaskRun
d-cs Jun 19, 2026
1e60662
feat(database): mirror TaskRun relations on TaskRunV2
d-cs Jun 19, 2026
0a591fb
test(testcontainers): strip run foreign keys after schema push
d-cs Jun 19, 2026
f8c1a04
feat(run-store): route run reads and writes by id format
d-cs Jun 19, 2026
e174341
feat(run-store): both-table merged keyset cursor for findRuns
d-cs Jun 19, 2026
37b7f97
fix(webapp): read runs across both run tables with a time keyset
d-cs Jun 19, 2026
658b385
feat(run-store): read non-id predicates across both run tables
d-cs Jun 19, 2026
47610ee
feat(webapp): per-org cutover flag for the v2 run table
d-cs Jun 19, 2026
837ee01
Merge branch 'main' into runstore-read-path
d-cs Jun 19, 2026
912a504
feat(replication): co-publish additional tables and reconcile existin…
d-cs Jun 19, 2026
3549341
feat(webapp): stream task_run_v2 into ClickHouse
d-cs Jun 19, 2026
7732861
Merge branch 'main' into runstore-read-path
d-cs Jun 20, 2026
435e895
Merge branch 'runstore-read-path' into runstore-table-redirect
d-cs Jun 20, 2026
4410999
test(webapp): de-flake the task_run_v2 replication streaming test
d-cs Jun 22, 2026
e72d9fb
Merge branch 'main' into runstore-table-redirect
d-cs Jun 22, 2026
fd06ef4
fix(run-store): guard findRuns skip and skip the non-candidate table …
d-cs Jun 22, 2026
5ebea98
fix(run-store): correct single-table findRuns ordering and cross-tabl…
d-cs Jun 22, 2026
6a2b4e3
fix(webapp): serialise idempotency claims for v2-cutover orgs
d-cs Jun 22, 2026
b925f25
fix(database,replication): pin task_run_v2 REPLICA IDENTITY FULL and …
d-cs Jun 22, 2026
5282e01
fix(webapp): resolve cross-table run parent/root/children in presenters
d-cs Jun 22, 2026
24b0f87
fix(run-store): prefer task_run_v2 on cross-table single-row reads
d-cs Jun 22, 2026
df8a7a8
fix(database): drop unused task_run_v2 m2m relations
d-cs Jun 22, 2026
18a67c2
fix(run-store): guard cross-table cursor/take and route plain id reads
d-cs Jun 22, 2026
f641091
fix(webapp): back idempotency claims with Redis when the mollifier is…
d-cs Jun 22, 2026
388dd66
fix(webapp): serve realtime run feeds across both run tables
d-cs Jun 22, 2026
eeb1079
fix(webapp): lock runTableV2 on the global flags page
d-cs Jun 22, 2026
3d4ca9e
fix(webapp): scope cross-table run hierarchy hydration to the environ…
d-cs Jun 22, 2026
59dd560
fix(webapp): swallow the aborted sibling fetch in the realtime merge
d-cs Jun 22, 2026
59866a9
fix(webapp): harden the realtime merge against orphaned fetch rejections
d-cs Jun 22, 2026
e44af57
Merge remote-tracking branch 'origin/main' into runstore-table-redirect
d-cs Jun 22, 2026
0084704
fix(webapp): gate runTableV2 on native realtime instead of merging El…
d-cs Jun 22, 2026
760c24c
fix(webapp): gate runTableV2 on the native realtime backend
d-cs Jun 22, 2026
c4d8c4b
fix(webapp): serve task_run_v2 runs over Electric realtime
d-cs Jun 22, 2026
ef54cb9
fix(webapp,run-engine): close cross-table gaps in the task_run_v2 mix…
d-cs Jun 22, 2026
3218843
test(run-engine): cover cross-table cancel cascade in the task_run_v2…
d-cs Jun 22, 2026
e64e950
fix(run-store): reject findRuns take without orderBy across both run …
d-cs Jun 22, 2026
8ee83c5
test(run-store): drop obsolete findRuns take-without-orderBy cap test
d-cs Jun 22, 2026
0143ade
fix(webapp): close pass-2 cross-table gaps (span-detail 500, one-time…
d-cs Jun 22, 2026
5f14bf3
fix(webapp): gate runTableV2 on native realtime and drop the Electric…
d-cs Jun 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 24 additions & 10 deletions apps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
} from "~/v3/mollifier/readFallback.server";
import { generatePresignedUrl } from "~/v3/objectStore.server";
import { runStore } from "~/v3/runStore.server";
import { hydrateParentAndRoot, hydrateChildRuns } from "~/v3/runHierarchy.server";
import { tracer } from "~/v3/tracer.server";
import { startSpanWithEnv } from "~/v3/tracing.server";

Expand Down Expand Up @@ -133,21 +134,34 @@ export class ApiRetrieveRunPresenter {
attemptNumber: true,
engine: true,
taskEventStore: true,
parentTaskRun: {
select: commonRunSelect,
},
rootTaskRun: {
select: commonRunSelect,
},
childRuns: {
select: commonRunSelect,
},
parentTaskRunId: true,
rootTaskRunId: true,
},
},
$replica
);

if (pgRow) return { ...pgRow, isBuffered: false };
if (pgRow) {
// Resolve parent/root/children across both run tables. A single Prisma
// relation select is table-bound, so a v2 run's legacy parent (or a
// legacy run's v2 children), which arise in the mixed window, would come
// back null/empty. Resolve parent/root by id (RunStore routes by format)
// and children by a both-table predicate.
const { parentTaskRun, rootTaskRun } = await hydrateParentAndRoot(
{ parentTaskRunId: pgRow.parentTaskRunId, rootTaskRunId: pgRow.rootTaskRunId },
{ runtimeEnvironmentId: env.id },
commonRunSelect,
$replica
);
const childRuns = await hydrateChildRuns(
pgRow.id,
{ runtimeEnvironmentId: env.id },
commonRunSelect,
$replica
);

return { ...pgRow, parentTaskRun, rootTaskRun, childRuns, isBuffered: false };
}

// Postgres miss → fall back to the mollifier buffer. When the gate
// diverted a trigger, the run lives in Redis until the drainer replays
Expand Down
33 changes: 16 additions & 17 deletions apps/webapp/app/presenters/v3/RunPresenter.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { isFinalRunStatus } from "~/v3/taskStatus";
import { env } from "~/env.server";
import { getEventRepositoryForStore } from "~/v3/eventRepository/index.server";
import { runStore } from "~/v3/runStore.server";
import { hydrateParentAndRoot } from "~/v3/runHierarchy.server";

type Result = Awaited<ReturnType<RunPresenter["call"]>>;
export type Run = Result["run"];
Expand Down Expand Up @@ -93,20 +94,8 @@ export class RunPresenter {
completedAt: true,
logsDeletedAt: true,
annotations: true,
rootTaskRun: {
select: {
friendlyId: true,
spanId: true,
createdAt: true,
},
},
parentTaskRun: {
select: {
friendlyId: true,
spanId: true,
createdAt: true,
},
},
rootTaskRunId: true,
parentTaskRunId: true,
runtimeEnvironment: {
select: {
id: true,
Expand Down Expand Up @@ -143,6 +132,16 @@ export class RunPresenter {

const showLogs = showDeletedLogs || !run.logsDeletedAt;

// Resolve parent/root across both physical run tables: a v2 run can have a
// legacy parent/root (or vice versa) in the mixed window, which a
// table-bound Prisma relation select would miss.
const { parentTaskRun, rootTaskRun } = await hydrateParentAndRoot(
{ parentTaskRunId: run.parentTaskRunId, rootTaskRunId: run.rootTaskRunId },
{ runtimeEnvironmentId: run.runtimeEnvironment.id },
{ friendlyId: true, spanId: true, createdAt: true },
this.#prismaClient
);

const runData = {
id: run.id,
number: run.number,
Expand All @@ -154,8 +153,8 @@ export class RunPresenter {
startedAt: run.startedAt,
completedAt: run.completedAt,
logsDeletedAt: showDeletedLogs ? null : run.logsDeletedAt,
rootTaskRun: run.rootTaskRun,
parentTaskRun: run.parentTaskRun,
rootTaskRun,
parentTaskRun,
environment: {
id: run.runtimeEnvironment.id,
organizationId: run.runtimeEnvironment.organizationId,
Expand Down Expand Up @@ -184,7 +183,7 @@ export class RunPresenter {
getTaskEventStoreTableForRun(run),
run.runtimeEnvironment.id,
run.traceId,
run.rootTaskRun?.createdAt ?? run.createdAt,
rootTaskRun?.createdAt ?? run.createdAt,
run.completedAt ?? undefined,
{ includeDebugLogs: showDebug }
);
Expand Down
45 changes: 28 additions & 17 deletions apps/webapp/app/presenters/v3/SpanPresenter.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -587,22 +587,9 @@ export class SpanPresenter extends BasePresenter {
filePath: true,
},
},
//relationships
rootTaskRun: {
select: {
taskIdentifier: true,
friendlyId: true,
spanId: true,
createdAt: true,
},
},
parentTaskRun: {
select: {
taskIdentifier: true,
friendlyId: true,
spanId: true,
},
},
//relationships (resolved across both run tables after the fetch)
rootTaskRunId: true,
parentTaskRunId: true,
batch: {
select: {
friendlyId: true,
Expand All @@ -626,7 +613,31 @@ export class SpanPresenter extends BasePresenter {
this._replica
);

return run;
if (!run) {
return run;
}

// Resolve parent/root across both run tables: a v2 run can reference a
// legacy parent/root (or vice versa) in the mixed window, which a
// table-bound Prisma relation select on a single table would miss.
const [parentTaskRun, rootTaskRun] = await Promise.all([
run.parentTaskRunId
? runStore.findRun(
{ id: run.parentTaskRunId, runtimeEnvironmentId: environmentId },
{ select: { taskIdentifier: true, friendlyId: true, spanId: true } },
this._replica
)
: Promise.resolve(null),
run.rootTaskRunId
? runStore.findRun(
{ id: run.rootTaskRunId, runtimeEnvironmentId: environmentId },
{ select: { taskIdentifier: true, friendlyId: true, spanId: true, createdAt: true } },
this._replica
)
: Promise.resolve(null),
]);
Comment thread
coderabbitai[bot] marked this conversation as resolved.

return { ...run, parentTaskRun, rootTaskRun };
}

async #getSpan({
Expand Down
Loading