Don't start app version migrations until storage is ready.

This commit is contained in:
Matthew Chen 2018-04-23 15:49:20 -04:00
parent 2c1e633914
commit 2265ae08aa
8 changed files with 56 additions and 79 deletions

View File

@ -161,6 +161,11 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
}
notificationsProtocolBlock:^{
return SignalApp.sharedApp.notificationsManager;
}
migrationCompletion:^{
OWSAssertIsOnMainThread();
[self versionMigrationsDidComplete];
}];
[UIUtil applySignalAppearence];
@ -176,14 +181,6 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
mainWindow.rootViewController = [self loadingRootViewController];
[mainWindow makeKeyAndVisible];
// performUpdateCheck must be invoked after Environment has been initialized because
// upgrade process may depend on Environment.
[VersionMigrations performUpdateCheckWithCompletion:^{
OWSAssertIsOnMainThread();
[self versionMigrationsDidComplete];
}];
// Accept push notification when app is not open
NSDictionary *remoteNotif = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];
if (remoteNotif) {

View File

@ -1,5 +1,5 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
NS_ASSUME_NONNULL_BEGIN
@ -14,7 +14,8 @@ typedef id<NotificationsProtocol> _Nonnull (^NotificationsManagerBlock)(void);
@interface AppSetup : NSObject
+ (void)setupEnvironment:(CallMessageHandlerBlock)callMessageHandlerBlock
notificationsProtocolBlock:(NotificationsManagerBlock)notificationsManagerBlock;
notificationsProtocolBlock:(NotificationsManagerBlock)notificationsManagerBlock
migrationCompletion:(dispatch_block_t)migrationCompletion;
@end

View File

@ -20,9 +20,14 @@ NS_ASSUME_NONNULL_BEGIN
+ (void)setupEnvironment:(CallMessageHandlerBlock)callMessageHandlerBlock
notificationsProtocolBlock:(NotificationsManagerBlock)notificationsManagerBlock
migrationCompletion:(dispatch_block_t)migrationCompletion
{
OWSAssert(callMessageHandlerBlock);
OWSAssert(notificationsManagerBlock);
OWSAssert(migrationCompletion);
__block OWSBackgroundTask *_Nullable backgroundTask =
[OWSBackgroundTask backgroundTaskWithLabelStr:__PRETTY_FUNCTION__];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
@ -46,7 +51,16 @@ NS_ASSUME_NONNULL_BEGIN
[NSKeyedUnarchiver setClass:[OWSUserProfile class] forClassName:[OWSUserProfile collection]];
[NSKeyedUnarchiver setClass:[OWSDatabaseMigration class] forClassName:[OWSDatabaseMigration collection]];
[OWSStorage setupStorage];
[OWSStorage setupStorageWithMigrationBlock:^() {
// Don't start database migrations until storage is ready.
[VersionMigrations performUpdateCheckWithCompletion:^() {
OWSAssertIsOnMainThread();
migrationCompletion();
backgroundTask = nil;
}];
}];
[[Environment current].contactsManager startObserving];
});
}

View File

