Skip to content

Commit 4f42614

Browse files
authored
Add delay property (#18)
* delay * add delay logic * change to delayUntil
1 parent 992440a commit 4f42614

File tree

5 files changed

+42
-5
lines changed

5 files changed

+42
-5
lines changed

Sources/Jobs/JobStorage.swift

+5-1
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,23 @@ public struct JobStorage: Codable {
1212
/// The maxRetryCount for the `Job`.
1313
var maxRetryCount: Int
1414

15+
/// A date to execute this job after
16+
var delayUntil: Date?
17+
1518
/// A unique ID for the job
1619
public internal(set) var id: String
1720

1821
/// The name of the `Job`
1922
var jobName: String
2023

2124
/// Creates a new `JobStorage` holding object
22-
public init(key: String, data: Data, maxRetryCount: Int, id: String, jobName: String) {
25+
public init(key: String, data: Data, maxRetryCount: Int, id: String, jobName: String, delayUntil: Date?) {
2326
self.key = key
2427
self.data = data
2528
self.maxRetryCount = maxRetryCount
2629
self.id = id
2730
self.jobName = jobName
31+
self.delayUntil = delayUntil
2832
}
2933

3034
/// Returns a string representation of the JobStorage object

Sources/Jobs/JobsCommand.swift

+15-1
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,12 @@ public final class JobsCommand: Command {
113113
) throws {
114114
let queue = QueueName(name: queueName)
115115
let key = queue.makeKey(with: queueService.persistenceKey)
116-
_ = eventLoop.scheduleRepeatedAsyncTask(initialDelay: .seconds(0), delay: queueService.refreshInterval) { task -> EventLoopFuture<Void> in
116+
117+
// Schedule the repeating task
118+
_ = eventLoop.scheduleRepeatedAsyncTask(
119+
initialDelay: .seconds(0),
120+
delay: queueService.refreshInterval
121+
) { task -> EventLoopFuture<Void> in
117122
//Check if shutting down
118123

119124
if self.isShuttingDown {
@@ -124,6 +129,15 @@ public final class JobsCommand: Command {
124129
return self.queueService.persistenceLayer.get(key: key).flatMap { jobStorage in
125130
//No job found, go to the next iteration
126131
guard let jobStorage = jobStorage else { return eventLoop.makeSucceededFuture(()) }
132+
133+
// If the job has a delay, we must check to make sure we can execute
134+
if let delay = jobStorage.delayUntil {
135+
guard delay >= Date() else {
136+
// The delay has not passed yet, requeue the job
137+
return self.queueService.persistenceLayer.requeue(key: key, jobStorage: jobStorage)
138+
}
139+
}
140+
127141
guard let job = self.config.make(for: jobStorage.jobName) else {
128142
return eventLoop.makeFailedFuture(Abort(.internalServerError, reason: "Please register \(jobStorage.jobName)"))
129143
}

Sources/Jobs/JobsPersistenceLayer.swift

+5
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,9 @@ public protocol JobsPersistenceLayer {
3636
/// - Parameter key: The base key
3737
/// - Returns: The processing key
3838
func processingKey(key: String) -> String
39+
40+
/// Requeues a job due to a delay
41+
/// - Parameter key: The key of the job
42+
/// - Parameter jobStorage: The jobStorage holding the `Job` to be requeued
43+
func requeue(key: String, jobStorage: JobStorage) -> EventLoopFuture<Void>
3944
}

Sources/Jobs/QueueService.swift

+9-2
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,21 @@ public struct QueueService {
2020
/// - jobData: The `JobData` to dispatch to the queue
2121
/// - maxRetryCount: The number of retries to attempt upon error before calling `Job`.`error()`
2222
/// - queue: The queue to run this job on
23+
/// - delay: A date to execute the job after
2324
/// - Returns: A future `Void` value used to signify completion
24-
public func dispatch<J: JobData>(jobData: J, maxRetryCount: Int = 0, queue: QueueName = .default) throws -> EventLoopFuture<Void> {
25+
public func dispatch<J: JobData>(
26+
jobData: J,
27+
maxRetryCount: Int = 0,
28+
queue: QueueName = .default,
29+
delayUntil: Date? = nil
30+
) throws -> EventLoopFuture<Void> {
2531
let data = try JSONEncoder().encode(jobData)
2632
let jobStorage = JobStorage(key: persistenceKey,
2733
data: data,
2834
maxRetryCount: maxRetryCount,
2935
id: UUID().uuidString,
30-
jobName: J.jobName)
36+
jobName: J.jobName,
37+
delayUntil: delayUntil)
3138

3239
return persistenceLayer.set(key: queue.makeKey(with: persistenceKey), jobStorage: jobStorage).map({})
3340
}

Tests/JobsTests/JobStorageTests.swift

+8-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,13 @@ import XCTest
1010

1111
final class JobStorageTests: XCTestCase {
1212
func testStringRepresentationIsValidJSON() {
13-
let jobStorage = JobStorage(key: "vapor", data: Data(), maxRetryCount: 1, id: "identifier", jobName: "jobs")
13+
let jobStorage = JobStorage(key: "vapor",
14+
data: Data(),
15+
maxRetryCount: 1,
16+
id: "identifier",
17+
jobName: "jobs",
18+
delayUntil: nil)
19+
1420
let stringRepresentation = jobStorage.stringValue()
1521

1622
if let data = stringRepresentation?.data(using: String.Encoding.utf8), let jobStorageRestored = try? JSONDecoder().decode(JobStorage.self, from: data) {
@@ -19,6 +25,7 @@ final class JobStorageTests: XCTestCase {
1925
XCTAssertEqual(jobStorage.maxRetryCount, jobStorageRestored.maxRetryCount)
2026
XCTAssertEqual(jobStorage.id, jobStorageRestored.id)
2127
XCTAssertEqual(jobStorage.jobName, jobStorageRestored.jobName)
28+
XCTAssertEqual(jobStorage.delayUntil, nil)
2229
} else {
2330
XCTFail("There was a problem restoring JobStorage")
2431
}

0 commit comments

Comments
 (0)