Async migrations framework

* nonblocking by default
* track success of each migration independently

// FREEBIE
This commit is contained in:
Michael Kirk 2016-09-28 12:21:14 -04:00
parent 3b687da0ec
commit 3e7e67e276
12 changed files with 283 additions and 19 deletions

View File

@ -3,7 +3,7 @@ source 'https://github.com/CocoaPods/Specs.git'
target 'Signal' do
pod 'SocketRocket', :git => 'https://github.com/facebook/SocketRocket.git'
pod 'SignalServiceKit', git: 'https://github.com/WhisperSystems/SignalServiceKit.git', branch: 'mkirk/async-db-setup'
pod 'SignalServiceKit', git: 'https://github.com/WhisperSystems/SignalServiceKit.git'
#pod 'SignalServiceKit', path: '../SignalServiceKit'
pod 'OpenSSL', '~> 1.0.208'
pod 'PastelogKit', '~> 1.3'

View File

@ -119,19 +119,18 @@ DEPENDENCIES:
- OpenSSL (~> 1.0.208)
- PastelogKit (~> 1.3)
- SCWaveformView (~> 1.0)
- SignalServiceKit (from `https://github.com/WhisperSystems/SignalServiceKit.git`, branch `mkirk/async-db-setup`)
- SignalServiceKit (from `https://github.com/WhisperSystems/SignalServiceKit.git`)
- SocketRocket (from `https://github.com/facebook/SocketRocket.git`)
EXTERNAL SOURCES:
SignalServiceKit:
:branch: mkirk/async-db-setup
:git: https://github.com/WhisperSystems/SignalServiceKit.git
SocketRocket:
:git: https://github.com/facebook/SocketRocket.git
CHECKOUT OPTIONS:
SignalServiceKit:
:commit: d06455fe2b3af5c072be3437a55d78eb6a5d540f
:commit: cf035a597d73d68bd15993dbac4ada576573d96e
:git: https://github.com/WhisperSystems/SignalServiceKit.git
SocketRocket:
:commit: 8096fef47d582bff8ae3758c9ae7af1d55ea53d6
@ -162,6 +161,6 @@ SPEC CHECKSUMS:
UnionFind: c33be5adb12983981d6e827ea94fc7f9e370f52d
YapDatabase: b1e43555a34a5298e23a045be96817a5ef0da58f
PODFILE CHECKSUM: e5ab6db291fd21f96ab3767fbf468988b9c916c7
PODFILE CHECKSUM: 5dccee4c1c1ba5d4bf9575a81eeede82d1e89e8b
COCOAPODS: 1.0.1

View File

