@shipfox/api-runners 12.6.0 → 13.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +4 -4
- package/CHANGELOG.md +20 -0
- package/dist/db/index.d.ts +1 -1
- package/dist/db/index.d.ts.map +1 -1
- package/dist/db/index.js +1 -1
- package/dist/db/index.js.map +1 -1
- package/dist/db/job-executions.d.ts +8 -21
- package/dist/db/job-executions.d.ts.map +1 -1
- package/dist/db/job-executions.js +63 -60
- package/dist/db/job-executions.js.map +1 -1
- package/dist/db/provisioner-tokens.d.ts.map +1 -1
- package/dist/db/provisioner-tokens.js +14 -2
- package/dist/db/provisioner-tokens.js.map +1 -1
- package/dist/db/reservations.d.ts +10 -0
- package/dist/db/reservations.d.ts.map +1 -1
- package/dist/db/reservations.js +139 -17
- package/dist/db/reservations.js.map +1 -1
- package/dist/db/runner-assignments.d.ts.map +1 -1
- package/dist/db/runner-assignments.js +8 -5
- package/dist/db/runner-assignments.js.map +1 -1
- package/dist/db/runner-instances.d.ts +0 -1
- package/dist/db/runner-instances.d.ts.map +1 -1
- package/dist/db/runner-instances.js +85 -100
- package/dist/db/runner-instances.js.map +1 -1
- package/dist/db/runner-states.d.ts +3 -1
- package/dist/db/runner-states.d.ts.map +1 -1
- package/dist/db/runner-states.js +6 -4
- package/dist/db/runner-states.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -4
- package/dist/index.js.map +1 -1
- package/dist/presentation/index.d.ts +2 -1
- package/dist/presentation/index.d.ts.map +1 -1
- package/dist/presentation/index.js +2 -1
- package/dist/presentation/index.js.map +1 -1
- package/dist/presentation/inter-module.d.ts +0 -1
- package/dist/presentation/inter-module.d.ts.map +1 -1
- package/dist/presentation/inter-module.js +2 -25
- package/dist/presentation/inter-module.js.map +1 -1
- package/dist/presentation/subscribers/on-workflows-job-execution-queued.d.ts +3 -0
- package/dist/presentation/subscribers/on-workflows-job-execution-queued.d.ts.map +1 -0
- package/dist/presentation/subscribers/on-workflows-job-execution-queued.js +21 -0
- package/dist/presentation/subscribers/on-workflows-job-execution-queued.js.map +1 -0
- package/dist/presentation/subscribers/on-workflows-job-execution-terminated.d.ts +3 -0
- package/dist/presentation/subscribers/on-workflows-job-execution-terminated.d.ts.map +1 -0
- package/dist/presentation/subscribers/{on-workflows-job-execution-timed-out.js → on-workflows-job-execution-terminated.js} +5 -4
- package/dist/presentation/subscribers/on-workflows-job-execution-terminated.js.map +1 -0
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/src/db/index.ts +0 -2
- package/src/db/job-executions.test.ts +106 -217
- package/src/db/job-executions.ts +71 -70
- package/src/db/provisioner-tokens.test.ts +19 -0
- package/src/db/provisioner-tokens.ts +13 -2
- package/src/db/reservations.test.ts +138 -0
- package/src/db/reservations.ts +314 -35
- package/src/db/runner-assignments.test.ts +20 -0
- package/src/db/runner-assignments.ts +6 -7
- package/src/db/runner-instances.test.ts +350 -15
- package/src/db/runner-instances.ts +136 -198
- package/src/db/runner-states.test.ts +16 -0
- package/src/db/runner-states.ts +9 -4
- package/src/index.test.ts +1 -0
- package/src/index.ts +8 -7
- package/src/presentation/index.ts +2 -1
- package/src/presentation/inter-module.ts +2 -40
- package/src/presentation/subscribers/on-workflows-job-execution-queued.test.ts +57 -0
- package/src/presentation/subscribers/on-workflows-job-execution-queued.ts +26 -0
- package/src/presentation/subscribers/on-workflows-job-execution-terminated.test.ts +142 -0
- package/src/presentation/subscribers/{on-workflows-job-execution-timed-out.ts → on-workflows-job-execution-terminated.ts} +5 -4
- package/test/factories/pending-job.ts +3 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/dist/presentation/subscribers/on-workflows-job-execution-timed-out.d.ts +0 -3
- package/dist/presentation/subscribers/on-workflows-job-execution-timed-out.d.ts.map +0 -1
- package/dist/presentation/subscribers/on-workflows-job-execution-timed-out.js.map +0 -1
- package/src/presentation/inter-module.test.ts +0 -15
- package/src/presentation/subscribers/on-workflows-job-execution-timed-out.test.ts +0 -163
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/db/runner-assignments.ts"],"sourcesContent":["import {and, eq, inArray, isNull, ne, notInArray, or, sql} from 'drizzle-orm';\nimport {\n ReservationExpiredError,\n ReservationNotFoundError,\n RunnerInstanceAlreadyAssignedError,\n RunnerInstanceNotAssignableError,\n} from '#core/errors.js';\nimport {\n type ProviderRunnerAssignmentObservation,\n type RunnerAssignmentSurface,\n recordProviderRunnerControlSessionToAssignment,\n} from '#metrics/instance.js';\nimport type {Tx} from './db.js';\nimport {db} from './db.js';\nimport {lockRunnerReservationAdvisoryKeysTx} from './reservation-locks.js';\nimport {terminalStates} from './runner-states.js';\nimport {reservations} from './schema/reservations.js';\nimport {runnerControlSessions} from './schema/runner-control-sessions.js';\nimport {providerRunners} from './schema/runner-instances.js';\n\n/** Atomically consumes reservation units by writing the immutable assignment on each runner. */\nexport async function assignRunnerInstances(params: {\n provisionerId: string;\n reservationId: string;\n runnerInstanceIds: string[];\n}): Promise<string[]> {\n const result = await db().transaction(async (tx) =>\n assignRunnerInstancesTx(tx, {...params, surface: 'provisioner'}),\n );\n for (const observation of result.controlSessionToAssignment)\n recordProviderRunnerControlSessionToAssignment(observation);\n return result.runnerInstanceIds;\n}\n\ninterface AssignRunnerInstancesTxResult {\n runnerInstanceIds: string[];\n controlSessionToAssignment: ProviderRunnerAssignmentObservation[];\n}\n\nexport async function assignRunnerInstancesTx(\n tx: Tx,\n params: {\n provisionerId: string;\n reservationId: string;\n runnerInstanceIds: string[];\n surface: RunnerAssignmentSurface;\n },\n): Promise<AssignRunnerInstancesTxResult> {\n const runnerInstanceIds = [...params.runnerInstanceIds].sort();\n await lockRunnerReservationAdvisoryKeysTx(tx, {\n provisionerId: params.provisionerId,\n reservationIds: [params.reservationId],\n });\n const runnerRows = await tx\n .select({\n id: providerRunners.id,\n reservationId: providerRunners.reservationId,\n intendedReservationId: providerRunners.intendedReservationId,\n workspaceId: providerRunners.workspaceId,\n assignedAt: providerRunners.assignedAt,\n providerRunnerId: providerRunners.providerRunnerId,\n provider: providerRunners.providerKind,\n launchKind: providerRunners.launchKind,\n labels: providerRunners.labels,\n state: providerRunners.state,\n })\n .from(providerRunners)\n .where(\n and(\n eq(providerRunners.provisionerId, params.provisionerId),\n inArray(providerRunners.id, runnerInstanceIds),\n ),\n )\n .for('update');\n\n // The assignment outlives its short reservation row. A retry after maintenance\n // cleanup is complete when every owned runner already carries the committed assignment.\n if (\n runnerInstanceIds.length > 0 &&\n runnerRows.length === runnerInstanceIds.length &&\n runnerRows.every(\n (runner) =>\n runner.reservationId === params.reservationId &&\n runner.assignedAt !== null &&\n runner.workspaceId !== null,\n )\n ) {\n await tx\n .update(providerRunners)\n .set({intendedReservationId: null, updatedAt: sql`now()`})\n .where(\n and(\n eq(providerRunners.provisionerId, params.provisionerId),\n inArray(providerRunners.id, runnerInstanceIds),\n ),\n );\n return {\n runnerInstanceIds: params.runnerInstanceIds,\n controlSessionToAssignment: [],\n };\n }\n\n const [reservation] = await tx\n .select()\n .from(reservations)\n .where(\n and(\n eq(reservations.id, params.reservationId),\n eq(reservations.provisionerId, params.provisionerId),\n eq(reservations.kind, 'launch'),\n ),\n )\n .limit(1)\n .for('update');\n if (!reservation) throw new ReservationNotFoundError(params.reservationId);\n if (reservation.expiresAt <= new Date()) throw new ReservationExpiredError(params.reservationId);\n if (runnerRows.length !== runnerInstanceIds.length)\n throw new RunnerInstanceNotAssignableError(runnerInstanceIds[0] ?? '', 'runner-not-found');\n\n const activeControlSessions = await tx\n .select({\n runnerInstanceId: runnerControlSessions.runnerInstanceId,\n createdAt: runnerControlSessions.createdAt,\n })\n .from(runnerControlSessions)\n .where(\n and(\n inArray(\n runnerControlSessions.runnerInstanceId,\n runnerRows.map((runner) => runner.id),\n ),\n isNull(runnerControlSessions.closedAt),\n ),\n );\n const runnerInstanceIdsWithControlSession = new Set(\n activeControlSessions.map((session) => session.runnerInstanceId),\n );\n const controlSessionCreatedAtByRunner = new Map(\n activeControlSessions.map((session) => [session.runnerInstanceId, session.createdAt]),\n );\n const runners = runnerRows.map((runner) => ({\n ...runner,\n controlSessionId: runnerInstanceIdsWithControlSession.has(runner.id) ? runner.id : null,\n controlSessionCreatedAt: controlSessionCreatedAtByRunner.get(runner.id) ?? null,\n }));\n\n const alreadyAssigned = runners.filter((runner) => runner.reservationId !== null);\n if (alreadyAssigned.some((runner) => runner.reservationId !== reservation.id))\n throw new RunnerInstanceAlreadyAssignedError(alreadyAssigned[0]?.id ?? '');\n const newRunners = runners.filter(\n (runner) =>\n runner.reservationId === null ||\n (runner.reservationId === reservation.id &&\n (runner.assignedAt === null || runner.workspaceId === null)),\n );\n const newRunnerIds = newRunners.map((runner) => runner.id);\n const assignedCount = await tx\n .select({count: sql<number>`count(*)::int`})\n .from(providerRunners)\n .where(\n and(\n eq(providerRunners.provisionerId, params.provisionerId),\n notInArray(providerRunners.id, newRunnerIds),\n or(\n and(\n eq(providerRunners.reservationId, reservation.id),\n isNull(providerRunners.reservationReleasedAt),\n ),\n and(\n eq(providerRunners.intendedReservationId, reservation.id),\n isNull(providerRunners.reservationReleasedAt),\n notInArray(providerRunners.state, [...terminalStates]),\n ),\n ),\n ),\n );\n if ((assignedCount[0]?.count ?? 0) + newRunners.length > reservation.count)\n throw new RunnerInstanceNotAssignableError(newRunners[0]?.id ?? '', 'capacity-exhausted');\n for (const runner of newRunners) {\n if (runner.state !== 'running')\n throw new RunnerInstanceNotAssignableError(runner.id, 'runner-not-running');\n if (!runner.providerRunnerId)\n throw new RunnerInstanceNotAssignableError(runner.id, 'provider-identity-missing');\n if (!runner.controlSessionId)\n throw new RunnerInstanceNotAssignableError(runner.id, 'control-session-not-active');\n if (!reservation.requiredLabels.every((label) => runner.labels.includes(label)))\n throw new RunnerInstanceNotAssignableError(runner.id, 'labels-mismatch');\n }\n if (newRunners.length > 0) {\n const assignedRows = await tx\n .update(providerRunners)\n .set({\n workspaceId: reservation.workspaceId,\n reservationId: reservation.id,\n intendedReservationId: null,\n assignedAt: sql`now()`,\n updatedAt: sql`now()`,\n })\n .where(\n inArray(\n providerRunners.id,\n newRunners.map((runner) => runner.id),\n ),\n )\n .returning({id: providerRunners.id, assignedAt: providerRunners.assignedAt});\n const runnersById = new Map(newRunners.map((runner) => [runner.id, runner]));\n const controlSessionToAssignment: ProviderRunnerAssignmentObservation[] = [];\n for (const assigned of assignedRows) {\n const runner = runnersById.get(assigned.id);\n const controlSessionCreatedAt = runner?.controlSessionCreatedAt;\n if (!runner || !assigned.assignedAt || !controlSessionCreatedAt) continue;\n controlSessionToAssignment.push({\n durationMilliseconds: assigned.assignedAt.getTime() - controlSessionCreatedAt.getTime(),\n provider: runner.provider,\n launchKind: runner.launchKind,\n surface: params.surface,\n runnerInstanceId: runner.id,\n });\n }\n return {\n runnerInstanceIds: params.runnerInstanceIds,\n controlSessionToAssignment,\n };\n }\n return {\n runnerInstanceIds: params.runnerInstanceIds,\n controlSessionToAssignment: [],\n };\n}\n\nexport type RunnerReservationCapacityFailureReason =\n | 'reservation-not-found'\n | 'reservation-kind-mismatch'\n | 'reservation-expired'\n | 'capacity-exhausted';\n\nexport interface RunnerReservationCapacityValidation {\n acceptedByReservation: Map<string, number>;\n unavailableByReservation: Map<\n string,\n {reason: RunnerReservationCapacityFailureReason; count: number}\n >;\n}\n\nexport async function validateRunnerReservationCapacityTx(\n tx: Tx,\n params: {\n provisionerId: string;\n requests: readonly {reservationId: string; count: number}[];\n },\n options: {advisoryLocksHeld?: boolean} = {},\n): Promise<RunnerReservationCapacityValidation> {\n const requestedByReservation = new Map<string, number>();\n for (const request of params.requests) {\n if (request.count <= 0) continue;\n requestedByReservation.set(\n request.reservationId,\n (requestedByReservation.get(request.reservationId) ?? 0) + request.count,\n );\n }\n const reservationIds = [...requestedByReservation.keys()].sort();\n if (reservationIds.length === 0)\n return {acceptedByReservation: new Map(), unavailableByReservation: new Map()};\n\n if (!options.advisoryLocksHeld)\n await lockRunnerReservationAdvisoryKeysTx(tx, {\n provisionerId: params.provisionerId,\n reservationIds,\n });\n\n const reservationRows = await tx\n .select({\n id: reservations.id,\n count: reservations.count,\n expiresAt: reservations.expiresAt,\n isExpired: sql<boolean>`${reservations.expiresAt} <= now()`,\n })\n .from(reservations)\n .where(\n and(\n eq(reservations.provisionerId, params.provisionerId),\n inArray(reservations.id, reservationIds),\n eq(reservations.kind, 'launch'),\n ),\n )\n .for('update');\n const nonLaunchReservationRows = await tx\n .select({id: reservations.id})\n .from(reservations)\n .where(\n and(\n eq(reservations.provisionerId, params.provisionerId),\n inArray(reservations.id, reservationIds),\n ne(reservations.kind, 'launch'),\n ),\n )\n .for('update');\n const reservationsById = new Map(\n reservationRows.map((reservation) => [reservation.id, reservation]),\n );\n const nonLaunchReservationIds = new Set(\n nonLaunchReservationRows.map((reservation) => reservation.id),\n );\n const requestedReservationIds = new Set(reservationIds);\n const usedRunnerIdsByReservation = new Map<string, Set<string>>();\n const usedRunnerRows = await tx\n .select({\n id: providerRunners.id,\n reservationId: providerRunners.reservationId,\n intendedReservationId: providerRunners.intendedReservationId,\n state: providerRunners.state,\n })\n .from(providerRunners)\n .where(\n and(\n eq(providerRunners.provisionerId, params.provisionerId),\n isNull(providerRunners.reservationReleasedAt),\n or(\n inArray(providerRunners.reservationId, reservationIds),\n and(\n inArray(providerRunners.intendedReservationId, reservationIds),\n notInArray(providerRunners.state, [...terminalStates]),\n ),\n ),\n ),\n );\n for (const runner of usedRunnerRows) {\n if (runner.reservationId && requestedReservationIds.has(runner.reservationId)) {\n const usedRunnerIds =\n usedRunnerIdsByReservation.get(runner.reservationId) ?? new Set<string>();\n usedRunnerIds.add(runner.id);\n usedRunnerIdsByReservation.set(runner.reservationId, usedRunnerIds);\n }\n if (\n runner.intendedReservationId &&\n requestedReservationIds.has(runner.intendedReservationId) &&\n !terminalStates.includes(runner.state as (typeof terminalStates)[number])\n ) {\n const usedRunnerIds =\n usedRunnerIdsByReservation.get(runner.intendedReservationId) ?? new Set<string>();\n usedRunnerIds.add(runner.id);\n usedRunnerIdsByReservation.set(runner.intendedReservationId, usedRunnerIds);\n }\n }\n\n const acceptedByReservation = new Map<string, number>();\n const unavailableByReservation = new Map<\n string,\n {reason: RunnerReservationCapacityFailureReason; count: number}\n >();\n for (const reservationId of reservationIds) {\n const requested = requestedByReservation.get(reservationId) ?? 0;\n const reservation = reservationsById.get(reservationId);\n if (!reservation) {\n unavailableByReservation.set(reservationId, {\n reason: nonLaunchReservationIds.has(reservationId)\n ? 'reservation-kind-mismatch'\n : 'reservation-not-found',\n count: requested,\n });\n continue;\n }\n if (reservation.isExpired) {\n unavailableByReservation.set(reservationId, {\n reason: 'reservation-expired',\n count: requested,\n });\n continue;\n }\n\n const used = usedRunnerIdsByReservation.get(reservationId)?.size ?? 0;\n const accepted = Math.min(requested, Math.max(0, reservation.count - used));\n acceptedByReservation.set(reservationId, accepted);\n if (accepted < requested) {\n unavailableByReservation.set(reservationId, {\n reason: 'capacity-exhausted',\n count: requested - accepted,\n });\n }\n }\n\n return {acceptedByReservation, unavailableByReservation};\n}\n"],"names":["and","eq","inArray","isNull","ne","notInArray","or","sql","ReservationExpiredError","ReservationNotFoundError","RunnerInstanceAlreadyAssignedError","RunnerInstanceNotAssignableError","recordProviderRunnerControlSessionToAssignment","db","lockRunnerReservationAdvisoryKeysTx","terminalStates","reservations","runnerControlSessions","providerRunners","assignRunnerInstances","params","result","transaction","tx","assignRunnerInstancesTx","surface","observation","controlSessionToAssignment","runnerInstanceIds","sort","provisionerId","reservationIds","reservationId","runnerRows","select","id","intendedReservationId","workspaceId","assignedAt","providerRunnerId","provider","providerKind","launchKind","labels","state","from","where","for","length","every","runner","update","set","updatedAt","reservation","kind","limit","expiresAt","Date","activeControlSessions","runnerInstanceId","createdAt","map","closedAt","runnerInstanceIdsWithControlSession","Set","session","controlSessionCreatedAtByRunner","Map","runners","controlSessionId","has","controlSessionCreatedAt","get","alreadyAssigned","filter","some","newRunners","newRunnerIds","assignedCount","count","reservationReleasedAt","requiredLabels","label","includes","assignedRows","returning","runnersById","assigned","push","durationMilliseconds","getTime","validateRunnerReservationCapacityTx","options","requestedByReservation","request","requests","keys","acceptedByReservation","unavailableByReservation","advisoryLocksHeld","reservationRows","isExpired","nonLaunchReservationRows","reservationsById","nonLaunchReservationIds","requestedReservationIds","usedRunnerIdsByReservation","usedRunnerRows","usedRunnerIds","add","requested","reason","used","size","accepted","Math","min","max"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/db/runner-assignments.ts"],"sourcesContent":["import {and, eq, inArray, isNull, ne, notInArray, or, sql} from 'drizzle-orm';\nimport {\n ReservationExpiredError,\n ReservationNotFoundError,\n RunnerInstanceAlreadyAssignedError,\n RunnerInstanceNotAssignableError,\n} from '#core/errors.js';\nimport {\n type ProviderRunnerAssignmentObservation,\n type RunnerAssignmentSurface,\n recordProviderRunnerControlSessionToAssignment,\n} from '#metrics/instance.js';\nimport type {Tx} from './db.js';\nimport {db} from './db.js';\nimport {lockRunnerReservationAdvisoryKeysTx} from './reservation-locks.js';\nimport {terminalStates} from './runner-states.js';\nimport {reservations} from './schema/reservations.js';\nimport {runnerControlSessions} from './schema/runner-control-sessions.js';\nimport {providerRunners} from './schema/runner-instances.js';\n\n/** Atomically consumes reservation units by writing the immutable assignment on each runner. */\nexport async function assignRunnerInstances(params: {\n provisionerId: string;\n reservationId: string;\n runnerInstanceIds: string[];\n}): Promise<string[]> {\n const result = await db().transaction(async (tx) =>\n assignRunnerInstancesTx(tx, {...params, surface: 'provisioner'}),\n );\n for (const observation of result.controlSessionToAssignment)\n recordProviderRunnerControlSessionToAssignment(observation);\n return result.runnerInstanceIds;\n}\n\ninterface AssignRunnerInstancesTxResult {\n runnerInstanceIds: string[];\n controlSessionToAssignment: ProviderRunnerAssignmentObservation[];\n}\n\nexport async function assignRunnerInstancesTx(\n tx: Tx,\n params: {\n provisionerId: string;\n reservationId: string;\n runnerInstanceIds: string[];\n surface: RunnerAssignmentSurface;\n },\n): Promise<AssignRunnerInstancesTxResult> {\n const runnerInstanceIds = [...params.runnerInstanceIds].sort();\n await lockRunnerReservationAdvisoryKeysTx(tx, {\n provisionerId: params.provisionerId,\n reservationIds: [params.reservationId],\n });\n const runnerRows = await tx\n .select({\n id: providerRunners.id,\n reservationId: providerRunners.reservationId,\n intendedReservationId: providerRunners.intendedReservationId,\n workspaceId: providerRunners.workspaceId,\n assignedAt: providerRunners.assignedAt,\n providerRunnerId: providerRunners.providerRunnerId,\n provider: providerRunners.providerKind,\n launchKind: providerRunners.launchKind,\n labels: providerRunners.labels,\n state: providerRunners.state,\n })\n .from(providerRunners)\n .where(\n and(\n eq(providerRunners.provisionerId, params.provisionerId),\n inArray(providerRunners.id, runnerInstanceIds),\n ),\n )\n .for('update');\n\n // The assignment outlives its short reservation row. A retry after maintenance\n // cleanup is complete when every owned runner already carries the committed assignment.\n if (\n runnerInstanceIds.length > 0 &&\n runnerRows.length === runnerInstanceIds.length &&\n runnerRows.every(\n (runner) =>\n runner.reservationId === params.reservationId &&\n runner.assignedAt !== null &&\n runner.workspaceId !== null,\n )\n ) {\n await tx\n .update(providerRunners)\n .set({intendedReservationId: null, updatedAt: sql`now()`})\n .where(\n and(\n eq(providerRunners.provisionerId, params.provisionerId),\n inArray(providerRunners.id, runnerInstanceIds),\n ),\n );\n return {\n runnerInstanceIds: params.runnerInstanceIds,\n controlSessionToAssignment: [],\n };\n }\n\n const [reservation] = await tx\n .select()\n .from(reservations)\n .where(\n and(\n eq(reservations.id, params.reservationId),\n eq(reservations.provisionerId, params.provisionerId),\n eq(reservations.kind, 'launch'),\n ),\n )\n .limit(1)\n .for('update');\n if (!reservation) throw new ReservationNotFoundError(params.reservationId);\n if (reservation.expiresAt <= new Date()) throw new ReservationExpiredError(params.reservationId);\n if (runnerRows.length !== runnerInstanceIds.length)\n throw new RunnerInstanceNotAssignableError(runnerInstanceIds[0] ?? '', 'runner-not-found');\n\n const activeControlSessions = await tx\n .select({\n runnerInstanceId: runnerControlSessions.runnerInstanceId,\n createdAt: runnerControlSessions.createdAt,\n })\n .from(runnerControlSessions)\n .where(\n and(\n inArray(\n runnerControlSessions.runnerInstanceId,\n runnerRows.map((runner) => runner.id),\n ),\n isNull(runnerControlSessions.closedAt),\n ),\n );\n const runnerInstanceIdsWithControlSession = new Set(\n activeControlSessions.map((session) => session.runnerInstanceId),\n );\n const controlSessionCreatedAtByRunner = new Map(\n activeControlSessions.map((session) => [session.runnerInstanceId, session.createdAt]),\n );\n const runners = runnerRows.map((runner) => ({\n ...runner,\n controlSessionId: runnerInstanceIdsWithControlSession.has(runner.id) ? runner.id : null,\n controlSessionCreatedAt: controlSessionCreatedAtByRunner.get(runner.id) ?? null,\n }));\n\n const alreadyAssigned = runners.filter((runner) => runner.reservationId !== null);\n if (alreadyAssigned.some((runner) => runner.reservationId !== reservation.id))\n throw new RunnerInstanceAlreadyAssignedError(alreadyAssigned[0]?.id ?? '');\n const newRunners = runners.filter(\n (runner) =>\n runner.reservationId === null ||\n (runner.reservationId === reservation.id &&\n (runner.assignedAt === null || runner.workspaceId === null)),\n );\n const newRunnerIds = newRunners.map((runner) => runner.id);\n const assignedCount = await tx\n .select({count: sql<number>`count(*)::int`})\n .from(providerRunners)\n .where(\n and(\n eq(providerRunners.provisionerId, params.provisionerId),\n notInArray(providerRunners.id, newRunnerIds),\n or(\n and(\n eq(providerRunners.reservationId, reservation.id),\n isNull(providerRunners.reservationReleasedAt),\n notInArray(providerRunners.state, [...terminalStates]),\n ),\n and(\n eq(providerRunners.intendedReservationId, reservation.id),\n isNull(providerRunners.reservationReleasedAt),\n notInArray(providerRunners.state, [...terminalStates]),\n ),\n ),\n ),\n );\n if ((assignedCount[0]?.count ?? 0) + newRunners.length > reservation.count)\n throw new RunnerInstanceNotAssignableError(newRunners[0]?.id ?? '', 'capacity-exhausted');\n for (const runner of newRunners) {\n if (runner.state !== 'running')\n throw new RunnerInstanceNotAssignableError(runner.id, 'runner-not-running');\n if (!runner.providerRunnerId)\n throw new RunnerInstanceNotAssignableError(runner.id, 'provider-identity-missing');\n if (!runner.controlSessionId)\n throw new RunnerInstanceNotAssignableError(runner.id, 'control-session-not-active');\n if (!reservation.requiredLabels.every((label) => runner.labels.includes(label)))\n throw new RunnerInstanceNotAssignableError(runner.id, 'labels-mismatch');\n }\n if (newRunners.length > 0) {\n const assignedRows = await tx\n .update(providerRunners)\n .set({\n workspaceId: reservation.workspaceId,\n reservationId: reservation.id,\n intendedReservationId: null,\n assignedAt: sql`now()`,\n updatedAt: sql`now()`,\n })\n .where(\n inArray(\n providerRunners.id,\n newRunners.map((runner) => runner.id),\n ),\n )\n .returning({id: providerRunners.id, assignedAt: providerRunners.assignedAt});\n const runnersById = new Map(newRunners.map((runner) => [runner.id, runner]));\n const controlSessionToAssignment: ProviderRunnerAssignmentObservation[] = [];\n for (const assigned of assignedRows) {\n const runner = runnersById.get(assigned.id);\n const controlSessionCreatedAt = runner?.controlSessionCreatedAt;\n if (!runner || !assigned.assignedAt || !controlSessionCreatedAt) continue;\n controlSessionToAssignment.push({\n durationMilliseconds: assigned.assignedAt.getTime() - controlSessionCreatedAt.getTime(),\n provider: runner.provider,\n launchKind: runner.launchKind,\n surface: params.surface,\n runnerInstanceId: runner.id,\n });\n }\n return {\n runnerInstanceIds: params.runnerInstanceIds,\n controlSessionToAssignment,\n };\n }\n return {\n runnerInstanceIds: params.runnerInstanceIds,\n controlSessionToAssignment: [],\n };\n}\n\nexport type RunnerReservationCapacityFailureReason =\n | 'reservation-not-found'\n | 'reservation-kind-mismatch'\n | 'reservation-expired'\n | 'capacity-exhausted';\n\nexport interface RunnerReservationCapacityValidation {\n acceptedByReservation: Map<string, number>;\n unavailableByReservation: Map<\n string,\n {reason: RunnerReservationCapacityFailureReason; count: number}\n >;\n}\n\nexport async function validateRunnerReservationCapacityTx(\n tx: Tx,\n params: {\n provisionerId: string;\n requests: readonly {reservationId: string; count: number}[];\n },\n options: {advisoryLocksHeld?: boolean} = {},\n): Promise<RunnerReservationCapacityValidation> {\n const requestedByReservation = new Map<string, number>();\n for (const request of params.requests) {\n if (request.count <= 0) continue;\n requestedByReservation.set(\n request.reservationId,\n (requestedByReservation.get(request.reservationId) ?? 0) + request.count,\n );\n }\n const reservationIds = [...requestedByReservation.keys()].sort();\n if (reservationIds.length === 0)\n return {acceptedByReservation: new Map(), unavailableByReservation: new Map()};\n\n if (!options.advisoryLocksHeld)\n await lockRunnerReservationAdvisoryKeysTx(tx, {\n provisionerId: params.provisionerId,\n reservationIds,\n });\n\n const reservationRows = await tx\n .select({\n id: reservations.id,\n count: reservations.count,\n expiresAt: reservations.expiresAt,\n isExpired: sql<boolean>`${reservations.expiresAt} <= now()`,\n })\n .from(reservations)\n .where(\n and(\n eq(reservations.provisionerId, params.provisionerId),\n inArray(reservations.id, reservationIds),\n eq(reservations.kind, 'launch'),\n ),\n )\n .for('update');\n const nonLaunchReservationRows = await tx\n .select({id: reservations.id})\n .from(reservations)\n .where(\n and(\n eq(reservations.provisionerId, params.provisionerId),\n inArray(reservations.id, reservationIds),\n ne(reservations.kind, 'launch'),\n ),\n )\n .for('update');\n const reservationsById = new Map(\n reservationRows.map((reservation) => [reservation.id, reservation]),\n );\n const nonLaunchReservationIds = new Set(\n nonLaunchReservationRows.map((reservation) => reservation.id),\n );\n const requestedReservationIds = new Set(reservationIds);\n const usedRunnerIdsByReservation = new Map<string, Set<string>>();\n const usedRunnerRows = await tx\n .select({\n id: providerRunners.id,\n reservationId: providerRunners.reservationId,\n intendedReservationId: providerRunners.intendedReservationId,\n })\n .from(providerRunners)\n .where(\n and(\n eq(providerRunners.provisionerId, params.provisionerId),\n isNull(providerRunners.reservationReleasedAt),\n or(\n and(\n inArray(providerRunners.reservationId, reservationIds),\n notInArray(providerRunners.state, [...terminalStates]),\n ),\n and(\n inArray(providerRunners.intendedReservationId, reservationIds),\n notInArray(providerRunners.state, [...terminalStates]),\n ),\n ),\n ),\n );\n for (const runner of usedRunnerRows) {\n if (runner.reservationId && requestedReservationIds.has(runner.reservationId)) {\n const usedRunnerIds =\n usedRunnerIdsByReservation.get(runner.reservationId) ?? new Set<string>();\n usedRunnerIds.add(runner.id);\n usedRunnerIdsByReservation.set(runner.reservationId, usedRunnerIds);\n }\n if (runner.intendedReservationId && requestedReservationIds.has(runner.intendedReservationId)) {\n const usedRunnerIds =\n usedRunnerIdsByReservation.get(runner.intendedReservationId) ?? new Set<string>();\n usedRunnerIds.add(runner.id);\n usedRunnerIdsByReservation.set(runner.intendedReservationId, usedRunnerIds);\n }\n }\n\n const acceptedByReservation = new Map<string, number>();\n const unavailableByReservation = new Map<\n string,\n {reason: RunnerReservationCapacityFailureReason; count: number}\n >();\n for (const reservationId of reservationIds) {\n const requested = requestedByReservation.get(reservationId) ?? 0;\n const reservation = reservationsById.get(reservationId);\n if (!reservation) {\n unavailableByReservation.set(reservationId, {\n reason: nonLaunchReservationIds.has(reservationId)\n ? 'reservation-kind-mismatch'\n : 'reservation-not-found',\n count: requested,\n });\n continue;\n }\n if (reservation.isExpired) {\n unavailableByReservation.set(reservationId, {\n reason: 'reservation-expired',\n count: requested,\n });\n continue;\n }\n\n const used = usedRunnerIdsByReservation.get(reservationId)?.size ?? 0;\n const accepted = Math.min(requested, Math.max(0, reservation.count - used));\n acceptedByReservation.set(reservationId, accepted);\n if (accepted < requested) {\n unavailableByReservation.set(reservationId, {\n reason: 'capacity-exhausted',\n count: requested - accepted,\n });\n }\n }\n\n return {acceptedByReservation, unavailableByReservation};\n}\n"],"names":["and","eq","inArray","isNull","ne","notInArray","or","sql","ReservationExpiredError","ReservationNotFoundError","RunnerInstanceAlreadyAssignedError","RunnerInstanceNotAssignableError","recordProviderRunnerControlSessionToAssignment","db","lockRunnerReservationAdvisoryKeysTx","terminalStates","reservations","runnerControlSessions","providerRunners","assignRunnerInstances","params","result","transaction","tx","assignRunnerInstancesTx","surface","observation","controlSessionToAssignment","runnerInstanceIds","sort","provisionerId","reservationIds","reservationId","runnerRows","select","id","intendedReservationId","workspaceId","assignedAt","providerRunnerId","provider","providerKind","launchKind","labels","state","from","where","for","length","every","runner","update","set","updatedAt","reservation","kind","limit","expiresAt","Date","activeControlSessions","runnerInstanceId","createdAt","map","closedAt","runnerInstanceIdsWithControlSession","Set","session","controlSessionCreatedAtByRunner","Map","runners","controlSessionId","has","controlSessionCreatedAt","get","alreadyAssigned","filter","some","newRunners","newRunnerIds","assignedCount","count","reservationReleasedAt","requiredLabels","label","includes","assignedRows","returning","runnersById","assigned","push","durationMilliseconds","getTime","validateRunnerReservationCapacityTx","options","requestedByReservation","request","requests","keys","acceptedByReservation","unavailableByReservation","advisoryLocksHeld","reservationRows","isExpired","nonLaunchReservationRows","reservationsById","nonLaunchReservationIds","requestedReservationIds","usedRunnerIdsByReservation","usedRunnerRows","usedRunnerIds","add","requested","reason","used","size","accepted","Math","min","max"],"mappings":"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"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { RunnerInstance, RunnerInstanceState } from '#core/entities/runner-instance.js';
|
|
2
2
|
import type { Tx } from './db.js';
|
|
3
3
|
import { type RunnerInstanceBoundJobExecution } from './job-executions.js';
|
|
4
|
-
export declare const activeStates: readonly ["starting", "running", "stopping"];
|
|
5
4
|
export declare const divergenceCountStates: readonly ["starting", "running"];
|
|
6
5
|
export type RunnerInstanceTerminateIntentReason = 'activation-timeout' | 'job-cancelled';
|
|
7
6
|
export interface RunnerInstanceTerminateIntent {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner-instances.d.ts","sourceRoot":"","sources":["../../src/db/runner-instances.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAC,cAAc,EAAE,mBAAmB,EAAC,MAAM,mCAAmC,CAAC;AAG3F,OAAO,KAAK,EAAC,EAAE,EAAC,MAAM,SAAS,CAAC;AAEhC,OAAO,EAEL,KAAK,+BAA+B,EACrC,MAAM,qBAAqB,CAAC;AAa7B,eAAO,MAAM,
|
|
1
|
+
{"version":3,"file":"runner-instances.d.ts","sourceRoot":"","sources":["../../src/db/runner-instances.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,9 +7,9 @@ import { recordRunnerReservationCapacityFailure } from '#metrics/index.js';
|
|
|
7
7
|
import { db } from './db.js';
|
|
8
8
|
import { listRunningJobExecutionsByRunnerInstanceTx } from './job-executions.js';
|
|
9
9
|
import { lockRunnerReservationAdvisoryKeysTx } from './reservation-locks.js';
|
|
10
|
-
import {
|
|
10
|
+
import { releaseTerminalRunnerInstanceReservationsByIds } from './reservations.js';
|
|
11
11
|
import { validateRunnerReservationCapacityTx } from './runner-assignments.js';
|
|
12
|
-
import { terminalStates } from './runner-states.js';
|
|
12
|
+
import { activeStates, terminalStates } from './runner-states.js';
|
|
13
13
|
import { ephemeralRegistrationTokens } from './schema/ephemeral-registration-tokens.js';
|
|
14
14
|
import { provisionerTokens } from './schema/provisioner-tokens.js';
|
|
15
15
|
import { reservations } from './schema/reservations.js';
|
|
@@ -17,11 +17,6 @@ import { runnerControlSessions } from './schema/runner-control-sessions.js';
|
|
|
17
17
|
import { providerRunners, toRunnerInstance } from './schema/runner-instances.js';
|
|
18
18
|
import { runnerSessions } from './schema/runner-sessions.js';
|
|
19
19
|
import { runningJobExecutions } from './schema/running-job-executions.js';
|
|
20
|
-
export const activeStates = [
|
|
21
|
-
'starting',
|
|
22
|
-
'running',
|
|
23
|
-
'stopping'
|
|
24
|
-
];
|
|
25
20
|
export const divergenceCountStates = [
|
|
26
21
|
'starting',
|
|
27
22
|
'running'
|
|
@@ -52,6 +47,12 @@ export async function reportRunnerInstances(params) {
|
|
|
52
47
|
}
|
|
53
48
|
const events = await hydrateRunnerSessionIdsFromConsumedTokens(tx, params, aggregatedEvents);
|
|
54
49
|
const reservationSafeEvents = await guardReportedReservationIdsTx(tx, params, events);
|
|
50
|
+
const existingReportedAtByProviderRunnerId = hasTerminalEvent ? await listExistingProviderRunnerReportedAtTx(tx, params, reservationSafeEvents) : new Map();
|
|
51
|
+
const freshTerminalEvents = reservationSafeEvents.filter((event)=>{
|
|
52
|
+
if (!isTerminalState(event.state)) return false;
|
|
53
|
+
const existingReportedAt = existingReportedAtByProviderRunnerId.get(event.providerRunnerId);
|
|
54
|
+
return existingReportedAt === undefined || event.reportedAt >= existingReportedAt;
|
|
55
|
+
});
|
|
55
56
|
const terminateIntentsHonored = await listTerminateIntentsHonoredByTerminatedReportsTx(tx, params, reservationSafeEvents);
|
|
56
57
|
const values = reservationSafeEvents.map((event)=>({
|
|
57
58
|
workspaceId: params.workspaceId,
|
|
@@ -102,7 +103,7 @@ export async function reportRunnerInstances(params) {
|
|
|
102
103
|
OR ${providerRunnerMilestoneUpdateCondition()}
|
|
103
104
|
`
|
|
104
105
|
});
|
|
105
|
-
const reservationsReleased =
|
|
106
|
+
const reservationsReleased = freshTerminalEvents.length > 0 ? await releaseTerminalRunnerInstanceReservations(tx, params, freshTerminalEvents) : 0;
|
|
106
107
|
return {
|
|
107
108
|
accepted: reservationSafeEvents.length,
|
|
108
109
|
reservationsReleased,
|
|
@@ -110,18 +111,54 @@ export async function reportRunnerInstances(params) {
|
|
|
110
111
|
};
|
|
111
112
|
});
|
|
112
113
|
}
|
|
114
|
+
async function listExistingProviderRunnerReportedAtTx(tx, params, events) {
|
|
115
|
+
const providerRunnerIds = [
|
|
116
|
+
...new Set(events.map((event)=>event.providerRunnerId))
|
|
117
|
+
];
|
|
118
|
+
if (providerRunnerIds.length === 0) return new Map();
|
|
119
|
+
const rows = await tx.select({
|
|
120
|
+
providerRunnerId: providerRunners.providerRunnerId,
|
|
121
|
+
reportedAt: providerRunners.reportedAt
|
|
122
|
+
}).from(providerRunners).where(and(eq(providerRunners.provisionerId, params.provisionerId), inArray(providerRunners.providerRunnerId, providerRunnerIds)));
|
|
123
|
+
return new Map(rows.flatMap((row)=>row.providerRunnerId ? [
|
|
124
|
+
[
|
|
125
|
+
row.providerRunnerId,
|
|
126
|
+
row.reportedAt
|
|
127
|
+
]
|
|
128
|
+
] : []));
|
|
129
|
+
}
|
|
113
130
|
async function guardReportedReservationIdsTx(tx, params, events) {
|
|
114
|
-
const
|
|
131
|
+
const reportedReservationIds = [
|
|
115
132
|
...new Set(events.flatMap((event)=>event.reservationId ? [
|
|
116
133
|
event.reservationId
|
|
117
134
|
] : []))
|
|
118
135
|
].sort();
|
|
119
|
-
|
|
120
|
-
|
|
136
|
+
const providerRunnerIds = [
|
|
137
|
+
...new Set(events.map((event)=>event.providerRunnerId))
|
|
138
|
+
];
|
|
139
|
+
if (reportedReservationIds.length === 0 && !events.some((event)=>isTerminalState(event.state))) return events;
|
|
140
|
+
// Lock every reservation currently associated with the reported runners before taking a
|
|
141
|
+
// runner row lock. Terminal reports may omit reservationId, or carry a stale one; discovering
|
|
142
|
+
// the stored IDs here keeps report projection in the same reservation-then-runner order as
|
|
143
|
+
// assignment and terminal cleanup.
|
|
144
|
+
const existingReservationRows = await tx.select({
|
|
145
|
+
reservationId: providerRunners.reservationId,
|
|
146
|
+
intendedReservationId: providerRunners.intendedReservationId
|
|
147
|
+
}).from(providerRunners).where(and(eq(providerRunners.provisionerId, params.provisionerId), inArray(providerRunners.providerRunnerId, providerRunnerIds)));
|
|
148
|
+
const storedReservationIds = existingReservationRows.flatMap((row)=>[
|
|
149
|
+
row.reservationId,
|
|
150
|
+
row.intendedReservationId
|
|
151
|
+
].filter((reservationId)=>reservationId !== null));
|
|
152
|
+
const reservationIds = [
|
|
153
|
+
...new Set([
|
|
154
|
+
...reportedReservationIds,
|
|
155
|
+
...storedReservationIds
|
|
156
|
+
])
|
|
157
|
+
].sort();
|
|
158
|
+
if (reservationIds.length > 0) await lockRunnerReservationAdvisoryKeysTx(tx, {
|
|
121
159
|
provisionerId: params.provisionerId,
|
|
122
160
|
reservationIds
|
|
123
161
|
});
|
|
124
|
-
const providerRunnerIds = events.map((event)=>event.providerRunnerId);
|
|
125
162
|
const existingRows = await tx.select({
|
|
126
163
|
providerRunnerId: providerRunners.providerRunnerId,
|
|
127
164
|
reservationId: providerRunners.reservationId,
|
|
@@ -349,18 +386,18 @@ export async function reconcileRunnerInstances(params) {
|
|
|
349
386
|
terminatedAt: sql`coalesce(${providerRunners.terminatedAt}, now())`,
|
|
350
387
|
updatedAt: sql`now()`
|
|
351
388
|
}).where(and(inArray(providerRunners.id, staleAbsentRows.map((row)=>row.id)), inArray(providerRunners.state, activeStates), lt(providerRunners.reportedAt, sql`now() - (${params.terminateGraceSeconds} || ' seconds')::interval`))).returning({
|
|
389
|
+
id: providerRunners.id,
|
|
352
390
|
providerRunnerId: providerRunners.providerRunnerId
|
|
353
391
|
});
|
|
354
392
|
absentIds = updated.flatMap((row)=>row.providerRunnerId ? [
|
|
355
393
|
row.providerRunnerId
|
|
356
394
|
] : []);
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
}
|
|
395
|
+
reservationsReleased = await releaseTerminalRunnerInstanceReservationsByIds(tx, {
|
|
396
|
+
workspaceId: params.workspaceId,
|
|
397
|
+
provisionerId: params.provisionerId,
|
|
398
|
+
runnerInstanceIds: updated.map((row)=>row.id),
|
|
399
|
+
requireUnlinkedSession: false
|
|
400
|
+
});
|
|
364
401
|
}
|
|
365
402
|
}
|
|
366
403
|
const observedRows = observedRunnerInstanceIds.length === 0 ? [] : (await tx.select().from(providerRunners).where(and(params.workspaceId ? eq(providerRunners.workspaceId, params.workspaceId) : isNull(providerRunners.workspaceId), eq(providerRunners.provisionerId, params.provisionerId), inArray(providerRunners.providerRunnerId, observedRunnerInstanceIds)))).map(toRunnerInstance);
|
|
@@ -389,12 +426,21 @@ export async function reapStaleRunnerInstances(params) {
|
|
|
389
426
|
provisionerId: providerRunners.provisionerId,
|
|
390
427
|
providerRunnerId: providerRunners.providerRunnerId
|
|
391
428
|
}).from(providerRunners).where(staleRunnerInstanceWhere(tx, cutoff)).orderBy(asc(providerRunners.updatedAt), asc(providerRunners.id)).limit(params.limit);
|
|
392
|
-
|
|
429
|
+
const terminalReservationRows = candidateRows.length === 0 ? await tx.select({
|
|
430
|
+
id: providerRunners.id,
|
|
431
|
+
workspaceId: providerRunners.workspaceId,
|
|
432
|
+
provisionerId: providerRunners.provisionerId,
|
|
433
|
+
providerRunnerId: providerRunners.providerRunnerId
|
|
434
|
+
}).from(providerRunners).where(and(inArray(providerRunners.state, terminalStates), isNotNull(providerRunners.runnerSessionId), lt(providerRunners.updatedAt, cutoff), isNull(providerRunners.reservationReleasedAt), or(isNotNull(providerRunners.reservationId), isNotNull(providerRunners.intendedReservationId)))).orderBy(asc(providerRunners.updatedAt), asc(providerRunners.id)).limit(params.limit) : [];
|
|
435
|
+
if (candidateRows.length === 0 && terminalReservationRows.length === 0) return {
|
|
393
436
|
reaped: 0,
|
|
394
437
|
reservationsReleased: 0
|
|
395
438
|
};
|
|
396
439
|
const workspaceIds = [
|
|
397
|
-
...new Set(
|
|
440
|
+
...new Set([
|
|
441
|
+
...candidateRows,
|
|
442
|
+
...terminalReservationRows
|
|
443
|
+
].flatMap((row)=>row.workspaceId ? [
|
|
398
444
|
row.workspaceId
|
|
399
445
|
] : []))
|
|
400
446
|
].sort();
|
|
@@ -407,6 +453,7 @@ export async function reapStaleRunnerInstances(params) {
|
|
|
407
453
|
failedAt: sql`coalesce(${providerRunners.failedAt}, now())`,
|
|
408
454
|
updatedAt: sql`now()`
|
|
409
455
|
}).where(and(inArray(providerRunners.id, candidateRows.map((row)=>row.id)), staleRunnerInstanceWhere(tx, cutoff))).returning({
|
|
456
|
+
id: providerRunners.id,
|
|
410
457
|
workspaceId: providerRunners.workspaceId,
|
|
411
458
|
provisionerId: providerRunners.provisionerId,
|
|
412
459
|
providerRunnerId: providerRunners.providerRunnerId
|
|
@@ -416,7 +463,15 @@ export async function reapStaleRunnerInstances(params) {
|
|
|
416
463
|
reservationsReleased += await releaseTerminalRunnerInstanceReservationsByIds(tx, {
|
|
417
464
|
workspaceId: group.workspaceId,
|
|
418
465
|
provisionerId: group.provisionerId,
|
|
419
|
-
|
|
466
|
+
runnerInstanceIds: group.runnerInstanceIds,
|
|
467
|
+
requireUnlinkedSession: false
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
for (const group of groupRunnerInstanceIds(terminalReservationRows)){
|
|
471
|
+
reservationsReleased += await releaseTerminalRunnerInstanceReservationsByIds(tx, {
|
|
472
|
+
workspaceId: group.workspaceId,
|
|
473
|
+
provisionerId: group.provisionerId,
|
|
474
|
+
runnerInstanceIds: group.runnerInstanceIds,
|
|
420
475
|
requireUnlinkedSession: false
|
|
421
476
|
});
|
|
422
477
|
}
|
|
@@ -432,83 +487,14 @@ async function releaseTerminalRunnerInstanceReservations(tx, params, events) {
|
|
|
432
487
|
return await releaseTerminalRunnerInstanceReservationsByIds(tx, {
|
|
433
488
|
workspaceId: params.workspaceId,
|
|
434
489
|
provisionerId: params.provisionerId,
|
|
435
|
-
providerRunnerIds: terminalEvents.map((event)=>event.providerRunnerId)
|
|
490
|
+
providerRunnerIds: terminalEvents.map((event)=>event.providerRunnerId),
|
|
491
|
+
reportedAtByProviderRunnerId: new Map(terminalEvents.map((event)=>[
|
|
492
|
+
event.providerRunnerId,
|
|
493
|
+
event.reportedAt
|
|
494
|
+
])),
|
|
495
|
+
requireUnlinkedSession: false
|
|
436
496
|
});
|
|
437
497
|
}
|
|
438
|
-
async function releaseTerminalRunnerInstanceReservationsByIds(tx, params) {
|
|
439
|
-
if (params.providerRunnerIds.length === 0) return 0;
|
|
440
|
-
const reservationWorkspacePredicate = params.workspaceId === null ? sql`` : sql`and ${eq(reservations.workspaceId, params.workspaceId)}`;
|
|
441
|
-
const rows = await tx.select({
|
|
442
|
-
id: providerRunners.id,
|
|
443
|
-
releaseReservationId: sql`coalesce(
|
|
444
|
-
(select ${reservations.id}
|
|
445
|
-
from ${reservations}
|
|
446
|
-
where ${reservations.id} = ${providerRunners.intendedReservationId}
|
|
447
|
-
and ${reservations.provisionerId} = ${params.provisionerId}
|
|
448
|
-
${reservationWorkspacePredicate}),
|
|
449
|
-
(select ${reservations.id}
|
|
450
|
-
from ${reservations}
|
|
451
|
-
where ${reservations.id} = ${providerRunners.reservationId}
|
|
452
|
-
and ${reservations.provisionerId} = ${params.provisionerId}
|
|
453
|
-
${reservationWorkspacePredicate})
|
|
454
|
-
)`,
|
|
455
|
-
releaseReservationWorkspaceId: sql`coalesce(
|
|
456
|
-
(select ${reservations.workspaceId}
|
|
457
|
-
from ${reservations}
|
|
458
|
-
where ${reservations.id} = ${providerRunners.intendedReservationId}
|
|
459
|
-
and ${reservations.provisionerId} = ${params.provisionerId}
|
|
460
|
-
${reservationWorkspacePredicate}),
|
|
461
|
-
(select ${reservations.workspaceId}
|
|
462
|
-
from ${reservations}
|
|
463
|
-
where ${reservations.id} = ${providerRunners.reservationId}
|
|
464
|
-
and ${reservations.provisionerId} = ${params.provisionerId}
|
|
465
|
-
${reservationWorkspacePredicate})
|
|
466
|
-
)`
|
|
467
|
-
}).from(providerRunners).where(and(eq(providerRunners.provisionerId, params.provisionerId), inArray(providerRunners.providerRunnerId, params.providerRunnerIds), inArray(providerRunners.state, terminalStates), or(isNotNull(providerRunners.reservationId), isNotNull(providerRunners.intendedReservationId)), params.workspaceId === null ? undefined : or(and(eq(providerRunners.workspaceId, params.workspaceId), or(exists(tx.select({
|
|
468
|
-
id: reservations.id
|
|
469
|
-
}).from(reservations).where(and(eq(reservations.workspaceId, params.workspaceId), eq(reservations.provisionerId, params.provisionerId), eq(reservations.id, providerRunners.reservationId)))), exists(tx.select({
|
|
470
|
-
id: reservations.id
|
|
471
|
-
}).from(reservations).where(and(eq(reservations.workspaceId, params.workspaceId), eq(reservations.provisionerId, params.provisionerId), eq(reservations.id, providerRunners.intendedReservationId)))))), and(isNull(providerRunners.workspaceId), isNotNull(providerRunners.intendedReservationId), exists(tx.select({
|
|
472
|
-
id: reservations.id
|
|
473
|
-
}).from(reservations).where(and(eq(reservations.workspaceId, params.workspaceId), eq(reservations.provisionerId, params.provisionerId), eq(reservations.id, providerRunners.intendedReservationId)))))), params.requireUnlinkedSession === false ? undefined : isNull(providerRunners.runnerSessionId), isNull(providerRunners.reservationReleasedAt))).for('update');
|
|
474
|
-
if (rows.length === 0) return 0;
|
|
475
|
-
const updated = await tx.update(providerRunners).set({
|
|
476
|
-
intendedReservationId: null,
|
|
477
|
-
reservationReleasedAt: sql`now()`,
|
|
478
|
-
updatedAt: sql`now()`
|
|
479
|
-
}).where(and(inArray(providerRunners.id, rows.map((row)=>row.id)), params.requireUnlinkedSession === false ? undefined : isNull(providerRunners.runnerSessionId), isNull(providerRunners.reservationReleasedAt))).returning({
|
|
480
|
-
id: providerRunners.id
|
|
481
|
-
});
|
|
482
|
-
const releasesByReservationId = new Map();
|
|
483
|
-
const updatedIds = new Set(updated.map((row)=>row.id));
|
|
484
|
-
for (const row of rows){
|
|
485
|
-
if (!updatedIds.has(row.id)) continue;
|
|
486
|
-
if (!row.releaseReservationId || !row.releaseReservationWorkspaceId) continue;
|
|
487
|
-
const key = `${row.releaseReservationWorkspaceId}:${row.releaseReservationId}`;
|
|
488
|
-
const release = releasesByReservationId.get(key) ?? {
|
|
489
|
-
workspaceId: row.releaseReservationWorkspaceId,
|
|
490
|
-
reservationId: row.releaseReservationId,
|
|
491
|
-
count: 0
|
|
492
|
-
};
|
|
493
|
-
release.count += 1;
|
|
494
|
-
releasesByReservationId.set(key, release);
|
|
495
|
-
}
|
|
496
|
-
if (releasesByReservationId.size === 0) return 0;
|
|
497
|
-
let released = 0;
|
|
498
|
-
for (const release of releasesByReservationId.values()){
|
|
499
|
-
released += await releaseReservationUnits(tx, {
|
|
500
|
-
workspaceId: release.workspaceId,
|
|
501
|
-
provisionerId: params.provisionerId,
|
|
502
|
-
releases: [
|
|
503
|
-
{
|
|
504
|
-
reservationId: release.reservationId,
|
|
505
|
-
count: release.count
|
|
506
|
-
}
|
|
507
|
-
]
|
|
508
|
-
});
|
|
509
|
-
}
|
|
510
|
-
return released;
|
|
511
|
-
}
|
|
512
498
|
function staleRunnerInstanceCutoff(thresholdSeconds) {
|
|
513
499
|
return sql`now() - (${thresholdSeconds} || ' seconds')::interval`;
|
|
514
500
|
}
|
|
@@ -524,14 +510,13 @@ function staleRunnerInstanceWhere(tx, cutoff) {
|
|
|
524
510
|
function groupRunnerInstanceIds(rows) {
|
|
525
511
|
const groups = new Map();
|
|
526
512
|
for (const row of rows){
|
|
527
|
-
if (!row.providerRunnerId) continue;
|
|
528
513
|
const key = `${row.workspaceId ?? ''}:${row.provisionerId}`;
|
|
529
514
|
const group = groups.get(key) ?? {
|
|
530
515
|
workspaceId: row.workspaceId,
|
|
531
516
|
provisionerId: row.provisionerId,
|
|
532
|
-
|
|
517
|
+
runnerInstanceIds: []
|
|
533
518
|
};
|
|
534
|
-
group.
|
|
519
|
+
group.runnerInstanceIds.push(row.id);
|
|
535
520
|
groups.set(key, group);
|
|
536
521
|
}
|
|
537
522
|
return [
|