@@ -16,6 +16,7 @@ import {
1616 Prisma ,
1717 PrismaClient ,
1818 PrismaClientOrTransaction ,
19+ PrismaReplicaClient ,
1920 TaskRun ,
2021 TaskRunExecutionSnapshot ,
2122 Waitpoint ,
@@ -50,6 +51,7 @@ import { TtlSystem } from "./systems/ttlSystem.js";
5051import { WaitpointSystem } from "./systems/waitpointSystem.js" ;
5152import { EngineWorker , HeartbeatTimeouts , RunEngineOptions , TriggerParams } from "./types.js" ;
5253import { workerCatalog } from "./workerCatalog.js" ;
54+ import { getFinalRunStatuses , isFinalRunStatus } from "./statuses.js" ;
5355
5456export class RunEngine {
5557 private runLockRedis : Redis ;
@@ -61,6 +63,7 @@ export class RunEngine {
6163 private heartbeatTimeouts : HeartbeatTimeouts ;
6264
6365 prisma : PrismaClient ;
66+ readOnlyPrisma : PrismaReplicaClient ;
6467 runQueue : RunQueue ;
6568 eventBus : EventBus = new EventEmitter < EventBusEvents > ( ) ;
6669 executionSnapshotSystem : ExecutionSnapshotSystem ;
@@ -79,6 +82,7 @@ export class RunEngine {
7982 constructor ( private readonly options : RunEngineOptions ) {
8083 this . logger = options . logger ?? new Logger ( "RunEngine" , this . options . logLevel ?? "info" ) ;
8184 this . prisma = options . prisma ;
85+ this . readOnlyPrisma = options . readOnlyPrisma ?? this . prisma ;
8286 this . runLockRedis = createRedisClient (
8387 {
8488 ...options . runLock . redis ,
@@ -123,7 +127,7 @@ export class RunEngine {
123127 defaultEnvConcurrencyLimit : options . queue ?. defaultEnvConcurrency ?? 10 ,
124128 } ) ,
125129 defaultEnvConcurrency : options . queue ?. defaultEnvConcurrency ?? 10 ,
126- logger : new Logger ( "RunQueue" , this . options . logLevel ?? "info" ) ,
130+ logger : new Logger ( "RunQueue" , options . queue ? .logLevel ?? "info" ) ,
127131 redis : { ...options . queue . redis , keyPrefix : `${ options . queue . redis . keyPrefix } runqueue:` } ,
128132 retryOptions : options . queue ?. retryOptions ,
129133 workerOptions : {
@@ -133,6 +137,14 @@ export class RunEngine {
133137 immediatePollIntervalMs : options . worker . immediatePollIntervalMs ,
134138 shutdownTimeoutMs : options . worker . shutdownTimeoutMs ,
135139 } ,
140+ concurrencySweeper : {
141+ enabled : ! options . worker . disabled ,
142+ scanIntervalMs : options . queue ?. concurrencySweeper ?. scanIntervalMs ?? 60_000 ,
143+ processMarkedIntervalMs :
144+ options . queue ?. concurrencySweeper ?. processMarkedIntervalMs ?? 5_000 ,
145+ logLevel : options . queue ?. concurrencySweeper ?. logLevel ?? options . queue ?. logLevel ,
146+ callback : this . #concurrencySweeperCallback. bind ( this ) ,
147+ } ,
136148 shardCount : options . queue ?. shardCount ,
137149 masterQueueConsumersDisabled : options . queue ?. masterQueueConsumersDisabled ,
138150 masterQueueConsumersIntervalMs : options . queue ?. masterQueueConsumersIntervalMs ,
@@ -1329,4 +1341,44 @@ export class RunEngine {
13291341 }
13301342 } ) ;
13311343 }
1344+
1345+ async #concurrencySweeperCallback(
1346+ runIds : string [ ]
1347+ ) : Promise < Array < { id : string ; orgId : string } > > {
1348+ const runs = await this . readOnlyPrisma . taskRun . findMany ( {
1349+ where : {
1350+ id : { in : runIds } ,
1351+ completedAt : {
1352+ lte : new Date ( Date . now ( ) - 1000 * 60 * 10 ) , // This only finds runs that were completed more than 10 minutes ago
1353+ } ,
1354+ organizationId : {
1355+ not : null ,
1356+ } ,
1357+ status : {
1358+ in : getFinalRunStatuses ( ) ,
1359+ } ,
1360+ } ,
1361+ select : {
1362+ id : true ,
1363+ status : true ,
1364+ organizationId : true ,
1365+ } ,
1366+ } ) ;
1367+
1368+ // Log the finished runs
1369+ for ( const run of runs ) {
1370+ this . logger . info ( "Concurrency sweeper callback found finished run" , {
1371+ runId : run . id ,
1372+ orgId : run . organizationId ,
1373+ status : run . status ,
1374+ } ) ;
1375+ }
1376+
1377+ return runs
1378+ . filter ( ( run ) => ! ! run . organizationId )
1379+ . map ( ( run ) => ( {
1380+ id : run . id ,
1381+ orgId : run . organizationId ! ,
1382+ } ) ) ;
1383+ }
13321384}
0 commit comments