@ -17,6 +17,9 @@
453D28BB1D332DB100D523F0 /* OWSMessagesBubblesSizeCalculator.m in Sources */ = {isa = PBXBuildFile; fileRef = 453D28B91D332DB100D523F0 /* OWSMessagesBubblesSizeCalculator.m */; };
45666F561D9B2827008FE134 /* OWSScrubbingLogFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 45666F551D9B2827008FE134 /* OWSScrubbingLogFormatter.m */; };
45666F581D9B2880008FE134 /* OWSScrubbingLogFormatterTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 45666F571D9B2880008FE134 /* OWSScrubbingLogFormatterTest.m */; };
45666F761D9BFE00008FE134 /* OWS100RemoveTSRecipientsMigration.m in Sources */ = {isa = PBXBuildFile; fileRef = 45666F751D9BFE00008FE134 /* OWS100RemoveTSRecipientsMigration.m */; };
45666F7B1D9C0533008FE134 /* OWSDatabaseMigration.m in Sources */ = {isa = PBXBuildFile; fileRef = 45666F7A1D9C0533008FE134 /* OWSDatabaseMigration.m */; };
45666F7E1D9C0814008FE134 /* OWSDatabaseMigrationRunner.m in Sources */ = {isa = PBXBuildFile; fileRef = 45666F7D1D9C0814008FE134 /* OWSDatabaseMigrationRunner.m */; };
45843D1F1D2236B30013E85A /* OWSContactsSearcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 45843D1E1D2236B30013E85A /* OWSContactsSearcher.m */; };
45843D201D2236B30013E85A /* OWSContactsSearcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 45843D1E1D2236B30013E85A /* OWSContactsSearcher.m */; };
45843D221D223BA10013E85A /* OWSContactsSearcherTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 45843D211D223BA10013E85A /* OWSContactsSearcherTest.m */; };
@ -529,6 +532,12 @@
45666F541D9B2827008FE134 /* OWSScrubbingLogFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSScrubbingLogFormatter.h; sourceTree = "<group>"; };
45666F551D9B2827008FE134 /* OWSScrubbingLogFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSScrubbingLogFormatter.m; sourceTree = "<group>"; };
45666F571D9B2880008FE134 /* OWSScrubbingLogFormatterTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSScrubbingLogFormatterTest.m; sourceTree = "<group>"; };
45666F741D9BFE00008FE134 /* OWS100RemoveTSRecipientsMigration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWS100RemoveTSRecipientsMigration.h; path = Migrations/OWS100RemoveTSRecipientsMigration.h; sourceTree = "<group>"; };
45666F751D9BFE00008FE134 /* OWS100RemoveTSRecipientsMigration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWS100RemoveTSRecipientsMigration.m; path = Migrations/OWS100RemoveTSRecipientsMigration.m; sourceTree = "<group>"; };
45666F791D9C0533008FE134 /* OWSDatabaseMigration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSDatabaseMigration.h; path = Migrations/OWSDatabaseMigration.h; sourceTree = "<group>"; };
45666F7A1D9C0533008FE134 /* OWSDatabaseMigration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSDatabaseMigration.m; path = Migrations/OWSDatabaseMigration.m; sourceTree = "<group>"; };
45666F7C1D9C0814008FE134 /* OWSDatabaseMigrationRunner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSDatabaseMigrationRunner.h; path = Migrations/OWSDatabaseMigrationRunner.h; sourceTree = "<group>"; };
45666F7D1D9C0814008FE134 /* OWSDatabaseMigrationRunner.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSDatabaseMigrationRunner.m; path = Migrations/OWSDatabaseMigrationRunner.m; sourceTree = "<group>"; };
45843D1D1D2236B30013E85A /* OWSContactsSearcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSContactsSearcher.h; sourceTree = "<group>"; };
45843D1E1D2236B30013E85A /* OWSContactsSearcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSContactsSearcher.m; sourceTree = "<group>"; };
45843D211D223BA10013E85A /* OWSContactsSearcherTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSContactsSearcherTest.m; sourceTree = "<group>"; };
@ -1140,6 +1149,19 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
45666F731D9BFDB9008FE134 /* Migrations */ = {
isa = PBXGroup;
children = (
45666F741D9BFE00008FE134 /* OWS100RemoveTSRecipientsMigration.h */,
45666F751D9BFE00008FE134 /* OWS100RemoveTSRecipientsMigration.m */,
45666F791D9C0533008FE134 /* OWSDatabaseMigration.h */,
45666F7A1D9C0533008FE134 /* OWSDatabaseMigration.m */,
45666F7C1D9C0814008FE134 /* OWSDatabaseMigrationRunner.h */,
45666F7D1D9C0814008FE134 /* OWSDatabaseMigrationRunner.m */,
);
name = Migrations;
sourceTree = "<group>";
};
457F3AC01D14A0F700C51351 /* Models */ = {
isa = PBXGroup;
children = (
@ -1285,6 +1307,7 @@
76EB041118170B33006006FC /* environment */ = {
isa = PBXGroup;
children = (
45666F731D9BFDB9008FE134 /* Migrations */,
B6258B311C29E2E60014138E /* NotificationsManager.h */,
B6258B321C29E2E60014138E /* NotificationsManager.m */,
76EB041218170B33006006FC /* Environment.h */,
@ -2808,6 +2831,7 @@
76EB05F418170B33006006FC /* CallConnectResult.m in Sources */,
FCFD256F1A151BCB00F4C644 /* NewGroupViewController.m in Sources */,
76EB059E18170B33006006FC /* HttpSocket.m in Sources */,
45666F761D9BFE00008FE134 /* OWS100RemoveTSRecipientsMigration.m in Sources */,
E197B60E18BBEC1A00F073E5 /* CallAudioManager.m in Sources */,
FCC81A981A44558300DFEC7D /* UIDevice+TSHardwareVersion.m in Sources */,
76EB054018170B33006006FC /* AppDelegate.m in Sources */,
@ -2828,6 +2852,7 @@
76EB05F818170B33006006FC /* CallConnectUtil_Initiator.m in Sources */,
B62F5E101C2980B4000D370C /* NSData+ows_StripToken.m in Sources */,
B6B2269A1BE4C59200860F4D /* APNavigationController.m in Sources */,
45666F7E1D9C0814008FE134 /* OWSDatabaseMigrationRunner.m in Sources */,
B63761E319E1F487005735D1 /* AFHTTPSessionManager+SignalMethods.m in Sources */,
76EB05CC18170B33006006FC /* ShortAuthenticationStringGenerator.m in Sources */,
E16E5BEF18AAC40200B7C403 /* EC25KeyAgreementProtocol.m in Sources */,
@ -2836,6 +2861,7 @@
76EB058818170B33006006FC /* PropertyListPreferences.m in Sources */,
76EB05B218170B33006006FC /* DH3KKeyAgreementProtocol.m in Sources */,
B63761EC19E1FBE8005735D1 /* HttpRequest.m in Sources */,
45666F7B1D9C0533008FE134 /* OWSDatabaseMigration.m in Sources */,
76EB060818170B33006006FC /* ResponderSessionDescriptor.m in Sources */,
B90418E6183E9DD40038554A /* DateUtil.m in Sources */,
76EB05C618170B33006006FC /* HelloAckPacket.m in Sources */,

View File

@ -38,7 +38,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>2.5.3.0</string>
<string>2.5.3.3</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LOGS_EMAIL</key>

View File

@ -99,7 +99,8 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
[self prepareScreenProtection];
if ([TSAccountManager isRegistered]) {
// Avoid blocking app launch by putting all possible DB access in async thread.
[TSAccountManager runIfRegistered:^{
if (application.applicationState == UIApplicationStateInactive) {
[TSSocketManager becomeActiveFromForeground];
} else if (application.applicationState == UIApplicationStateBackground) {
@ -110,10 +111,9 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
[[PushManager sharedManager] validateUserNotificationSettings];
[TSPreKeyManager refreshPreKeys];
}
}];
[AppStoreRating setupRatingLibrary];
return YES;
}
@ -185,12 +185,12 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
return;
}
if ([TSAccountManager isRegistered]) {
[TSAccountManager runIfRegistered:^{
// We're double checking that the app is active, to be sure since we can't verify in production env due to code
// signing.
[TSSocketManager becomeActiveFromForeground];
[[Environment getCurrent].contactsManager verifyABPermission];
}
}];
[self removeScreenProtection];
}

View File

@ -0,0 +1,12 @@
// Created by Michael Kirk on 9/28/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#import "OWSDatabaseMigration.h"
NS_ASSUME_NONNULL_BEGIN
@interface OWS100RemoveTSRecipientsMigration : OWSDatabaseMigration
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,28 @@
// Created by Michael Kirk on 9/28/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#import "OWS100RemoveTSRecipientsMigration.h"
#import <YapDatabase/YapDatabaseTransaction.h>
NS_ASSUME_NONNULL_BEGIN
// Increment a similar constant for every future DBMigration
static NSString *const OWS100RemoveTSRecipientsMigrationId = @"100";
@implementation OWS100RemoveTSRecipientsMigration
+ (NSString *)migrationId
{
return OWS100RemoveTSRecipientsMigrationId;
}
- (void)runUpWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
{
NSUInteger legacyRecipientCount = [transaction numberOfKeysInCollection:@"TSRecipient"];
DDLogWarn(@"Removing %lu objects from TSRecipient collection", (unsigned long)legacyRecipientCount);
[transaction removeAllObjectsInCollection:@"TSRecipient"];
}
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,32 @@
// Created by Michael Kirk on 9/28/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#include <SignalServiceKit/TSYapDatabaseObject.h>
NS_ASSUME_NONNULL_BEGIN
@class TSStorageManager;
@interface OWSDatabaseMigration : TSYapDatabaseObject
- (instancetype)initWithStorageManager:(TSStorageManager *)storageManager;
@property (nonatomic, readonly) TSStorageManager *storageManager;
/**
* Run an asynchronous migration. Prefer this to the blocking variant whenever possible as the migration runner will
* block launching, and potentially crash apps e.g. if a view is being populated.
*/
- (void)runUp;
/**
* Run a synchronous migration.
* TODO: there's currently no tooling in the migration runner to run BlockingMigrations, as we don't have any yet.
* Try to avoid this whenever possible as the migration runner will block launching, and potentially crash apps
* e.g. if a view is being populated.
*/
- (void)runUpWithBlockingMigration;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,81 @@
// Created by Michael Kirk on 9/28/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#import "OWSDatabaseMigration.h"
#import <SignalServiceKit/TSStorageManager.h>
NS_ASSUME_NONNULL_BEGIN
@implementation OWSDatabaseMigration
- (instancetype)initWithStorageManager:(TSStorageManager *)storageManager
{
self = [super initWithUniqueId:[self.class migrationId]];
if (!self) {
return self;
}
_storageManager = storageManager;
return self;
}
+ (MTLPropertyStorage)storageBehaviorForPropertyWithKey:(NSString *)propertyKey
{
if ([propertyKey isEqualToString:@"storageManager"]) {
return MTLPropertyStorageNone;
} else {
return [super storageBehaviorForPropertyWithKey:propertyKey];
}
}
+ (NSString *)migrationId
{
@throw [NSException
exceptionWithName:NSInternalInconsistencyException
reason:[NSString stringWithFormat:@"Must override %@ in subclass", NSStringFromSelector(_cmd)]
userInfo:nil];
}
+ (NSString *)collection
{
// We want all subclasses in the same collection
return @"OWSDatabaseMigration";
}
- (void)runUpWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
{
@throw [NSException
exceptionWithName:NSInternalInconsistencyException
reason:[NSString stringWithFormat:@"Must override %@ in subclass", NSStringFromSelector(_cmd)]
userInfo:nil];
}
- (void)runUp
{
[self.storageManager.newDatabaseConnection
asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
[self runUpWithTransaction:transaction];
}
completionBlock:^{
DDLogInfo(@"Completed migration %@", self.uniqueId);
[self save];
}];
}
/**
* Try to avoid using this.
*/
- (void)runUpWithBlockingMigration
{
[self.storageManager.newDatabaseConnection
readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
[self runUpWithTransaction:transaction];
}];
DDLogInfo(@"Completed migration %@", self.uniqueId);
[self save];
}
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,26 @@
// Created by Michael Kirk on 9/28/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
NS_ASSUME_NONNULL_BEGIN
@class TSStorageManager;
@interface OWSDatabaseMigrationRunner : NSObject
- (instancetype)initWithStorageManager:(TSStorageManager *)storageManager;
@property (nonatomic, readonly) TSStorageManager *storageManager;
/**
* Run any outstanding version migrations.
*/
- (void)runAllOutstanding;
/**
* On new installations, no need to migrate anything.
*/
- (void)assumeAllExistingMigrationsRun;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,62 @@
// Created by Michael Kirk on 9/28/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#import "OWSDatabaseMigrationRunner.h"
#import "OWS100RemoveTSRecipientsMigration.h"
NS_ASSUME_NONNULL_BEGIN
@implementation OWSDatabaseMigrationRunner
- (instancetype)initWithStorageManager:(TSStorageManager *)storageManager
{
self = [super init];
if (!self) {
return self;
}
_storageManager = storageManager;
return self;
}
- (NSArray<OWSDatabaseMigration *> *)allMigrations
{
return @[ [[OWS100RemoveTSRecipientsMigration alloc] initWithStorageManager:self.storageManager] ];
}
- (void)assumeAllExistingMigrationsRun
{
for (OWSDatabaseMigration *migration in self.allMigrations) {
DDLogInfo(@"%@ Skipping migration on new install: %@", self.tag, migration);
[migration save];
}
}
- (void)runAllOutstanding
{
for (OWSDatabaseMigration *migration in self.allMigrations) {
if ([OWSDatabaseMigration fetchObjectWithUniqueID:migration.uniqueId]) {
DDLogDebug(@"%@ Skipping previously run migration: %@", self.tag, migration);
} else {
DDLogWarn(@"%@ Running migration: %@", self.tag, migration);
[migration runUp];
}
}
}
#pragma mark - Logging
+ (NSString *)tag
{
return [NSString stringWithFormat:@"[%@]", self.class];
}
- (NSString *)tag
{
return self.class.tag;
}
@end
NS_ASSUME_NONNULL_END

