Skip to content

Commit e8c949d

Browse files
authored
Allow overriding previous job for SingleInstance constraint (#38)
1 parent c96a520 commit e8c949d

File tree

7 files changed

+75
-16
lines changed

7 files changed

+75
-16
lines changed

Sources/SwiftQueue/Constraints+UniqueUUID.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,13 @@ internal class UniqueUUIDConstraint: JobConstraint {
1111

1212
func willSchedule(queue: SwiftQueue, operation: SwiftQueueJob) throws {
1313
for op in queue.operations where op.name == operation.info.uuid {
14-
throw TaskAlreadyExist()
14+
if operation.info.override {
15+
// Cancel previous job
16+
queue.cancelOperations(uuid: operation.info.uuid)
17+
} else {
18+
// Cancel new job
19+
throw TaskAlreadyExist()
20+
}
1521
}
1622
}
1723

Sources/SwiftQueue/Job.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@ public final class JobBuilder {
1616
}
1717

1818
/// Allow only 1 job at the time with this ID scheduled or running
19-
/// Same job scheduled with same id will result in onRemove(TaskAlreadyExist)
20-
public func singleInstance(forId: String) -> JobBuilder {
19+
/// Same job scheduled with same id will result in onRemove(TaskAlreadyExist) if override = false
20+
/// If override = true the previous job will be canceled and the new job will be scheduled
21+
public func singleInstance(forId: String, override: Bool = false) -> JobBuilder {
2122
info.uuid = forId
23+
info.override = override
2224
return self
2325
}
2426

Sources/SwiftQueue/JobInfo.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ struct JobInfo {
99
let type: String
1010

1111
var uuid: String = UUID().uuidString
12+
var override = false
13+
1214
var group: String = "GLOBAL"
1315
var tags = Set<String>()
1416
var delay: TimeInterval?
@@ -31,6 +33,7 @@ struct JobInfo {
3133
init?(dictionary: [String: Any]) {
3234
guard let type = dictionary["type"] as? String,
3335
let uuid = dictionary["uuid"] as? String,
36+
let override = dictionary["override"] as? Bool,
3437
let group = dictionary["group"] as? String,
3538
let tags = dictionary["tags"] as? [String],
3639
let delay = dictionary["delay"] as? TimeInterval?,
@@ -52,6 +55,7 @@ struct JobInfo {
5255

5356
self.type = type
5457
self.uuid = uuid
58+
self.override = override
5559
self.group = group
5660
self.tags = Set(tags)
5761
self.delay = delay
@@ -70,6 +74,7 @@ struct JobInfo {
7074
var dict = [String: Any]()
7175
dict["type"] = self.type
7276
dict["uuid"] = self.uuid
77+
dict["override"] = self.override
7378
dict["group"] = self.group
7479
dict["tags"] = Array(self.tags)
7580
dict["delay"] = self.delay

Sources/SwiftQueue/SwiftQueue.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,16 @@ internal final class SwiftQueue: OperationQueue {
9494
}
9595
}
9696

97+
func cancelOperations(uuid: String) {
98+
operations.flatMap { operation -> SwiftQueueJob? in
99+
operation as? SwiftQueueJob
100+
}.filter {
101+
$0.info.uuid == uuid
102+
}.forEach {
103+
$0.cancel()
104+
}
105+
}
106+
97107
private func completed(_ job: SwiftQueueJob) {
98108
// Remove this operation from serialization
99109
if job.info.isPersisted, let sp = persister {

Tests/SwiftQueueTests/ConstraintUniqueUUIDTests.swift

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,30 +17,61 @@ class ConstraintUniqueUUIDTests: XCTestCase {
1717
let job2 = TestJob()
1818
let type2 = UUID().uuidString
1919

20-
let job3 = TestJob()
21-
let type3 = UUID().uuidString
22-
23-
let creator = TestCreator([type1: job1, type2: job2, type3: job3])
20+
let creator = TestCreator([type1: job1, type2: job2])
2421

2522
let manager = SwiftQueueManager(creators: [creator])
2623
JobBuilder(type: type1)
2724
.singleInstance(forId: id)
2825
.delay(time: 3600)
2926
.schedule(manager: manager)
3027

31-
JobBuilder(type: type2)
28+
JobBuilder(type: type2).singleInstance(forId: id).schedule(manager: manager)
29+
30+
job2.await()
31+
32+
XCTAssertEqual(job2.onRunJobCalled, 0)
33+
XCTAssertEqual(job2.onCompleteCalled, 0)
34+
XCTAssertEqual(job2.onRetryCalled, 0)
35+
XCTAssertEqual(job2.onCancelCalled, 1)
36+
37+
XCTAssertTrue(job2.lastError is TaskAlreadyExist)
38+
39+
manager.cancelAllOperations()
40+
manager.waitUntilAllOperationsAreFinished()
41+
}
42+
43+
func testUniqueIdConstraintShouldCancelTheFirst() {
44+
let id = UUID().uuidString
45+
46+
let job1 = TestJob()
47+
let type1 = UUID().uuidString
48+
49+
let job2 = TestJob()
50+
let type2 = UUID().uuidString
51+
52+
let creator = TestCreator([type1: job1, type2: job2])
53+
54+
let manager = SwiftQueueManager(creators: [creator])
55+
JobBuilder(type: type1)
3256
.singleInstance(forId: id)
3357
.delay(time: 3600)
3458
.schedule(manager: manager)
3559

36-
JobBuilder(type: type3).singleInstance(forId: id).schedule(manager: manager)
60+
JobBuilder(type: type2)
61+
.singleInstance(forId: id, override: true)
62+
.schedule(manager: manager)
63+
64+
job2.await()
3765

38-
job3.await()
66+
XCTAssertEqual(job1.onRunJobCalled, 0)
67+
XCTAssertEqual(job1.onCompleteCalled, 0)
68+
XCTAssertEqual(job1.onRetryCalled, 0)
69+
XCTAssertEqual(job1.onCancelCalled, 1)
3970

40-
XCTAssertEqual(job3.onRunJobCalled, 0)
41-
XCTAssertEqual(job3.onCompleteCalled, 0)
42-
XCTAssertEqual(job3.onRetryCalled, 0)
43-
XCTAssertEqual(job3.onCancelCalled, 1)
71+
XCTAssertEqual(job2.onRunJobCalled, 1)
72+
XCTAssertEqual(job2.onCompleteCalled, 1)
73+
XCTAssertEqual(job2.onRetryCalled, 0)
74+
XCTAssertEqual(job2.onCancelCalled, 0)
4475

4576
manager.cancelAllOperations()
4677
manager.waitUntilAllOperationsAreFinished()

Tests/SwiftQueueTests/SwiftQueueBuilderTests.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class SwiftQueueBuilderTests: XCTestCase {
3232

3333
let manager = SwiftQueueManager(creators: [creator], persister: persister)
3434
JobBuilder(type: type)
35-
.singleInstance(forId: taskID)
35+
.singleInstance(forId: taskID, override: true)
3636
.group(name: group)
3737
.addTag(tag: tag)
3838
.delay(time: delay)
@@ -53,6 +53,7 @@ class SwiftQueueBuilderTests: XCTestCase {
5353

5454
XCTAssertEqual(actual?.name, taskID)
5555
XCTAssertEqual(jobInfo?.uuid, taskID)
56+
XCTAssertEqual(jobInfo?.override, true)
5657
XCTAssertEqual(jobInfo?.type, type)
5758
XCTAssertEqual(jobInfo?.group, group)
5859
XCTAssertEqual(jobInfo?.tags.first, tag)

Tests/SwiftQueueTests/TestUtils.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ class TestJob: Job {
1212

1313
public var result: Error?
1414

15+
public var lastError: Error?
16+
1517
public var onRunJobCalled = 0
1618
public var onRetryCalled = 0
1719
public var onCompleteCalled = 0
@@ -39,6 +41,7 @@ class TestJob: Job {
3941
}
4042

4143
func onRetry(error: Error) -> RetryConstraint {
44+
lastError = error
4245
onRetryCalled += 1
4346
return retryConstraint
4447
}
@@ -49,7 +52,8 @@ class TestJob: Job {
4952
onCompleteCalled += 1
5053
semaphore.signal()
5154

52-
case .fail:
55+
case .fail(let error):
56+
lastError = error
5357
onCancelCalled += 1
5458
semaphore.signal()
5559
}

0 commit comments

Comments
 (0)