11import type { RunOpsPrismaClient } from "@internal/run-ops-database" ;
22import type { BatchTaskRunExecutionResult } from "@trigger.dev/core/v3" ;
3+ import { ownerEngine } from "@trigger.dev/core/v3/isomorphic" ;
34import {
45 $replica ,
56 type PrismaClientOrTransaction ,
@@ -10,7 +11,6 @@ import {
1011import type { TaskRunWithAttempts } from "~/models/taskRun.server" ;
1112import { executionResultForTaskRun } from "~/models/taskRun.server" ;
1213import type { AuthenticatedEnvironment } from "~/services/apiAuth.server" ;
13- import { readThroughRun } from "~/v3/runOpsMigration/readThrough.server" ;
1414import { runStore as defaultRunStore } from "~/v3/runStore.server" ;
1515import { BasePresenter } from "./basePresenter.server" ;
1616
@@ -45,11 +45,14 @@ const memberRunSelect = {
4545} as const ;
4646
4747/**
48- * Split on: the batch row + its item rows resolve new-run-ops first, then the LEGACY RUN-OPS
49- * READ REPLICA ONLY (never the legacy primary — there is no such handle); each member run is
50- * hydrated independently via readThroughRun keyed on the member runId, so a batch whose members
51- * span migrated + abandoned runs returns the complete reachable set (the batch-spanning-the-line
52- * read; the dangling-reference termination gate is a separate, adjacent unit).
48+ * Split on: the batch row + its members resolve new-run-ops first, then the LEGACY RUN-OPS READ
49+ * REPLICA ONLY (never the legacy primary — there is no such handle). Members hydrate via ONE
50+ * grouped read against `newClient` for the whole id set, then ONE grouped read against
51+ * `legacyReplica` for just the misses that could still be legacy-resident — the same
52+ * residency-partitioned shape as the old per-member read-through, but batched instead of fanned
53+ * out one query per member. A batch whose members span migrated + abandoned runs returns the
54+ * complete reachable set (the batch-spanning-the-line read; the dangling-reference termination
55+ * gate is a separate, adjacent unit).
5356 *
5457 * Split off (single-DB / self-host): one passthrough read for the batch row + a single store
5558 * id-set hydrate for the members — no legacy read, no known-migrated probe, no second connection.
@@ -136,7 +139,7 @@ export class ApiBatchResultsPresenter extends BasePresenter {
136139 // Split: resolve the batch row new-first then off the legacy READ REPLICA only (a batch id may
137140 // be cuid or run-ops id, and a cuid-shaped id can still have been backfilled onto NEW, so id-shape
138141 // residency is not authoritative for the row — the new-first-then-legacy probe is), then
139- // hydrate every member run independently via the per-run read-through primitive .
142+ // hydrate every member run in ONE grouped new-then-legacy read .
140143 async #callSplit(
141144 friendlyId : string ,
142145 env : AuthenticatedEnvironment
@@ -181,45 +184,35 @@ export class ApiBatchResultsPresenter extends BasePresenter {
181184 } ;
182185 }
183186
184- // Per-member fan-out: each member may live on a different DB, so a single nested include cannot
185- // cross the seam. Promise.all preserves batchRun.items order, unchanged from today.
186- const memberResults = await Promise . all (
187- batchRun . items . map ( async ( item ) => {
188- const result = await readThroughRun < TaskRunWithAttempts > ( {
189- runId : item . taskRunId ,
190- environmentId : env . id ,
191- readNew : ( client ) =>
192- client . taskRun . findFirst ( {
193- // runops-routed-ok: per-member readThrough fan-out
194- where : { id : item . taskRunId } ,
195- select : memberRunSelect ,
196- } ) as Promise < TaskRunWithAttempts | null > ,
197- readLegacy : ( replica ) =>
198- replica . taskRun . findFirst ( {
199- // runops-routed-ok: per-member readThrough fan-out
200- where : { id : item . taskRunId } ,
201- select : memberRunSelect ,
202- } ) as Promise < TaskRunWithAttempts | null > ,
203- deps : {
204- splitEnabled : true ,
205- // Pass the SAME resolved handles the batch row used, so the batch row and its members
206- // never resolve against different DBs. (Letting these fall through to readThroughRun's
207- // own module-level defaults would diverge from the batch read's resolved handles.)
208- newClient,
209- legacyReplica,
210- isPastRetention : this . readThrough ?. isPastRetention ,
211- } ,
212- } ) ;
187+ const taskRunIds = batchRun . items . map ( ( item ) => item . taskRunId ) ;
213188
214- // not-found / past-retention members are omitted (matches today's drop-undefined behavior);
215- // the dangling-reference termination gate (separate unit) governs whether that's permitted.
216- if ( result . source === "not-found" || result . source === "past-retention" ) {
217- return undefined ;
218- }
189+ const newRows = ( await newClient . taskRun . findMany ( {
190+ where : { id : { in : taskRunIds } } ,
191+ select : memberRunSelect ,
192+ } ) ) as TaskRunWithAttempts [ ] ;
193+ const runsById = new Map ( newRows . map ( ( run ) => [ run . id , run ] ) ) ;
219194
220- return executionResultForTaskRun ( result . value ) ;
221- } )
195+ // A run-ops id can only live on NEW, so only misses that AREN'T run-ops-shaped are candidates
196+ // for the legacy probe — mirrors readThroughRun's per-id "NEW residency skips legacy" rule.
197+ const legacyCandidateIds = taskRunIds . filter (
198+ ( id ) => ! runsById . has ( id ) && ownerEngine ( id ) !== "NEW"
222199 ) ;
200+ if ( legacyCandidateIds . length > 0 ) {
201+ const legacyRows = ( await legacyReplica . taskRun . findMany ( {
202+ where : { id : { in : legacyCandidateIds } } ,
203+ select : memberRunSelect ,
204+ } ) ) as TaskRunWithAttempts [ ] ;
205+ for ( const run of legacyRows ) {
206+ runsById . set ( run . id , run ) ;
207+ }
208+ }
209+
210+ // not-found members are omitted (matches today's drop-undefined behavior); the
211+ // dangling-reference termination gate (separate unit) governs whether that's permitted.
212+ const memberResults = batchRun . items . map ( ( item ) => {
213+ const run = runsById . get ( item . taskRunId ) ;
214+ return run ? executionResultForTaskRun ( run ) : undefined ;
215+ } ) ;
223216
224217 return {
225218 id : batchRun . friendlyId ,
0 commit comments