2
2
//
3
3
// This source file is part of the Hummingbird server framework project
4
4
//
5
- // Copyright (c) 2024 the Hummingbird authors
5
+ // Copyright (c) 2024-2025 the Hummingbird authors
6
6
// Licensed under Apache License v2.0
7
7
//
8
8
// See LICENSE.txt for license information
@@ -173,10 +173,10 @@ public final class PostgresJobQueue: JobQueueDriver {
173
173
/// Push Job onto queue
174
174
/// - Returns: Identifier of queued job
175
175
@discardableResult public func push< Parameters> ( _ jobRequest: JobRequest < Parameters > , options: JobOptions ) async throws -> JobID {
176
- let buffer = try self . jobRegistry. encode ( jobRequest: jobRequest)
176
+ //. let buffer = try self.jobRegistry.encode(jobRequest: jobRequest)
177
177
let jobID = JobID ( )
178
178
try await self . client. withTransaction ( logger: self . logger) { connection in
179
- try await self . add ( jobID: jobID, jobBuffer : buffer , connection: connection)
179
+ try await self . add ( jobID: jobID, jobRequest : jobRequest , connection: connection)
180
180
try await self . addToQueue ( jobID: jobID, connection: connection, delayUntil: options. delayUntil)
181
181
}
182
182
return jobID
@@ -233,10 +233,15 @@ public final class PostgresJobQueue: JobQueueDriver {
233
233
}
234
234
235
235
func popFirst( ) async throws -> JobQueueResult < JobID > ? {
236
+ enum PopFirstResult {
237
+ case nothing
238
+ case result( Result < PostgresRow , Error > , jobID: JobID )
239
+ }
236
240
do {
237
241
// The withTransaction closure returns a Result<(ByteBuffer, JobID)?, Error> because
238
242
// we want to be able to exit the closure without cancelling the transaction
239
- let result = try await self . client. withTransaction ( logger: self . logger) { connection -> Result < ( ByteBuffer , JobID ) ? , Error > in
243
+ let popFirstResult = try await self . client. withTransaction ( logger: self . logger) {
244
+ connection -> PopFirstResult in
240
245
try Task . checkCancellation ( )
241
246
242
247
let stream = try await connection. query (
@@ -259,7 +264,7 @@ public final class PostgresJobQueue: JobQueueDriver {
259
264
)
260
265
// return nil if nothing in queue
261
266
guard let jobID = try await stream. decode ( UUID . self, context: . default) . first ( where: { _ in true } ) else {
262
- return Result . success ( nil )
267
+ return . nothing
263
268
}
264
269
// set job status to processing
265
270
try await self . setStatus ( jobID: jobID, status: . processing, connection: connection)
@@ -269,25 +274,30 @@ public final class PostgresJobQueue: JobQueueDriver {
269
274
" SELECT job FROM _hb_pg_jobs WHERE id = \( jobID) " ,
270
275
logger: self . logger
271
276
)
272
- guard let buffer = try await stream2. decode ( ByteBuffer . self , context : . default ) . first ( where: { _ in true } ) else {
277
+ guard let row = try await stream2. first ( where: { _ in true } ) else {
273
278
logger. error (
274
279
" Failed to find job with id " ,
275
280
metadata: [
276
281
" JobID " : " \( jobID) "
277
282
]
278
283
)
279
- // if failed to find the job in the job table return nil
280
- return . success ( nil )
284
+ // if failed to find the job in the job table return error
285
+ return . result ( . failure ( JobQueueError ( code : . unrecognisedJobId , jobName : nil ) ) , jobID : jobID )
281
286
}
282
- return . success( ( buffer, jobID) )
283
-
287
+ return . result( . success( row) , jobID: jobID)
284
288
}
285
- guard let ( buffer, jobID) = try result. get ( ) else { return nil }
286
- do {
287
- let jobInstance = try self . jobRegistry. decode ( buffer)
288
- return JobQueueResult ( id: jobID, result: . success( jobInstance) )
289
- } catch let error as JobQueueError {
290
- return JobQueueResult ( id: jobID, result: . failure( error) )
289
+
290
+ switch popFirstResult {
291
+ case . nothing:
292
+ return nil
293
+ case . result( let result, let jobID) :
294
+ do {
295
+ let row = try result. get ( )
296
+ let jobInstance = try row. decode ( AnyDecodableJob . self, context: . withJobRegistry( self . jobRegistry) ) . job
297
+ return JobQueueResult ( id: jobID, result: . success( jobInstance) )
298
+ } catch let error as JobQueueError {
299
+ return JobQueueResult ( id: jobID, result: . failure( error) )
300
+ }
291
301
}
292
302
} catch let error as PSQLError {
293
303
logger. error (
@@ -308,11 +318,11 @@ public final class PostgresJobQueue: JobQueueDriver {
308
318
}
309
319
}
310
320
311
- func add( jobID: JobID , jobBuffer : ByteBuffer , connection: PostgresConnection ) async throws {
321
+ func add< Parameters > ( jobID: JobID , jobRequest : JobRequest < Parameters > , connection: PostgresConnection ) async throws {
312
322
try await connection. query (
313
323
"""
314
324
INSERT INTO _hb_pg_jobs (id, job, status)
315
- VALUES ( \( jobID) , \( jobBuffer ) , \( Status . pending) )
325
+ VALUES ( \( jobID) , \( jobRequest ) , \( Status . pending) )
316
326
""" ,
317
327
logger: self . logger
318
328
)
@@ -443,3 +453,12 @@ extension JobQueueDriver where Self == PostgresJobQueue {
443
453
await Self ( client: client, migrations: migrations, configuration: configuration, logger: logger)
444
454
}
445
455
}
456
+
457
+ extension PostgresDecodingContext where JSONDecoder == Foundation . JSONDecoder {
458
+ /// A ``PostgresDecodingContext`` that uses a Foundation `JSONDecoder` with job registry attached as userInfo.
459
+ public static func withJobRegistry( _ jobRegistry: JobRegistry ) -> PostgresDecodingContext {
460
+ let jsonDecoder = JSONDecoder ( )
461
+ jsonDecoder. userInfo [ . configuration] = jobRegistry
462
+ return PostgresDecodingContext ( jsonDecoder: jsonDecoder)
463
+ }
464
+ }
0 commit comments