From f7842dd2aaf2fc45b371a15a290711ffd19825de Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Thu, 29 Nov 2018 12:09:18 -0500 Subject: [PATCH] Rework lazy attachment restore. --- Signal.xcodeproj/project.pbxproj | 8 +- Signal/src/AppDelegate.m | 5 - Signal/src/environment/AppEnvironment.swift | 4 + Signal/src/util/Backup/OWSBackup.h | 4 +- Signal/src/util/Backup/OWSBackupImportJob.m | 12 +- .../util/Backup/OWSBackupLazyRestore.swift | 148 ++++++++++++++++++ .../util/Backup/OWSBackupLazyRestoreJob.swift | 92 ----------- 7 files changed, 166 insertions(+), 107 deletions(-) create mode 100644 Signal/src/util/Backup/OWSBackupLazyRestore.swift delete mode 100644 Signal/src/util/Backup/OWSBackupLazyRestoreJob.swift diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index b06e2c143..48426240f 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -156,7 +156,7 @@ 3496955E219B605E00DCFE74 /* PhotoLibrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3496955B219B605E00DCFE74 /* PhotoLibrary.swift */; }; 3496956021A2FC8100DCFE74 /* CloudKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3496955F21A2FC8100DCFE74 /* CloudKit.framework */; }; 3496956E21A301A100DCFE74 /* OWSBackupExportJob.m in Sources */ = {isa = PBXBuildFile; fileRef = 3496956221A301A100DCFE74 /* OWSBackupExportJob.m */; }; - 3496956F21A301A100DCFE74 /* OWSBackupLazyRestoreJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3496956321A301A100DCFE74 /* OWSBackupLazyRestoreJob.swift */; }; + 3496956F21A301A100DCFE74 /* OWSBackupLazyRestore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3496956321A301A100DCFE74 /* OWSBackupLazyRestore.swift */; }; 3496957021A301A100DCFE74 /* OWSBackupIO.m in Sources */ = {isa = PBXBuildFile; fileRef = 3496956521A301A100DCFE74 /* OWSBackupIO.m */; }; 3496957121A301A100DCFE74 /* OWSBackupImportJob.m in Sources */ = {isa = PBXBuildFile; fileRef = 3496956621A301A100DCFE74 /* OWSBackupImportJob.m */; }; 3496957221A301A100DCFE74 /* OWSBackup.m in Sources */ = {isa = PBXBuildFile; fileRef = 3496956921A301A100DCFE74 /* OWSBackup.m */; }; @@ -812,7 +812,7 @@ 3496955B219B605E00DCFE74 /* PhotoLibrary.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotoLibrary.swift; sourceTree = ""; }; 3496955F21A2FC8100DCFE74 /* CloudKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CloudKit.framework; path = System/Library/Frameworks/CloudKit.framework; sourceTree = SDKROOT; }; 3496956221A301A100DCFE74 /* OWSBackupExportJob.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSBackupExportJob.m; sourceTree = ""; }; - 3496956321A301A100DCFE74 /* OWSBackupLazyRestoreJob.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OWSBackupLazyRestoreJob.swift; sourceTree = ""; }; + 3496956321A301A100DCFE74 /* OWSBackupLazyRestore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OWSBackupLazyRestore.swift; sourceTree = ""; }; 3496956421A301A100DCFE74 /* OWSBackup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSBackup.h; sourceTree = ""; }; 3496956521A301A100DCFE74 /* OWSBackupIO.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSBackupIO.m; sourceTree = ""; }; 3496956621A301A100DCFE74 /* OWSBackupImportJob.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSBackupImportJob.m; sourceTree = ""; }; @@ -1778,7 +1778,7 @@ 3496956521A301A100DCFE74 /* OWSBackupIO.m */, 3496956721A301A100DCFE74 /* OWSBackupJob.h */, 3496956A21A301A100DCFE74 /* OWSBackupJob.m */, - 3496956321A301A100DCFE74 /* OWSBackupLazyRestoreJob.swift */, + 3496956321A301A100DCFE74 /* OWSBackupLazyRestore.swift */, ); path = Backup; sourceTree = ""; @@ -3568,7 +3568,7 @@ 3427C64320F500E000EEC730 /* OWSMessageTimerView.m in Sources */, B90418E6183E9DD40038554A /* DateUtil.m in Sources */, 340FC8BD204DAC8D007AEB0F /* ShowGroupMembersViewController.m in Sources */, - 3496956F21A301A100DCFE74 /* OWSBackupLazyRestoreJob.swift in Sources */, + 3496956F21A301A100DCFE74 /* OWSBackupLazyRestore.swift in Sources */, 459311FC1D75C948008DD4F0 /* OWSDeviceTableViewCell.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Signal/src/AppDelegate.m b/Signal/src/AppDelegate.m index d75f217e0..9fb69a705 100644 --- a/Signal/src/AppDelegate.m +++ b/Signal/src/AppDelegate.m @@ -1212,11 +1212,6 @@ static NSTimeInterval launchStartedAt; [self.messageManager startObserving]; -#ifdef DEBUG - // Resume lazy restore. - [OWSBackupLazyRestoreJob runAsync]; -#endif - [self.udManager setup]; } diff --git a/Signal/src/environment/AppEnvironment.swift b/Signal/src/environment/AppEnvironment.swift index 44368d989..b52a96160 100644 --- a/Signal/src/environment/AppEnvironment.swift +++ b/Signal/src/environment/AppEnvironment.swift @@ -58,6 +58,9 @@ import SignalMessaging @objc public var backup: OWSBackup + @objc + public var backupLazyRestore: BackupLazyRestore + private override init() { self.callMessageHandler = WebRTCCallMessageHandler() self.callService = CallService() @@ -70,6 +73,7 @@ import SignalMessaging self.pushManager = PushManager() self.sessionResetJobQueue = SessionResetJobQueue() self.backup = OWSBackup() + self.backupLazyRestore = BackupLazyRestore() super.init() diff --git a/Signal/src/util/Backup/OWSBackup.h b/Signal/src/util/Backup/OWSBackup.h index 38211f101..c81a55d6b 100644 --- a/Signal/src/util/Backup/OWSBackup.h +++ b/Signal/src/util/Backup/OWSBackup.h @@ -95,9 +95,7 @@ NSError *OWSBackupErrorWithDescription(NSString *description); - (NSArray *)attachmentIdsForLazyRestore; -- (void)lazyRestoreAttachment:(TSAttachmentPointer *)attachment - backupIO:(OWSBackupIO *)backupIO - completion:(OWSBackupBoolBlock)completion; +- (AnyPromise *)lazyRestoreAttachment:(TSAttachmentPointer *)attachment backupIO:(OWSBackupIO *)backupIO; @end diff --git a/Signal/src/util/Backup/OWSBackupImportJob.m b/Signal/src/util/Backup/OWSBackupImportJob.m index 1f902e285..05a38375c 100644 --- a/Signal/src/util/Backup/OWSBackupImportJob.m +++ b/Signal/src/util/Backup/OWSBackupImportJob.m @@ -63,6 +63,11 @@ NSString *const kOWSBackup_ImportDatabaseKeySpec = @"kOWSBackup_ImportDatabaseKe return AppEnvironment.shared.backup; } +- (OWSBackupLazyRestore *)backupLazyRestore +{ + return AppEnvironment.shared.backupLazyRestore; +} + #pragma mark - - (NSArray *)databaseItems @@ -157,10 +162,11 @@ NSString *const kOWSBackup_ImportDatabaseKeySpec = @"kOWSBackup_ImportDatabaseKe .thenInBackground(^{ return [self restoreAttachmentFiles]; }) + .then(^{ + // Kick off lazy restore on main thread. + [self.backupLazyRestore runIfNecessary]; + }) .thenInBackground(^{ - // Kick off lazy restore. - [OWSBackupLazyRestoreJob runAsync]; - [self.profileManager fetchLocalUsersProfile]; [self.tsAccountManager updateAccountAttributes]; diff --git a/Signal/src/util/Backup/OWSBackupLazyRestore.swift b/Signal/src/util/Backup/OWSBackupLazyRestore.swift new file mode 100644 index 000000000..e58a2eb02 --- /dev/null +++ b/Signal/src/util/Backup/OWSBackupLazyRestore.swift @@ -0,0 +1,148 @@ +// +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// + +import Foundation +import PromiseKit +import SignalServiceKit + +@objc(OWSBackupLazyRestore) +public class BackupLazyRestore: NSObject { + + // MARK: - Dependencies + + private var backup: OWSBackup { + return AppEnvironment.shared.backup + } + + private var primaryStorage: OWSPrimaryStorage { + return SSKEnvironment.shared.primaryStorage + } + + private var tsAccountManager: TSAccountManager { + return TSAccountManager.sharedInstance() + } + + // MARK: - + + private var isRunning = false + private var isComplete = false + + @objc + public required override init() { + super.init() + + SwiftSingletons.register(self) + + AppReadiness.runNowOrWhenAppDidBecomeReady { + self.runIfNecessary() + } + + NotificationCenter.default.addObserver(forName: .OWSApplicationDidBecomeActive, object: nil, queue: nil) { _ in + self.runIfNecessary() + } + NotificationCenter.default.addObserver(forName: .RegistrationStateDidChange, object: nil, queue: nil) { _ in + self.runIfNecessary() + } + NotificationCenter.default.addObserver(forName: .reachabilityChanged, object: nil, queue: nil) { _ in + self.runIfNecessary() + } + } + + // MARK: - + + private var backgroundQueue = { + DispatchQueue.global(qos: .background) + }() + + @objc + public func runIfNecessary() { + AssertIsOnMainThread() + + guard CurrentAppContext().isMainAppAndActive else { + return + } + guard tsAccountManager.isRegisteredAndReady() else { + return + } + guard !isRunning, !isComplete else { + return + } + + isRunning = true + + backgroundQueue.async { + self.restoreAttachments() + } + } + + private func restoreAttachments() { + let temporaryDirectory = OWSTemporaryDirectory() + let jobTempDirPath = (temporaryDirectory as NSString).appendingPathComponent(NSUUID().uuidString) + + guard OWSFileSystem.ensureDirectoryExists(jobTempDirPath) else { + Logger.error("could not create temp directory.") + complete(errorCount: 1) + return + } + + let backupIO = OWSBackupIO(jobTempDirPath: jobTempDirPath) + + let attachmentIds = backup.attachmentIdsForLazyRestore() + guard attachmentIds.count > 0 else { + Logger.info("No attachments need lazy restore.") + complete(errorCount: 0) + return + } + Logger.info("Lazy restoring \(attachmentIds.count) attachments.") + tryToRestoreNextAttachment(attachmentIds: attachmentIds, errorCount: 0, backupIO: backupIO) + } + + private func tryToRestoreNextAttachment(attachmentIds: [String], errorCount: UInt, backupIO: OWSBackupIO) { + var attachmentIdsCopy = attachmentIds + guard let attachmentId = attachmentIdsCopy.last else { + // This job is done. + Logger.verbose("job is done.") + complete(errorCount: errorCount) + return + } + attachmentIdsCopy.removeLast() + guard let attachmentPointer = TSAttachment.fetch(uniqueId: attachmentId) as? TSAttachmentPointer else { + Logger.warn("could not load attachment.") + // Not necessarily an error. + // The attachment might have been deleted since the job began. + // Continue trying to restore the other attachments. + tryToRestoreNextAttachment(attachmentIds: attachmentIds, errorCount: errorCount + 1, backupIO: backupIO) + return + } + backup.lazyRestoreAttachment(attachmentPointer, + backupIO: backupIO) + .done { _ in + Logger.info("Restored attachment.") + + self.backgroundQueue.async { + // Continue trying to restore the other attachments. + self.tryToRestoreNextAttachment(attachmentIds: attachmentIdsCopy, errorCount: errorCount, backupIO: backupIO) + } + }.catch { (error) in + Logger.error("Could not restore attachment: \(error)") + + self.backgroundQueue.async { + // Continue trying to restore the other attachments. + self.tryToRestoreNextAttachment(attachmentIds: attachmentIdsCopy, errorCount: errorCount + 1, backupIO: backupIO) + } + } + } + + private func complete(errorCount: UInt) { + Logger.verbose("") + + DispatchQueue.main.async { + self.isRunning = false + + if errorCount == 0 { + self.isComplete = true + } + } + } +} diff --git a/Signal/src/util/Backup/OWSBackupLazyRestoreJob.swift b/Signal/src/util/Backup/OWSBackupLazyRestoreJob.swift deleted file mode 100644 index e58189cce..000000000 --- a/Signal/src/util/Backup/OWSBackupLazyRestoreJob.swift +++ /dev/null @@ -1,92 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -import Foundation -import PromiseKit -import SignalServiceKit - -@objc -public class OWSBackupLazyRestoreJob: NSObject { - - let primaryStorage: OWSPrimaryStorage - - private var jobTempDirPath: String? - - deinit { - if let jobTempDirPath = self.jobTempDirPath { - DispatchQueue.global().async { - OWSFileSystem.deleteFile(jobTempDirPath) - } - } - } - - @objc - public class func runAsync() { - OWSBackupLazyRestoreJob().runAsync() - } - - public override init() { - self.primaryStorage = OWSPrimaryStorage.shared() - } - - private func runAsync() { - AssertIsOnMainThread() - - DispatchQueue.global().async { - self.restoreAttachments() - } - } - - private func restoreAttachments() { - let temporaryDirectory = OWSTemporaryDirectory() - let jobTempDirPath = (temporaryDirectory as NSString).appendingPathComponent(NSUUID().uuidString) - - guard OWSFileSystem.ensureDirectoryExists(jobTempDirPath) else { - Logger.error("could not create temp directory.") - return - } - - self.jobTempDirPath = jobTempDirPath - - let backupIO = OWSBackupIO(jobTempDirPath: jobTempDirPath) - - let attachmentIds = OWSBackup.shared().attachmentIdsForLazyRestore() - guard attachmentIds.count > 0 else { - Logger.info("No attachments need lazy restore.") - return - } - Logger.info("Lazy restoring \(attachmentIds.count) attachments.") - self.tryToRestoreNextAttachment(attachmentIds: attachmentIds, backupIO: backupIO) - } - - private func tryToRestoreNextAttachment(attachmentIds: [String], backupIO: OWSBackupIO) { - var attachmentIdsCopy = attachmentIds - guard let attachmentId = attachmentIdsCopy.last else { - // This job is done. - Logger.verbose("job is done.") - return - } - attachmentIdsCopy.removeLast() - guard let attachmentPointer = TSAttachment.fetch(uniqueId: attachmentId) as? TSAttachmentPointer else { - Logger.warn("could not load attachment.") - // Not necessarily an error. - // The attachment might have been deleted since the job began. - // Continue trying to restore the other attachments. - tryToRestoreNextAttachment(attachmentIds: attachmentIds, backupIO: backupIO) - return - } - OWSBackup.shared().lazyRestoreAttachment(attachmentPointer, - backupIO: backupIO, - completion: { (success) in - if success { - Logger.info("restored attachment.") - } else { - Logger.warn("could not restore attachment.") - } - // Continue trying to restore the other attachments. - self.tryToRestoreNextAttachment(attachmentIds: attachmentIdsCopy, backupIO: backupIO) - }) - - } -}