@@ -99,6 +99,7 @@ public final class PostgresJobQueue: JobQueueDriver {
99
99
public init ( client: PostgresClient , migrations: DatabaseMigrations , configuration: Configuration = . init( ) , logger: Logger ) async {
100
100
self . client = client
101
101
self . configuration = configuration
102
+ self . jobRegistry = . init( )
102
103
self . logger = logger
103
104
self . isStopped = . init( false )
104
105
self . migrations = migrations
@@ -162,15 +163,23 @@ public final class PostgresJobQueue: JobQueueDriver {
162
163
}
163
164
}
164
165
166
+ /// Register job
167
+ /// - Parameters:
168
+ /// - job: Job Definition
169
+ public func registerJob< Parameters: Codable & Sendable > ( _ job: JobDefinition < Parameters > ) {
170
+ self . jobRegistry. registerJob ( job)
171
+ }
172
+
165
173
/// Push Job onto queue
166
174
/// - Returns: Identifier of queued job
167
- @discardableResult public func push( _ buffer: ByteBuffer , options: JobOptions ) async throws -> JobID {
175
+ @discardableResult public func push< Parameters> ( _ jobRequest: JobRequest < Parameters > , options: JobOptions ) async throws -> JobID {
176
+ let buffer = try self . jobRegistry. encode ( jobRequest: jobRequest)
177
+ let jobID = JobID ( )
168
178
try await self . client. withTransaction ( logger: self . logger) { connection in
169
- let queuedJob = QueuedJob < JobID > ( id: . init( ) , jobBuffer: buffer)
170
- try await self . add ( queuedJob, connection: connection)
171
- try await self . addToQueue ( jobId: queuedJob. id, connection: connection, delayUntil: options. delayUntil)
172
- return queuedJob. id
179
+ try await self . add ( jobID: jobID, jobBuffer: buffer, connection: connection)
180
+ try await self . addToQueue ( jobID: jobID, connection: connection, delayUntil: options. delayUntil)
173
181
}
182
+ return jobID
174
183
}
175
184
176
185
/// Retry a job
@@ -184,13 +193,13 @@ public final class PostgresJobQueue: JobQueueDriver {
184
193
}
185
194
186
195
/// This is called to say job has finished processing and it can be deleted
187
- public func finished( jobId : JobID ) async throws {
188
- try await self . delete ( jobId : jobId )
196
+ public func finished( jobID : JobID ) async throws {
197
+ try await self . delete ( jobID : jobID )
189
198
}
190
199
191
200
/// This is called to say job has failed to run and should be put aside
192
- public func failed( jobId : JobID , error: Error ) async throws {
193
- try await self . setStatus ( jobId : jobId , status: . failed)
201
+ public func failed( jobID : JobID , error: Error ) async throws {
202
+ try await self . setStatus ( jobID : jobID , status: . failed)
194
203
}
195
204
196
205
/// stop serving jobs
@@ -223,9 +232,9 @@ public final class PostgresJobQueue: JobQueueDriver {
223
232
)
224
233
}
225
234
226
- func popFirst( ) async throws -> QueuedJob < JobID > ? {
235
+ func popFirst( ) async throws -> JobQueueResult < JobID > ? {
227
236
do {
228
- let result = try await self . client. withTransaction ( logger: self . logger) { connection -> Result < QueuedJob < JobID > ? , Error > in
237
+ let result = try await self . client. withTransaction ( logger: self . logger) { connection -> Result < JobQueueResult < JobID > ? , Error > in
229
238
while true {
230
239
try Task . checkCancellation ( )
231
240
@@ -248,24 +257,33 @@ public final class PostgresJobQueue: JobQueueDriver {
248
257
logger: self . logger
249
258
)
250
259
// return nil if nothing in queue
251
- guard let jobId = try await stream. decode ( UUID . self, context: . default) . first ( where: { _ in true } ) else {
260
+ guard let jobID = try await stream. decode ( UUID . self, context: . default) . first ( where: { _ in true } ) else {
252
261
return Result . success ( nil )
253
262
}
254
263
// select job from job table
255
264
let stream2 = try await connection. query (
256
- " SELECT job FROM _hb_pg_jobs WHERE id = \( jobId ) " ,
265
+ " SELECT job FROM _hb_pg_jobs WHERE id = \( jobID ) " ,
257
266
logger: self . logger
258
267
)
259
268
260
269
do {
261
- try await self . setStatus ( jobId : jobId , status: . processing, connection: connection)
270
+ try await self . setStatus ( jobID : jobID , status: . processing, connection: connection)
262
271
// if failed to find a job in the job table try getting another index
263
272
guard let buffer = try await stream2. decode ( ByteBuffer . self, context: . default) . first ( where: { _ in true } ) else {
264
273
continue
265
274
}
266
- return Result . success ( QueuedJob ( id: jobId, jobBuffer: buffer) )
275
+ do {
276
+ let jobInstance = try self . jobRegistry. decode ( buffer)
277
+ return Result . success ( . init( id: jobID, result: . success( jobInstance) ) )
278
+ } catch let error as JobQueueError {
279
+ return Result . success ( . init( id: jobID, result: . failure( error) ) )
280
+ } catch {
281
+ return Result . success (
282
+ . init( id: jobID, result: . failure( JobQueueError ( code: . unrecognised, jobName: nil , details: " \( error) " ) ) )
283
+ )
284
+ }
267
285
} catch {
268
- try await self . setStatus ( jobId : jobId , status: . failed, connection: connection)
286
+ try await self . setStatus ( jobID : jobID , status: . failed, connection: connection)
269
287
return Result . failure (
270
288
JobQueueError (
271
289
code: . decodeJobFailed,
@@ -296,11 +314,11 @@ public final class PostgresJobQueue: JobQueueDriver {
296
314
}
297
315
}
298
316
299
- func add( _ job : QueuedJob < JobID > , connection: PostgresConnection ) async throws {
317
+ func add( jobID : JobID , jobBuffer : ByteBuffer , connection: PostgresConnection ) async throws {
300
318
try await connection. query (
301
319
"""
302
320
INSERT INTO _hb_pg_jobs (id, job, status)
303
- VALUES ( \( job . id ) , \( job . jobBuffer) , \( Status . pending) )
321
+ VALUES ( \( jobID ) , \( jobBuffer) , \( Status . pending) )
304
322
""" ,
305
323
logger: self . logger
306
324
)
@@ -319,35 +337,36 @@ public final class PostgresJobQueue: JobQueueDriver {
319
337
)
320
338
}
321
339
322
- func delete( jobId : JobID ) async throws {
340
+ func delete( jobID : JobID ) async throws {
323
341
try await self . client. query (
324
- " DELETE FROM _hb_pg_jobs WHERE id = \( jobId ) " ,
342
+ " DELETE FROM _hb_pg_jobs WHERE id = \( jobID ) " ,
325
343
logger: self . logger
326
344
)
327
345
}
328
346
329
- func addToQueue( jobId: JobID , connection: PostgresConnection , delayUntil: Date ) async throws {
347
+ func addToQueue( jobID: JobID , connection: PostgresConnection , delayUntil: Date ) async throws {
348
+ // TODO: assign Date.now in swift-jobs options?
330
349
try await connection. query (
331
350
"""
332
351
INSERT INTO _hb_pg_job_queue (job_id, createdAt, delayed_until)
333
- VALUES ( \( jobId ) , \( Date . now) , \( delayUntil) )
352
+ VALUES ( \( jobID ) , \( Date . now) , \( delayUntil) )
334
353
-- We have found an existing job with the same id, SKIP this INSERT
335
354
ON CONFLICT (job_id) DO NOTHING
336
355
""" ,
337
356
logger: self . logger
338
357
)
339
358
}
340
359
341
- func setStatus( jobId : JobID , status: Status , connection: PostgresConnection ) async throws {
360
+ func setStatus( jobID : JobID , status: Status , connection: PostgresConnection ) async throws {
342
361
try await connection. query (
343
- " UPDATE _hb_pg_jobs SET status = \( status) , lastModified = \( Date . now) WHERE id = \( jobId ) " ,
362
+ " UPDATE _hb_pg_jobs SET status = \( status) , lastModified = \( Date . now) WHERE id = \( jobID ) " ,
344
363
logger: self . logger
345
364
)
346
365
}
347
366
348
- func setStatus( jobId : JobID , status: Status ) async throws {
367
+ func setStatus( jobID : JobID , status: Status ) async throws {
349
368
try await self . client. query (
350
- " UPDATE _hb_pg_jobs SET status = \( status) , lastModified = \( Date . now) WHERE id = \( jobId ) " ,
369
+ " UPDATE _hb_pg_jobs SET status = \( status) , lastModified = \( Date . now) WHERE id = \( jobID ) " ,
351
370
logger: self . logger
352
371
)
353
372
}
@@ -375,20 +394,22 @@ public final class PostgresJobQueue: JobQueueDriver {
375
394
case . rerun:
376
395
let jobs = try await getJobs ( withStatus: status)
377
396
self . logger. info ( " Moving \( jobs. count) jobs with status: \( status) to job queue " )
378
- for jobId in jobs {
379
- try await self . addToQueue ( jobId : jobId , connection: connection, delayUntil: Date . now)
397
+ for jobID in jobs {
398
+ try await self . addToQueue ( jobID : jobID , connection: connection, delayUntil: Date . now)
380
399
}
381
400
382
401
case . doNothing:
383
402
break
384
403
}
385
404
}
405
+
406
+ let jobRegistry : JobRegistry
386
407
}
387
408
388
409
/// extend PostgresJobQueue to conform to AsyncSequence
389
410
extension PostgresJobQueue {
390
411
public struct AsyncIterator : AsyncIteratorProtocol {
391
- public typealias Element = QueuedJob < JobID >
412
+ public typealias Element = JobQueueResult < JobID >
392
413
393
414
let queue : PostgresJobQueue
394
415
0 commit comments