View File

@ -10,6 +10,7 @@
#import "Environment.h"
#import "LockInteractionController.h"
#import "OWSDatabaseMigrationRunner.h"
#import "PreferencesUtil.h"
#import "PushManager.h"
#import "RecentCallManager.h"
@ -25,6 +26,7 @@
+ (void)storeString:(NSString *)string forKey:(NSString *)key;
+ (void)storeData:(NSData *)data forKey:(NSString *)key;
@end
@implementation VersionMigrations
@ -36,6 +38,9 @@
NSString *previousVersion = Environment.preferences.lastRanVersion;
if (!previousVersion) {
DDLogInfo(@"No previous version found. Probably first launch since install - nothing to migrate.");
OWSDatabaseMigrationRunner *runner =
[[OWSDatabaseMigrationRunner alloc] initWithStorageManager:[TSStorageManager sharedManager]];
[runner assumeAllExistingMigrationsRun];
[Environment.preferences setAndGetCurrentVersion];
return;
}
@ -75,14 +80,7 @@
});
}
if ([self isVersion:previousVersion atLeast:@"2.0.0" andLessThan:@"2.5.2"] && [TSAccountManager isRegistered]) {
[[TSStorageManager sharedManager].dbConnection
readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
NSUInteger legacyRecipientCount = [transaction numberOfKeysInCollection:@"TSRecipient"];
DDLogWarn(@"Removing %lu objects from TSRecipient collection", (unsigned long)legacyRecipientCount);
[transaction removeAllObjectsInCollection:@"TSRecipient"];
}];
}
[[[OWSDatabaseMigrationRunner alloc] initWithStorageManager:[TSStorageManager sharedManager]] runAllOutstanding];
[Environment.preferences setAndGetCurrentVersion];
}