@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
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
$ shipfox-swc && shipfox-temporal-bundle dist/temporal/workflows/index.js
|
|
2
|
-
Successfully compiled:
|
|
3
|
-
2026-08-
|
|
2
|
+
Successfully compiled: 105 files with swc (500.52ms)
|
|
3
|
+
2026-08-14T18:57:08.640Z [INFO] asset workflow-bundle-0c3da2487e29e352d64e.js 3.17 MiB [emitted] [immutable] (name: main)
|
|
4
4
|
orphan modules 33.5 KiB [orphan] 21 modules
|
|
5
5
|
runtime modules 1.13 KiB 5 modules
|
|
6
6
|
modules by path ../../../node_modules/.pnpm/ 970 KiB 196 modules
|
|
@@ -14,6 +14,6 @@ optional modules 30 bytes [optional]
|
|
|
14
14
|
__temporal_custom_payload_converter (ignored) 15 bytes [optional] [built] [code generated]
|
|
15
15
|
__temporal_custom_failure_converter (ignored) 15 bytes [optional] [built] [code generated]
|
|
16
16
|
../../shared/node/temporal/dist/workflow-error-interceptor.js 1.21 KiB [built] [code generated]
|
|
17
|
-
webpack 5.107.2 compiled successfully in
|
|
18
|
-
2026-08-
|
|
17
|
+
webpack 5.107.2 compiled successfully in 6538 ms
|
|
18
|
+
2026-08-14T18:57:08.663Z [INFO] Workflow bundle created { size: '3.17MB' }
|
|
19
19
|
/home/runner/work/shipfox/shipfox/libs/api/runners/dist/temporal/workflows/index.js: 3326598 bytes
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# @shipfox/api-runners
|
|
2
2
|
|
|
3
|
+
## 13.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- af6b31e: Expose ordered queued and terminal workflow job-execution facts, and release terminal runner reservations once no uncancelled lease remains.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Updated dependencies [af6b31e]
|
|
12
|
+
- @shipfox/api-runners-dto@13.0.0
|
|
13
|
+
- @shipfox/api-workflows-dto@13.0.0
|
|
14
|
+
|
|
15
|
+
## 12.7.0
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- 7a5e247: Release runner reservation units when a provisioner reports a terminal runner.
|
|
20
|
+
- Updated dependencies [4df5e37]
|
|
21
|
+
- @shipfox/api-workflows-dto@12.7.0
|
|
22
|
+
|
|
3
23
|
## 12.6.0
|
|
4
24
|
|
|
5
25
|
### Patch Changes
|
package/dist/db/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export { closeDb, db, schema } from './db.js';
|
|
|
4
4
|
export type { CreateEphemeralRegistrationTokenParams, DeleteExpiredEphemeralRegistrationTokensParams, } from './ephemeral-registration-tokens.js';
|
|
5
5
|
export { createEphemeralRegistrationToken, createRunnerSessionConsumingEphemeralToken, deleteExpiredEphemeralRegistrationTokens, resolveEphemeralRegistrationTokenByHash, } from './ephemeral-registration-tokens.js';
|
|
6
6
|
export type { ClaimedJobExecution, EnqueueJobExecutionParams, RunnerInstanceBoundJobExecution, } from './job-executions.js';
|
|
7
|
-
export {
|
|
7
|
+
export { claimPendingJobExecution, enqueueJobExecution, expireStuckJobExecutions, getWorkspaceJobCounts, isJobLeaseActive, listActiveRunningJobExecutions, listRunningJobExecutionsByRunnerInstanceTx, recordHeartbeat, } from './job-executions.js';
|
|
8
8
|
export type { CreateManualRegistrationTokenParams } from './manual-registration-tokens.js';
|
|
9
9
|
export { createManualRegistrationToken, listUsableManualRegistrationTokensByWorkspaceId, resolveManualRegistrationTokenByHash, revokeManualRegistrationToken, } from './manual-registration-tokens.js';
|
|
10
10
|
export { hasActiveWorkspaceProvisionerCapability, listActiveWorkspaceProvisionerCapabilitySnapshots, listStaleWorkspaceProvisionerCapabilitySnapshots, publishWorkspaceProvisionerCapabilitySnapshot, } from './provisioner-capability-snapshots.js';
|
package/dist/db/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/db/index.ts"],"names":[],"mappings":"AAGA,YAAY,EACV,mCAAmC,EACnC,8BAA8B,EAC9B,sCAAsC,EACtC,sCAAsC,GACvC,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAC,gCAAgC,EAAC,MAAM,6BAA6B,CAAC;AAC7E,OAAO,EAAC,OAAO,EAAE,EAAE,EAAE,MAAM,EAAC,MAAM,SAAS,CAAC;AAC5C,YAAY,EACV,sCAAsC,EACtC,8CAA8C,GAC/C,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EACL,gCAAgC,EAChC,0CAA0C,EAC1C,wCAAwC,EACxC,uCAAuC,GACxC,MAAM,oCAAoC,CAAC;AAC5C,YAAY,EACV,mBAAmB,EACnB,yBAAyB,EACzB,+BAA+B,GAChC,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/db/index.ts"],"names":[],"mappings":"AAGA,YAAY,EACV,mCAAmC,EACnC,8BAA8B,EAC9B,sCAAsC,EACtC,sCAAsC,GACvC,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAC,gCAAgC,EAAC,MAAM,6BAA6B,CAAC;AAC7E,OAAO,EAAC,OAAO,EAAE,EAAE,EAAE,MAAM,EAAC,MAAM,SAAS,CAAC;AAC5C,YAAY,EACV,sCAAsC,EACtC,8CAA8C,GAC/C,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EACL,gCAAgC,EAChC,0CAA0C,EAC1C,wCAAwC,EACxC,uCAAuC,GACxC,MAAM,oCAAoC,CAAC;AAC5C,YAAY,EACV,mBAAmB,EACnB,yBAAyB,EACzB,+BAA+B,GAChC,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,wBAAwB,EACxB,mBAAmB,EACnB,wBAAwB,EACxB,qBAAqB,EACrB,gBAAgB,EAChB,8BAA8B,EAC9B,0CAA0C,EAC1C,eAAe,GAChB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EAAC,mCAAmC,EAAC,MAAM,iCAAiC,CAAC;AACzF,OAAO,EACL,6BAA6B,EAC7B,+CAA+C,EAC/C,oCAAoC,EACpC,6BAA6B,GAC9B,MAAM,iCAAiC,CAAC;AACzC,OAAO,EACL,uCAAuC,EACvC,iDAAiD,EACjD,gDAAgD,EAChD,6CAA6C,GAC9C,MAAM,uCAAuC,CAAC;AAC/C,YAAY,EAAC,4BAA4B,EAAC,MAAM,yBAAyB,CAAC;AAC1E,OAAO,EACL,2CAA2C,EAC3C,sBAAsB,EACtB,2BAA2B,EAC3B,iCAAiC,EACjC,wCAAwC,EACxC,6BAA6B,EAC7B,2CAA2C,EAC3C,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,uBAAuB,EACvB,6BAA6B,GAC9B,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACV,UAAU,EACV,0BAA0B,EAC1B,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,yBAAyB,EACzB,uBAAuB,EACvB,oBAAoB,EACpB,uBAAuB,GACxB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAC,qBAAqB,EAAC,MAAM,yBAAyB,CAAC;AAC9D,YAAY,EACV,iCAAiC,EACjC,8BAA8B,EAC9B,gCAAgC,EAChC,8BAA8B,EAC9B,2BAA2B,EAC3B,yBAAyB,EACzB,6BAA6B,EAC7B,mCAAmC,GACpC,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,8BAA8B,EAC9B,iCAAiC,EACjC,eAAe,EACf,0CAA0C,EAC1C,yBAAyB,EACzB,oCAAoC,EACpC,+BAA+B,EAC/B,wBAAwB,EACxB,wBAAwB,EACxB,qBAAqB,GACtB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EACV,yBAAyB,EACzB,iCAAiC,GAClC,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAC,mBAAmB,EAAE,2BAA2B,EAAC,MAAM,sBAAsB,CAAC;AACtF,OAAO,EAAC,aAAa,EAAC,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAC,qBAAqB,EAAE,qBAAqB,EAAC,MAAM,qCAAqC,CAAC;AAEjG,eAAO,MAAM,cAAc,QAAoE,CAAC"}
|
package/dist/db/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
3
3
|
export { listAdministratorRunnerInstances } from './admin-runner-instances.js';
|
|
4
4
|
export { closeDb, db, schema } from './db.js';
|
|
5
5
|
export { createEphemeralRegistrationToken, createRunnerSessionConsumingEphemeralToken, deleteExpiredEphemeralRegistrationTokens, resolveEphemeralRegistrationTokenByHash } from './ephemeral-registration-tokens.js';
|
|
6
|
-
export {
|
|
6
|
+
export { claimPendingJobExecution, enqueueJobExecution, expireStuckJobExecutions, getWorkspaceJobCounts, isJobLeaseActive, listActiveRunningJobExecutions, listRunningJobExecutionsByRunnerInstanceTx, recordHeartbeat } from './job-executions.js';
|
|
7
7
|
export { createManualRegistrationToken, listUsableManualRegistrationTokensByWorkspaceId, resolveManualRegistrationTokenByHash, revokeManualRegistrationToken } from './manual-registration-tokens.js';
|
|
8
8
|
export { hasActiveWorkspaceProvisionerCapability, listActiveWorkspaceProvisionerCapabilitySnapshots, listStaleWorkspaceProvisionerCapabilitySnapshots, publishWorkspaceProvisionerCapabilitySnapshot } from './provisioner-capability-snapshots.js';
|
|
9
9
|
export { createInstallationProvisionerTokenWithAudit, createProvisionerToken, listActiveProvisionerTokens, listInstallationProvisionerTokens, listUsableProvisionerTokensByWorkspaceId, resolveProvisionerTokenByHash, revokeInstallationProvisionerTokenWithAudit, revokeProvisionerToken, touchProvisionerLastSeen } from './provisioner-tokens.js';
|
package/dist/db/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/db/index.ts"],"sourcesContent":["import {dirname, resolve} from 'node:path';\nimport {fileURLToPath} from 'node:url';\n\nexport type {\n AdministratorRunnerAssignmentFilter,\n AdministratorRunnerInstanceRow,\n ListAdministratorRunnerInstancesParams,\n ListAdministratorRunnerInstancesResult,\n} from './admin-runner-instances.js';\nexport {listAdministratorRunnerInstances} from './admin-runner-instances.js';\nexport {closeDb, db, schema} from './db.js';\nexport type {\n CreateEphemeralRegistrationTokenParams,\n DeleteExpiredEphemeralRegistrationTokensParams,\n} from './ephemeral-registration-tokens.js';\nexport {\n createEphemeralRegistrationToken,\n createRunnerSessionConsumingEphemeralToken,\n deleteExpiredEphemeralRegistrationTokens,\n resolveEphemeralRegistrationTokenByHash,\n} from './ephemeral-registration-tokens.js';\nexport type {\n ClaimedJobExecution,\n EnqueueJobExecutionParams,\n RunnerInstanceBoundJobExecution,\n} from './job-executions.js';\nexport {\n
|
|
1
|
+
{"version":3,"sources":["../../src/db/index.ts"],"sourcesContent":["import {dirname, resolve} from 'node:path';\nimport {fileURLToPath} from 'node:url';\n\nexport type {\n AdministratorRunnerAssignmentFilter,\n AdministratorRunnerInstanceRow,\n ListAdministratorRunnerInstancesParams,\n ListAdministratorRunnerInstancesResult,\n} from './admin-runner-instances.js';\nexport {listAdministratorRunnerInstances} from './admin-runner-instances.js';\nexport {closeDb, db, schema} from './db.js';\nexport type {\n CreateEphemeralRegistrationTokenParams,\n DeleteExpiredEphemeralRegistrationTokensParams,\n} from './ephemeral-registration-tokens.js';\nexport {\n createEphemeralRegistrationToken,\n createRunnerSessionConsumingEphemeralToken,\n deleteExpiredEphemeralRegistrationTokens,\n resolveEphemeralRegistrationTokenByHash,\n} from './ephemeral-registration-tokens.js';\nexport type {\n ClaimedJobExecution,\n EnqueueJobExecutionParams,\n RunnerInstanceBoundJobExecution,\n} from './job-executions.js';\nexport {\n claimPendingJobExecution,\n enqueueJobExecution,\n expireStuckJobExecutions,\n getWorkspaceJobCounts,\n isJobLeaseActive,\n listActiveRunningJobExecutions,\n listRunningJobExecutionsByRunnerInstanceTx,\n recordHeartbeat,\n} from './job-executions.js';\nexport type {CreateManualRegistrationTokenParams} from './manual-registration-tokens.js';\nexport {\n createManualRegistrationToken,\n listUsableManualRegistrationTokensByWorkspaceId,\n resolveManualRegistrationTokenByHash,\n revokeManualRegistrationToken,\n} from './manual-registration-tokens.js';\nexport {\n hasActiveWorkspaceProvisionerCapability,\n listActiveWorkspaceProvisionerCapabilitySnapshots,\n listStaleWorkspaceProvisionerCapabilitySnapshots,\n publishWorkspaceProvisionerCapabilitySnapshot,\n} from './provisioner-capability-snapshots.js';\nexport type {CreateProvisionerTokenParams} from './provisioner-tokens.js';\nexport {\n createInstallationProvisionerTokenWithAudit,\n createProvisionerToken,\n listActiveProvisionerTokens,\n listInstallationProvisionerTokens,\n listUsableProvisionerTokensByWorkspaceId,\n resolveProvisionerTokenByHash,\n revokeInstallationProvisionerTokenWithAudit,\n revokeProvisionerToken,\n touchProvisionerLastSeen,\n} from './provisioner-tokens.js';\nexport {\n consumeRunnersRateLimit,\n pruneExpiredRunnersRateLimits,\n} from './rate-limits.js';\nexport type {\n DemandStat,\n PollDemandAndReserveParams,\n ReservationGrant,\n ReservationTemplate,\n} from './reservations.js';\nexport {\n deleteExpiredReservations,\n deleteReservationsByIds,\n pollDemandAndReserve,\n releaseReservationUnits,\n} from './reservations.js';\nexport {assignRunnerInstances} from './runner-assignments.js';\nexport type {\n ActiveRunnerInstanceTemplateCount,\n ReapStaleRunnerInstancesResult,\n ReconcileRunnerInstancesDbResult,\n ReconcileRunnerInstancesParams,\n ReportRunnerInstancesParams,\n RunnerInstanceReportEvent,\n RunnerInstanceTerminateIntent,\n RunnerInstanceTerminateIntentReason,\n} from './runner-instances.js';\nexport {\n attachRunnerInstanceProviderId,\n countStaleEnrolledRunnerInstances,\n isTerminalState,\n listActiveRunnerInstanceCountsByTemplateTx,\n listActiveRunnerInstances,\n listProvisionerTerminateIntentRowsTx,\n listProvisionerTerminateIntents,\n reapStaleRunnerInstances,\n reconcileRunnerInstances,\n reportRunnerInstances,\n} from './runner-instances.js';\nexport type {\n CreateRunnerSessionParams,\n DeleteExpiredRunnerSessionsParams,\n} from './runner-sessions.js';\nexport {createRunnerSession, deleteExpiredRunnerSessions} from './runner-sessions.js';\nexport {runnersOutbox} from './schema/outbox.js';\nexport {runnerBootstrapTokens, runnerControlSessions} from './schema/runner-control-sessions.js';\n\nexport const migrationsPath = resolve(dirname(fileURLToPath(import.meta.url)), '../../drizzle');\n"],"names":["dirname","resolve","fileURLToPath","listAdministratorRunnerInstances","closeDb","db","schema","createEphemeralRegistrationToken","createRunnerSessionConsumingEphemeralToken","deleteExpiredEphemeralRegistrationTokens","resolveEphemeralRegistrationTokenByHash","claimPendingJobExecution","enqueueJobExecution","expireStuckJobExecutions","getWorkspaceJobCounts","isJobLeaseActive","listActiveRunningJobExecutions","listRunningJobExecutionsByRunnerInstanceTx","recordHeartbeat","createManualRegistrationToken","listUsableManualRegistrationTokensByWorkspaceId","resolveManualRegistrationTokenByHash","revokeManualRegistrationToken","hasActiveWorkspaceProvisionerCapability","listActiveWorkspaceProvisionerCapabilitySnapshots","listStaleWorkspaceProvisionerCapabilitySnapshots","publishWorkspaceProvisionerCapabilitySnapshot","createInstallationProvisionerTokenWithAudit","createProvisionerToken","listActiveProvisionerTokens","listInstallationProvisionerTokens","listUsableProvisionerTokensByWorkspaceId","resolveProvisionerTokenByHash","revokeInstallationProvisionerTokenWithAudit","revokeProvisionerToken","touchProvisionerLastSeen","consumeRunnersRateLimit","pruneExpiredRunnersRateLimits","deleteExpiredReservations","deleteReservationsByIds","pollDemandAndReserve","releaseReservationUnits","assignRunnerInstances","attachRunnerInstanceProviderId","countStaleEnrolledRunnerInstances","isTerminalState","listActiveRunnerInstanceCountsByTemplateTx","listActiveRunnerInstances","listProvisionerTerminateIntentRowsTx","listProvisionerTerminateIntents","reapStaleRunnerInstances","reconcileRunnerInstances","reportRunnerInstances","createRunnerSession","deleteExpiredRunnerSessions","runnersOutbox","runnerBootstrapTokens","runnerControlSessions","migrationsPath","url"],"mappings":"AAAA,SAAQA,OAAO,EAAEC,OAAO,QAAO,YAAY;AAC3C,SAAQC,aAAa,QAAO,WAAW;AAQvC,SAAQC,gCAAgC,QAAO,8BAA8B;AAC7E,SAAQC,OAAO,EAAEC,EAAE,EAAEC,MAAM,QAAO,UAAU;AAK5C,SACEC,gCAAgC,EAChCC,0CAA0C,EAC1CC,wCAAwC,EACxCC,uCAAuC,QAClC,qCAAqC;AAM5C,SACEC,wBAAwB,EACxBC,mBAAmB,EACnBC,wBAAwB,EACxBC,qBAAqB,EACrBC,gBAAgB,EAChBC,8BAA8B,EAC9BC,0CAA0C,EAC1CC,eAAe,QACV,sBAAsB;AAE7B,SACEC,6BAA6B,EAC7BC,+CAA+C,EAC/CC,oCAAoC,EACpCC,6BAA6B,QACxB,kCAAkC;AACzC,SACEC,uCAAuC,EACvCC,iDAAiD,EACjDC,gDAAgD,EAChDC,6CAA6C,QACxC,wCAAwC;AAE/C,SACEC,2CAA2C,EAC3CC,sBAAsB,EACtBC,2BAA2B,EAC3BC,iCAAiC,EACjCC,wCAAwC,EACxCC,6BAA6B,EAC7BC,2CAA2C,EAC3CC,sBAAsB,EACtBC,wBAAwB,QACnB,0BAA0B;AACjC,SACEC,uBAAuB,EACvBC,6BAA6B,QACxB,mBAAmB;AAO1B,SACEC,yBAAyB,EACzBC,uBAAuB,EACvBC,oBAAoB,EACpBC,uBAAuB,QAClB,oBAAoB;AAC3B,SAAQC,qBAAqB,QAAO,0BAA0B;AAW9D,SACEC,8BAA8B,EAC9BC,iCAAiC,EACjCC,eAAe,EACfC,0CAA0C,EAC1CC,yBAAyB,EACzBC,oCAAoC,EACpCC,+BAA+B,EAC/BC,wBAAwB,EACxBC,wBAAwB,EACxBC,qBAAqB,QAChB,wBAAwB;AAK/B,SAAQC,mBAAmB,EAAEC,2BAA2B,QAAO,uBAAuB;AACtF,SAAQC,aAAa,QAAO,qBAAqB;AACjD,SAAQC,qBAAqB,EAAEC,qBAAqB,QAAO,sCAAsC;AAEjG,OAAO,MAAMC,iBAAiBzD,QAAQD,QAAQE,cAAc,YAAYyD,GAAG,IAAI,iBAAiB"}
|
|
@@ -8,6 +8,7 @@ export interface EnqueueJobExecutionParams {
|
|
|
8
8
|
jobExecutionId: string;
|
|
9
9
|
projectId: string;
|
|
10
10
|
requiredLabels: string[];
|
|
11
|
+
queuedAt: Date;
|
|
11
12
|
}
|
|
12
13
|
export declare function getWorkspaceJobCounts(params: {
|
|
13
14
|
workspaceIds: string[];
|
|
@@ -55,29 +56,18 @@ export declare function claimPendingJobExecution(params: {
|
|
|
55
56
|
maxClaims: number | null;
|
|
56
57
|
runnerSessionLivenessThrottleSeconds: number;
|
|
57
58
|
}): Promise<ClaimedJobExecution | null>;
|
|
58
|
-
/**
|
|
59
|
-
* Releases a job execution's lease when the orchestration workflow finalizes it: deletes the
|
|
60
|
-
* running-job-execution row AND any lingering pending row for the same execution, in one tx.
|
|
61
|
-
* Idempotent (0-row no-op), no token scope (the workflow is authoritative), and
|
|
62
|
-
* emits no event: the workflow already owns the outcome. Sweeping the pending row
|
|
63
|
-
* too closes the at-least-once window where an enqueue retry left an orphan that a
|
|
64
|
-
* later claim would otherwise pick up for an already-finished job execution.
|
|
65
|
-
*/
|
|
66
|
-
export declare function releaseJobExecution(params: {
|
|
67
|
-
jobExecutionId: string;
|
|
68
|
-
}): Promise<void>;
|
|
69
59
|
/**
|
|
70
60
|
* Reaps stale leases (bounded by `limit`), emitting one
|
|
71
61
|
* `runners.job.lease_expired` event per reaped job execution.
|
|
72
62
|
*
|
|
73
63
|
* The cutoff is re-checked in the DELETE, not just the locking subquery, so a
|
|
74
64
|
* heartbeat landing mid-call spares the live row. Each reaped execution also sweeps its
|
|
75
|
-
* pending row
|
|
76
|
-
* that a later claim re-runs as an already-finished job execution.
|
|
65
|
+
* pending row so an at-least-once queue replay cannot leave an orphan.
|
|
77
66
|
*
|
|
78
|
-
* Locks pending-then-running to match `claimPendingJobExecution
|
|
79
|
-
* `reconcileTerminalJobExecution`. The stale candidate scan intentionally does not lock
|
|
80
|
-
* rows first; the running-row DELETE re-checks the stale predicate after the
|
|
67
|
+
* Locks pending-then-running to match `claimPendingJobExecution` and
|
|
68
|
+
* `reconcileTerminalJobExecution`. The stale candidate scan intentionally does not lock
|
|
69
|
+
* running rows first; the running-row DELETE re-checks the stale predicate after the
|
|
70
|
+
* pending-row sweep.
|
|
81
71
|
*/
|
|
82
72
|
export declare function expireStuckJobExecutions(params: {
|
|
83
73
|
thresholdSeconds: number;
|
|
@@ -128,13 +118,10 @@ export declare function recordHeartbeat(params: {
|
|
|
128
118
|
}>;
|
|
129
119
|
/**
|
|
130
120
|
* Reconciles a terminal job execution with runner state in one transaction: removes its pending
|
|
131
|
-
* queue row
|
|
132
|
-
* idempotent and preserves the first cancellation-request timestamp.
|
|
121
|
+
* queue row, requests cancellation on its running lease, and converges any linked reservation.
|
|
122
|
+
* The operation is idempotent and preserves the first cancellation-request timestamp.
|
|
133
123
|
*/
|
|
134
124
|
export declare function reconcileTerminalJobExecution(params: {
|
|
135
125
|
jobExecutionId: string;
|
|
136
126
|
}): Promise<void>;
|
|
137
|
-
export declare function cancelRunnerJobs(params: {
|
|
138
|
-
jobIds: string[];
|
|
139
|
-
}): Promise<void>;
|
|
140
127
|
//# sourceMappingURL=job-executions.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"job-executions.d.ts","sourceRoot":"","sources":["../../src/db/job-executions.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"job-executions.d.ts","sourceRoot":"","sources":["../../src/db/job-executions.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { RUNNER_JOB_CLAIMED, RUNNER_JOB_LEASE_EXPIRED
|
|
1
|
+
import { RUNNER_JOB_CLAIMED, RUNNER_JOB_LEASE_EXPIRED } from '@shipfox/api-runners-dto';
|
|
2
2
|
import { logger } from '@shipfox/node-opentelemetry';
|
|
3
3
|
import { writeOutboxEvent, writeOutboxEvents } from '@shipfox/node-outbox';
|
|
4
4
|
import { canonicalizeLabels } from '@shipfox/runner-labels';
|
|
@@ -6,6 +6,7 @@ import { and, arrayContained, asc, count, desc, eq, gt, inArray, isNotNull, isNu
|
|
|
6
6
|
import { EmptyRequiredLabelsError, RunnerSessionExhaustedError, RunningJobExecutionNotFoundError } from '#core/errors.js';
|
|
7
7
|
import { jobExecutionEnqueuedCount, jobExecutionLeaseExpiredCount, recordJobExecutionQueueTime, recordProviderRunnerActivationToFirstClaim } from '#metrics/instance.js';
|
|
8
8
|
import { db } from './db.js';
|
|
9
|
+
import { releaseTerminalRunnerInstanceReservationsByIds } from './reservations.js';
|
|
9
10
|
import { runnersOutbox } from './schema/outbox.js';
|
|
10
11
|
import { pendingJobExecutions } from './schema/pending-job-executions.js';
|
|
11
12
|
import { providerRunners } from './schema/runner-instances.js';
|
|
@@ -15,6 +16,34 @@ const runnerJobExecutionLockPrefix = 'runners_job_execution:';
|
|
|
15
16
|
async function lockJobExecution(tx, jobExecutionId) {
|
|
16
17
|
await tx.execute(sql`select pg_advisory_xact_lock(hashtext(${`${runnerJobExecutionLockPrefix}${jobExecutionId}`}))`);
|
|
17
18
|
}
|
|
19
|
+
async function releaseReservationsForTerminalRunningRows(tx, rows) {
|
|
20
|
+
// Converge after the lease fact changes. The reservation helper locks each provider runner
|
|
21
|
+
// and releases only when the runner is terminal and no uncancelled lease remains.
|
|
22
|
+
const providerRunnerIdsByProvisionerId = new Map();
|
|
23
|
+
for (const row of rows){
|
|
24
|
+
if (row.provisionerId === null || row.providerRunnerId === null) continue;
|
|
25
|
+
const providerRunnerIds = providerRunnerIdsByProvisionerId.get(row.provisionerId) ?? new Set();
|
|
26
|
+
providerRunnerIds.add(row.providerRunnerId);
|
|
27
|
+
providerRunnerIdsByProvisionerId.set(row.provisionerId, providerRunnerIds);
|
|
28
|
+
}
|
|
29
|
+
for (const provisionerId of [
|
|
30
|
+
...providerRunnerIdsByProvisionerId.keys()
|
|
31
|
+
].sort()){
|
|
32
|
+
const providerRunnerIds = providerRunnerIdsByProvisionerId.get(provisionerId);
|
|
33
|
+
if (!providerRunnerIds) continue;
|
|
34
|
+
await releaseTerminalRunnerInstanceReservationsByIds(tx, {
|
|
35
|
+
workspaceId: null,
|
|
36
|
+
provisionerId,
|
|
37
|
+
providerRunnerIds: [
|
|
38
|
+
...providerRunnerIds
|
|
39
|
+
].sort(),
|
|
40
|
+
requireUnlinkedSession: false,
|
|
41
|
+
// Lock and re-check the runner row locally so lease finalization remains retryable
|
|
42
|
+
// without a workflow/runner scope lock.
|
|
43
|
+
requireTerminalState: false
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
18
47
|
export async function getWorkspaceJobCounts(params) {
|
|
19
48
|
const [pendingRows, runningRows] = await Promise.all([
|
|
20
49
|
db().select({
|
|
@@ -40,11 +69,9 @@ export async function getWorkspaceJobCounts(params) {
|
|
|
40
69
|
running: runningByWorkspace.get(workspaceId) ?? 0
|
|
41
70
|
}));
|
|
42
71
|
}
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
// The per-execution advisory lock serializes retries with lease expiry/reconciliation, and the
|
|
47
|
-
// durable lease-expired event prevents a retry from re-queueing an execution already reaped.
|
|
72
|
+
// The workflows outbox delivers queue facts at least once. The per-execution advisory lock and
|
|
73
|
+
// pending/running checks make replay a no-op across the claim transition. A durable lease-expired
|
|
74
|
+
// event also prevents a delayed replay from resurrecting an execution already reaped locally.
|
|
48
75
|
export async function enqueueJobExecution(params) {
|
|
49
76
|
const requiredLabels = [
|
|
50
77
|
...canonicalizeLabels(params.requiredLabels)
|
|
@@ -52,6 +79,10 @@ export async function enqueueJobExecution(params) {
|
|
|
52
79
|
if (requiredLabels.length === 0) throw new EmptyRequiredLabelsError();
|
|
53
80
|
const enqueued = await db().transaction(async (tx)=>{
|
|
54
81
|
await lockJobExecution(tx, params.jobExecutionId);
|
|
82
|
+
const [running] = await tx.select({
|
|
83
|
+
jobExecutionId: runningJobExecutions.jobExecutionId
|
|
84
|
+
}).from(runningJobExecutions).where(eq(runningJobExecutions.jobExecutionId, params.jobExecutionId)).limit(1);
|
|
85
|
+
if (running) return false;
|
|
55
86
|
const [leaseExpired] = await tx.select({
|
|
56
87
|
id: runnersOutbox.id
|
|
57
88
|
}).from(runnersOutbox).where(and(eq(runnersOutbox.eventType, RUNNER_JOB_LEASE_EXPIRED), sql`${runnersOutbox.payload}->>'jobExecutionId' = ${params.jobExecutionId}`)).limit(1);
|
|
@@ -63,27 +94,14 @@ export async function enqueueJobExecution(params) {
|
|
|
63
94
|
jobId: params.jobId,
|
|
64
95
|
jobExecutionId: params.jobExecutionId,
|
|
65
96
|
projectId: params.projectId,
|
|
66
|
-
requiredLabels
|
|
97
|
+
requiredLabels,
|
|
98
|
+
createdAt: params.queuedAt
|
|
67
99
|
}).onConflictDoNothing({
|
|
68
100
|
target: pendingJobExecutions.jobExecutionId
|
|
69
101
|
}).returning({
|
|
70
102
|
createdAt: pendingJobExecutions.createdAt
|
|
71
103
|
});
|
|
72
|
-
|
|
73
|
-
// emitted the queued event (durably, in the outbox), so re-emitting would
|
|
74
|
-
// only add a redundant row the subscriber coalesces away. Skip it.
|
|
75
|
-
if (!inserted) return false;
|
|
76
|
-
await writeOutboxEvent(tx, runnersOutbox, {
|
|
77
|
-
type: RUNNER_JOB_QUEUED,
|
|
78
|
-
payload: {
|
|
79
|
-
workflowRunId: params.workflowRunId,
|
|
80
|
-
workflowRunAttemptId: params.workflowRunAttemptId,
|
|
81
|
-
jobId: params.jobId,
|
|
82
|
-
jobExecutionId: params.jobExecutionId,
|
|
83
|
-
queuedAt: inserted.createdAt.toISOString()
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
return true;
|
|
104
|
+
return inserted !== undefined;
|
|
87
105
|
});
|
|
88
106
|
if (enqueued) jobExecutionEnqueuedCount.add(1);
|
|
89
107
|
}
|
|
@@ -226,36 +244,18 @@ async function touchRunnerSessionLiveness(params) {
|
|
|
226
244
|
updatedAt: sql`now()`
|
|
227
245
|
}).where(and(eq(runnerSessions.id, params.runnerSessionId), eq(runnerSessions.workspaceId, params.workspaceId), sql`${runnerSessions.updatedAt} < now() - (${params.throttleSeconds} || ' seconds')::interval`));
|
|
228
246
|
}
|
|
229
|
-
/**
|
|
230
|
-
* Releases a job execution's lease when the orchestration workflow finalizes it: deletes the
|
|
231
|
-
* running-job-execution row AND any lingering pending row for the same execution, in one tx.
|
|
232
|
-
* Idempotent (0-row no-op), no token scope (the workflow is authoritative), and
|
|
233
|
-
* emits no event: the workflow already owns the outcome. Sweeping the pending row
|
|
234
|
-
* too closes the at-least-once window where an enqueue retry left an orphan that a
|
|
235
|
-
* later claim would otherwise pick up for an already-finished job execution.
|
|
236
|
-
*/ export async function releaseJobExecution(params) {
|
|
237
|
-
await db().transaction(async (tx)=>{
|
|
238
|
-
await lockJobExecution(tx, params.jobExecutionId);
|
|
239
|
-
// Delete pending before running to match `claimPendingJobExecution`'s lock-acquisition
|
|
240
|
-
// order (it locks the pending row first, then the running row). A concurrent
|
|
241
|
-
// claim picking up an orphan pending row for this same job execution would otherwise
|
|
242
|
-
// deadlock against the reverse order here.
|
|
243
|
-
await tx.delete(pendingJobExecutions).where(eq(pendingJobExecutions.jobExecutionId, params.jobExecutionId));
|
|
244
|
-
await tx.delete(runningJobExecutions).where(eq(runningJobExecutions.jobExecutionId, params.jobExecutionId));
|
|
245
|
-
});
|
|
246
|
-
}
|
|
247
247
|
/**
|
|
248
248
|
* Reaps stale leases (bounded by `limit`), emitting one
|
|
249
249
|
* `runners.job.lease_expired` event per reaped job execution.
|
|
250
250
|
*
|
|
251
251
|
* The cutoff is re-checked in the DELETE, not just the locking subquery, so a
|
|
252
252
|
* heartbeat landing mid-call spares the live row. Each reaped execution also sweeps its
|
|
253
|
-
* pending row
|
|
254
|
-
* that a later claim re-runs as an already-finished job execution.
|
|
253
|
+
* pending row so an at-least-once queue replay cannot leave an orphan.
|
|
255
254
|
*
|
|
256
|
-
* Locks pending-then-running to match `claimPendingJobExecution
|
|
257
|
-
* `reconcileTerminalJobExecution`. The stale candidate scan intentionally does not lock
|
|
258
|
-
* rows first; the running-row DELETE re-checks the stale predicate after the
|
|
255
|
+
* Locks pending-then-running to match `claimPendingJobExecution` and
|
|
256
|
+
* `reconcileTerminalJobExecution`. The stale candidate scan intentionally does not lock
|
|
257
|
+
* running rows first; the running-row DELETE re-checks the stale predicate after the
|
|
258
|
+
* pending-row sweep.
|
|
259
259
|
*/ export async function expireStuckJobExecutions(params) {
|
|
260
260
|
const reaped = await db().transaction(async (tx)=>{
|
|
261
261
|
const heartbeatCutoff = sql`now() - (${params.thresholdSeconds} || ' seconds')::interval`;
|
|
@@ -281,9 +281,12 @@ async function touchRunnerSessionLiveness(params) {
|
|
|
281
281
|
workflowRunId: runningJobExecutions.workflowRunId,
|
|
282
282
|
workflowRunAttemptId: runningJobExecutions.workflowRunAttemptId,
|
|
283
283
|
jobId: runningJobExecutions.jobId,
|
|
284
|
-
jobExecutionId: runningJobExecutions.jobExecutionId
|
|
284
|
+
jobExecutionId: runningJobExecutions.jobExecutionId,
|
|
285
|
+
provisionerId: runningJobExecutions.provisionerId,
|
|
286
|
+
providerRunnerId: runningJobExecutions.providerRunnerId
|
|
285
287
|
});
|
|
286
288
|
if (deleted.length === 0) return [];
|
|
289
|
+
await releaseReservationsForTerminalRunningRows(tx, deleted);
|
|
287
290
|
await writeOutboxEvents(tx, runnersOutbox, deleted.map((row)=>({
|
|
288
291
|
type: RUNNER_JOB_LEASE_EXPIRED,
|
|
289
292
|
payload: {
|
|
@@ -293,7 +296,12 @@ async function touchRunnerSessionLiveness(params) {
|
|
|
293
296
|
jobExecutionId: row.jobExecutionId
|
|
294
297
|
}
|
|
295
298
|
})));
|
|
296
|
-
return deleted
|
|
299
|
+
return deleted.map(({ workflowRunId, workflowRunAttemptId, jobId, jobExecutionId })=>({
|
|
300
|
+
workflowRunId,
|
|
301
|
+
workflowRunAttemptId,
|
|
302
|
+
jobId,
|
|
303
|
+
jobExecutionId
|
|
304
|
+
}));
|
|
297
305
|
});
|
|
298
306
|
if (reaped.length > 0) jobExecutionLeaseExpiredCount.add(reaped.length);
|
|
299
307
|
return reaped;
|
|
@@ -431,8 +439,8 @@ export async function recordHeartbeat(params) {
|
|
|
431
439
|
}
|
|
432
440
|
/**
|
|
433
441
|
* Reconciles a terminal job execution with runner state in one transaction: removes its pending
|
|
434
|
-
* queue row
|
|
435
|
-
* idempotent and preserves the first cancellation-request timestamp.
|
|
442
|
+
* queue row, requests cancellation on its running lease, and converges any linked reservation.
|
|
443
|
+
* The operation is idempotent and preserves the first cancellation-request timestamp.
|
|
436
444
|
*/ export async function reconcileTerminalJobExecution(params) {
|
|
437
445
|
await db().transaction(async (tx)=>{
|
|
438
446
|
await lockJobExecution(tx, params.jobExecutionId);
|
|
@@ -440,18 +448,13 @@ export async function recordHeartbeat(params) {
|
|
|
440
448
|
// pending rows with SKIP LOCKED before inserting the running lease, so this ordering makes
|
|
441
449
|
// a concurrent terminal reconciliation either win before claim or cancel the new lease.
|
|
442
450
|
await tx.delete(pendingJobExecutions).where(eq(pendingJobExecutions.jobExecutionId, params.jobExecutionId));
|
|
443
|
-
await tx.update(runningJobExecutions).set({
|
|
451
|
+
const cancelledRunningRows = await tx.update(runningJobExecutions).set({
|
|
444
452
|
cancellationRequestedAt: sql`COALESCE(${runningJobExecutions.cancellationRequestedAt}, now())`
|
|
445
|
-
}).where(eq(runningJobExecutions.jobExecutionId, params.jobExecutionId))
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
await db().transaction(async (tx)=>{
|
|
451
|
-
await tx.delete(pendingJobExecutions).where(inArray(pendingJobExecutions.jobId, params.jobIds));
|
|
452
|
-
await tx.update(runningJobExecutions).set({
|
|
453
|
-
cancellationRequestedAt: sql`COALESCE(${runningJobExecutions.cancellationRequestedAt}, now())`
|
|
454
|
-
}).where(inArray(runningJobExecutions.jobId, params.jobIds));
|
|
453
|
+
}).where(eq(runningJobExecutions.jobExecutionId, params.jobExecutionId)).returning({
|
|
454
|
+
provisionerId: runningJobExecutions.provisionerId,
|
|
455
|
+
providerRunnerId: runningJobExecutions.providerRunnerId
|
|
456
|
+
});
|
|
457
|
+
await releaseReservationsForTerminalRunningRows(tx, cancelledRunningRows);
|
|
455
458
|
});
|
|
456
459
|
}
|
|
457
460
|
|