Cancel quickly if dependent operation fails

This commit is contained in:
Michael Kirk 2018-07-19 13:38:45 -06:00
parent 90214ae578
commit eb4c62593b
3 changed files with 56 additions and 5 deletions

View File

@ -5,7 +5,7 @@
import Foundation
@objc(OWSContactDiscoveryOperation)
class ContactDiscoveryOperation: OWSOperation {
class ContactDiscoveryOperation: OWSOperation, LegacyContactDiscoveryBatchOperationDelegate {
let batchSize = 2048
let recipientIdsToLookup: [String]
@ -23,6 +23,7 @@ class ContactDiscoveryOperation: OWSOperation {
Logger.debug("\(logTag) in \(#function) with recipientIdsToLookup: \(recipientIdsToLookup.count)")
for batchIds in recipientIdsToLookup.chunked(by: batchSize) {
let batchOperation = LegacyContactDiscoveryBatchOperation(recipientIdsToLookup: batchIds)
batchOperation.delegate = self
self.addDependency(batchOperation)
}
}
@ -44,11 +45,24 @@ class ContactDiscoveryOperation: OWSOperation {
self.reportSuccess()
}
// MARK: LegacyContactDiscoveryBatchOperationDelegate
func contactDiscoverBatchOperation(_ contactDiscoverBatchOperation: LegacyContactDiscoveryBatchOperation, didFailWithError error: Error) {
Logger.debug("\(logTag) in \(#function) canceling self and all dependencies.")
self.dependencies.forEach { $0.cancel() }
self.cancel()
}
}
protocol LegacyContactDiscoveryBatchOperationDelegate: class {
func contactDiscoverBatchOperation(_ contactDiscoverBatchOperation: LegacyContactDiscoveryBatchOperation, didFailWithError error: Error)
}
class LegacyContactDiscoveryBatchOperation: OWSOperation {
var registeredRecipientIds: Set<String>
weak var delegate: LegacyContactDiscoveryBatchOperationDelegate?
private let recipientIdsToLookup: [String]
private var networkManager: TSNetworkManager {
@ -72,10 +86,17 @@ class LegacyContactDiscoveryBatchOperation: OWSOperation {
override func run() {
Logger.debug("\(logTag) in \(#function)")
guard !isCancelled else {
Logger.info("\(logTag) in \(#function) no work to do, since we were canceled")
self.reportCancelled()
return
}
var phoneNumbersByHashes: [String: String] = [:]
for recipientId in recipientIdsToLookup {
let hash = Cryptography.truncatedSHA1Base64EncodedWithoutPadding(recipientId)
assert(phoneNumbersByHashes[hash] == nil)
phoneNumbersByHashes[hash] = recipientId
}
@ -121,6 +142,11 @@ class LegacyContactDiscoveryBatchOperation: OWSOperation {
CDSFeedbackOperation.operationQueue.addOperations([newCDSBatchOperation, cdsFeedbackOperation], waitUntilFinished: false)
}
// Called at most one time.
override func didFail(error: Error) {
self.delegate?.contactDiscoverBatchOperation(self, didFailWithError: error)
}
// MARK: Private Helpers
private func parse(response: Any?, phoneNumbersByHashes: [String: String]) throws -> Set<String> {

View File

@ -42,17 +42,27 @@ typedef NS_ENUM(NSInteger, OWSOperationState) {
// Called at most one time.
- (void)didSucceed;
// Called at most one time.
- (void)didCancel;
// Called at most one time, once retry is no longer possible.
- (void)didFailWithError:(NSError *)error NS_SWIFT_NAME(didFail(error:));
#pragma mark - Success/Error - Do Not Override
// Complete the operation successfully.
// Should be called at most once per operation instance.
// You must ensure that `run` cannot fail after calling `reportSuccess`.
// Report that the operation completed successfully.
//
// Each invocation of `run` must make exactly one call to one of: `reportSuccess`, `reportCancelled`, or `reportError:`
- (void)reportSuccess;
// Should be called at most once per `run`.
// Call this when you abort before completion due to being cancelled.
//
// Each invocation of `run` must make exactly one call to one of: `reportSuccess`, `reportCancelled`, or `reportError:`
- (void)reportCancelled;
// Report that the operation failed to complete due to an error.
//
// Each invocation of `run` must make exactly one call to one of: `reportSuccess`, `reportCancelled`, or `reportError:`
// You must ensure that `run` cannot succeed after calling `reportError`, e.g. generally you'll write something like
// this:
//

View File

@ -81,6 +81,13 @@ NSString *const OWSOperationKeyIsFinished = @"isFinished";
// Override in subclass if necessary
}
// Called at most one time.
- (void)didCancel
{
// no-op
// Override in subclass if necessary
}
// Called at most one time, once retry is no longer possible.
- (void)didFailWithError:(NSError *)error
{
@ -113,6 +120,14 @@ NSString *const OWSOperationKeyIsFinished = @"isFinished";
[self markAsComplete];
}
// These methods are not intended to be subclassed
- (void)reportCancelled
{
DDLogDebug(@"%@ cancelled.", self.logTag);
[self didCancel];
[self markAsComplete];
}
- (void)reportError:(NSError *)error
{
DDLogDebug(@"%@ reportError: %@, fatal?: %d, retryable?: %d, remainingRetries: %lu",