@ -33,6 +33,8 @@ NS_ASSUME_NONNULL_BEGIN
+ (void)performUpdateCheckWithCompletion:(VersionMigrationCompletion)completion
{
DDLogInfo(@"%@ %s", self.logTag, __PRETTY_FUNCTION__);
// performUpdateCheck must be invoked after Environment has been initialized because
// upgrade process may depend on Environment.
OWSAssert([Environment current]);

View File

@ -58,16 +58,6 @@ NS_ASSUME_NONNULL_BEGIN
OWSAssert(completion);
OWSDatabaseConnection *dbConnection = (OWSDatabaseConnection *)self.primaryStorage.newDatabaseConnection;
// These migrations won't be run until storage registrations are enqueued,
// but this transaction might begin before all registrations are marked as
// complete, so disable this checking.
//
// TODO: Once we move "app readiness" into AppSetup, we should explicitly
// not start these migrations until storage is ready. We can then remove
// this statement which disables checking.
#ifdef DEBUG
dbConnection.canWriteBeforeStorageReady = YES;
#endif
[dbConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
[self runUpWithTransaction:transaction];
@ -88,18 +78,12 @@ NS_ASSUME_NONNULL_BEGIN
return self.dbReadWriteConnection;
}
// Database migrations need to occur _before_ storage is ready (by definition),
// so we need to use a connection with canWriteBeforeStorageReady set in
// debug builds.
+ (YapDatabaseConnection *)dbReadWriteConnection
{
static dispatch_once_t onceToken;
static YapDatabaseConnection *sharedDBConnection;
dispatch_once(&onceToken, ^{
sharedDBConnection = [OWSPrimaryStorage sharedManager].newDatabaseConnection;
OWSAssert([sharedDBConnection isKindOfClass:[OWSDatabaseConnection class]]);
((OWSDatabaseConnection *)sharedDBConnection).canWriteBeforeStorageReady = YES;
});
return sharedDBConnection;

View File

@ -22,10 +22,6 @@ extern NSString *const StorageIsReadyNotification;
@property (atomic, weak) id<OWSDatabaseConnectionDelegate> delegate;
#ifdef DEBUG
@property (atomic) BOOL canWriteBeforeStorageReady;
#endif
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithDatabase:(YapDatabase *)database
delegate:(id<OWSDatabaseConnectionDelegate>)delegate NS_DESIGNATED_INITIALIZER;
@ -48,6 +44,8 @@ extern NSString *const StorageIsReadyNotification;
#pragma mark -
typedef void (^OWSStorageMigrationBlock)(void);
@interface OWSStorage : NSObject
- (instancetype)init NS_UNAVAILABLE;
@ -60,7 +58,7 @@ extern NSString *const StorageIsReadyNotification;
// This object can be used to filter database notifications.
@property (nonatomic, readonly, nullable) id dbNotificationObject;
+ (void)setupStorage;
+ (void)setupStorageWithMigrationBlock:(OWSStorageMigrationBlock)migrationBlock;
+ (void)resetAllStorage;

View File

@ -74,7 +74,7 @@ typedef NSData *_Nullable (^CreateDatabaseMetadataBlock)(void);
{
id<OWSDatabaseConnectionDelegate> delegate = self.delegate;
OWSAssert(delegate);
OWSAssert(delegate.areAllRegistrationsComplete || self.canWriteBeforeStorageReady);
OWSAssert(delegate.areAllRegistrationsComplete);
OWSBackgroundTask *_Nullable backgroundTask = nil;
if (CurrentAppContext().isMainApp) {
@ -101,7 +101,7 @@ typedef NSData *_Nullable (^CreateDatabaseMetadataBlock)(void);
{
id<OWSDatabaseConnectionDelegate> delegate = self.delegate;
OWSAssert(delegate);
OWSAssert(delegate.areAllRegistrationsComplete || self.canWriteBeforeStorageReady);
OWSAssert(delegate.areAllRegistrationsComplete);
__block OWSBackgroundTask *_Nullable backgroundTask = nil;
if (CurrentAppContext().isMainApp) {
@ -176,13 +176,6 @@ typedef NSData *_Nullable (^CreateDatabaseMetadataBlock)(void);
- (YapDatabaseConnection *)registrationConnection
{
YapDatabaseConnection *connection = [super registrationConnection];
#ifdef DEBUG
// Flag the registration connection as such.
OWSAssert([connection isKindOfClass:[OWSDatabaseConnection class]]);
((OWSDatabaseConnection *)connection).canWriteBeforeStorageReady = YES;
#endif
return connection;
}
@ -332,29 +325,24 @@ typedef NSData *_Nullable (^CreateDatabaseMetadataBlock)(void);
OWS_ABSTRACT_METHOD();
}
+ (NSArray<OWSStorage *> *)allStorages
+ (void)setupStorageWithMigrationBlock:(OWSStorageMigrationBlock)migrationBlock
{
return @[
OWSPrimaryStorage.sharedManager,
];
}
OWSAssert(migrationBlock);
+ (void)setupStorage
{
__block OWSBackgroundTask *_Nullable backgroundTask =
[OWSBackgroundTask backgroundTaskWithLabelStr:__PRETTY_FUNCTION__];
for (OWSStorage *storage in self.allStorages) {
[storage runSyncRegistrations];
}
[OWSPrimaryStorage.sharedManager runSyncRegistrations];
for (OWSStorage *storage in self.allStorages) {
[storage runAsyncRegistrationsWithCompletion:^{
if ([self postRegistrationCompleteNotificationIfPossible]) {
backgroundTask = nil;
}
}];
}
[OWSPrimaryStorage.sharedManager runAsyncRegistrationsWithCompletion:^{
OWSAssert(self.isStorageReady);
[self postRegistrationCompleteNotification];
migrationBlock();
backgroundTask = nil;
}];
}
- (YapDatabaseConnection *)registrationConnection
@ -363,11 +351,11 @@ typedef NSData *_Nullable (^CreateDatabaseMetadataBlock)(void);
}
// Returns YES IFF all registrations are complete.
+ (BOOL)postRegistrationCompleteNotificationIfPossible
+ (void)postRegistrationCompleteNotification
{
if (!self.isStorageReady) {
return NO;
}
OWSAssert(self.isStorageReady);
DDLogInfo(@"%@ %s", self.logTag, __PRETTY_FUNCTION__);
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
@ -375,18 +363,11 @@ typedef NSData *_Nullable (^CreateDatabaseMetadataBlock)(void);
object:nil
userInfo:nil];
});
return YES;
}
+ (BOOL)isStorageReady
{
for (OWSStorage *storage in self.allStorages) {
if (!storage.areAllRegistrationsComplete) {
return NO;
}
}
return YES;
return OWSPrimaryStorage.sharedManager.areAllRegistrationsComplete;
}
- (BOOL)tryToLoadDatabase

View File

@ -90,18 +90,18 @@ public class ShareViewController: UIViewController, ShareViewDelegate, SAEFailed
// We shouldn't set up our environment until after we've consulted isReadyForAppExtensions.
AppSetup.setupEnvironment({
return NoopCallMessageHandler()
}) {
},
notificationsProtocolBlock: {
return NoopNotificationsManager()
}
},
migrationCompletion: { [weak self] in
SwiftAssertIsOnMainThread(#function)
// performUpdateCheck must be invoked after Environment has been initialized because
// upgrade process may depend on Environment.
VersionMigrations.performUpdateCheck(completion: { [weak self] in
SwiftAssertIsOnMainThread(#function)
guard let strongSelf = self else { return }
guard let strongSelf = self else { return }
strongSelf.versionMigrationsDidComplete()
// performUpdateCheck must be invoked after Environment has been initialized because
// upgrade process may depend on Environment.
strongSelf.versionMigrationsDidComplete()
})
// We don't need to use "screen protection" in the SAE.