@@ -24,6 +24,7 @@ import { MetricsCounter } from '@sofie-automation/corelib/dist/prometheus'
2424import { isInTestWrite } from '../security/securityVerify'
2525import { UserError } from '@sofie-automation/corelib/dist/error'
2626import { QueueJobOptions } from '@sofie-automation/job-worker/dist/jobs'
27+ import _ from 'underscore'
2728
2829const FREEZE_LIMIT = 1000 // how long to wait for a response to a Ping
2930const RESTART_TIMEOUT = 30000 // how long to wait for a restart to complete before throwing an error
@@ -52,8 +53,9 @@ const metricsQueueErrorsCounter = new MetricsCounter({
5253} )
5354
5455interface JobQueue {
55- jobs : Array < JobEntry | null >
56- // lowPriorityJobs: Array<JobEntry>
56+ // TODO: figure out why there can be null types in the array.
57+ jobsHighPriority : Array < JobEntry | null >
58+ jobsLowPriority : Array < JobEntry | null >
5759
5860 /** Notify that there is a job waiting (aka worker is long-polling) */
5961 notifyWorker : ManualPromise < void > | null
@@ -78,7 +80,8 @@ function getOrCreateQueue(queueName: string): JobQueue {
7880 let queue = queues . get ( queueName )
7981 if ( ! queue ) {
8082 queue = {
81- jobs : [ ] ,
83+ jobsHighPriority : [ ] ,
84+ jobsLowPriority : [ ] ,
8285 notifyWorker : null ,
8386 metricsTotal : metricsQueueTotalCounter . labels ( queueName ) ,
8487 metricsSuccess : metricsQueueSuccessCounter . labels ( queueName ) ,
@@ -120,7 +123,7 @@ async function jobFinished(
120123async function waitForNextJob ( queueName : string ) : Promise < void > {
121124 // Check if there is a job waiting:
122125 const queue = getOrCreateQueue ( queueName )
123- if ( queue . jobs . length > 0 ) {
126+ if ( queue . jobsHighPriority . length + queue . jobsLowPriority . length > 0 ) {
124127 return
125128 }
126129 // No job ready, do a long-poll
@@ -147,7 +150,14 @@ async function waitForNextJob(queueName: string): Promise<void> {
147150async function getNextJob ( queueName : string ) : Promise < JobSpec | null > {
148151 // Check if there is a job waiting:
149152 const queue = getOrCreateQueue ( queueName )
150- const job = queue . jobs . shift ( )
153+ // Prefer high priority jobs
154+ let job : JobEntry | null | undefined
155+ if ( queue . jobsHighPriority . length > 0 ) {
156+ job = queue . jobsLowPriority . shift ( )
157+ } else {
158+ job = queue . jobsHighPriority . shift ( )
159+ }
160+
151161 if ( job ) {
152162 // If there is a completion handler, register it for execution
153163 runningJobs . set ( job . spec . id , {
@@ -179,7 +189,7 @@ async function interruptJobStream(queueName: string): Promise<void> {
179189 }
180190 } )
181191 } else {
182- queue . jobs . unshift ( null )
192+ queue . jobsLowPriority . unshift ( null )
183193 }
184194}
185195async function queueJobWithoutResult (
@@ -202,13 +212,28 @@ async function queueJobWithoutResult(
202212 )
203213}
204214
205- function queueJobInner ( queueName : string , jobToQueue : JobEntry , options : QueueJobOptions ) : void {
215+ function queueJobInner ( queueName : string , jobToQueue : JobEntry , options ? : QueueJobOptions ) : void {
206216 // Put the job at the end of the queue:
207217 const queue = getOrCreateQueue ( queueName )
208- queue . jobs . push ( jobToQueue )
209- queue . metricsTotal . inc ( )
210218
211- // nocommit - use options
219+ // Debounce: skip if an identical job is already queued
220+ if ( options ?. debounce ) {
221+ const alreadyQueued = [ ...queue . jobsHighPriority , ...queue . jobsLowPriority ] . some (
222+ ( job ) => job && job . spec . name === jobToQueue . spec . name && _ . isEqual ( job . spec . data , jobToQueue . spec . data )
223+ )
224+
225+ if ( alreadyQueued ) {
226+ logger . debug ( `Debounced duplicate job "${ jobToQueue . spec . name } " in queue "${ queueName } "` )
227+ return
228+ }
229+ }
230+
231+ // Queue the job based on priority
232+ if ( options ?. lowPriority ) queue . jobsLowPriority . push ( jobToQueue )
233+ else queue . jobsHighPriority . push ( jobToQueue )
234+
235+ queue . metricsTotal . inc ( )
236+ // nocommit - add test for priority and debounce
212237
213238 // If there is a worker waiting to pick up a job
214239 if ( queue . notifyWorker ) {
@@ -232,7 +257,7 @@ function queueJobAndWrapResult<TRes>(
232257 queueName : string ,
233258 job : JobSpec ,
234259 now : Time ,
235- options : QueueJobOptions | undefined
260+ options ? : QueueJobOptions
236261) : WorkerJob < TRes > {
237262 const { result, completionHandler } = generateCompletionHandler < TRes > ( job . id , now )
238263
@@ -242,7 +267,7 @@ function queueJobAndWrapResult<TRes>(
242267 spec : job ,
243268 completionHandler : completionHandler ,
244269 } ,
245- options ?? { }
270+ options
246271 )
247272
248273 return result
0 commit comments