Register all database views asynchronously.

This commit is contained in:
Matthew Chen 2018-01-26 15:53:26 -05:00
parent 96b7dc5163
commit 5cf89a0f3d
30 changed files with 473 additions and 179 deletions

View File

@ -7,8 +7,8 @@ def shared_pods
# OWS Pods
# pod 'SQLCipher', path: '../sqlcipher2'
pod 'SQLCipher', :git => 'https://github.com/sqlcipher/sqlcipher.git', :commit => 'd5c2bec'
# pod 'YapDatabase/SQLCipher', path: '../YapDatabase'
pod 'YapDatabase/SQLCipher', :git => 'https://github.com/WhisperSystems/YapDatabase.git', branch: 'release/unencryptedHeaders'
pod 'YapDatabase/SQLCipher', path: '../YapDatabase'
# pod 'YapDatabase/SQLCipher', :git => 'https://github.com/WhisperSystems/YapDatabase.git', branch: 'release/unencryptedHeaders'
pod 'SignalServiceKit', path: '.'
pod 'AxolotlKit', git: 'https://github.com/WhisperSystems/SignalProtocolKit.git', branch: 'mkirk/framework-friendly'
#pod 'AxolotlKit', path: '../SignalProtocolKit'

View File

@ -141,7 +141,7 @@ DEPENDENCIES:
- SocketRocket (from `https://github.com/facebook/SocketRocket.git`)
- SQLCipher (from `https://github.com/sqlcipher/sqlcipher.git`, commit `d5c2bec`)
- SSZipArchive
- YapDatabase/SQLCipher (from `https://github.com/WhisperSystems/YapDatabase.git`, branch `release/unencryptedHeaders`)
- YapDatabase/SQLCipher (from `../YapDatabase`)
- YYImage
EXTERNAL SOURCES:
@ -167,8 +167,7 @@ EXTERNAL SOURCES:
:commit: d5c2bec
:git: https://github.com/sqlcipher/sqlcipher.git
YapDatabase:
:branch: release/unencryptedHeaders
:git: https://github.com/WhisperSystems/YapDatabase.git
:path: ../YapDatabase
CHECKOUT OPTIONS:
AxolotlKit:
@ -192,9 +191,6 @@ CHECKOUT OPTIONS:
SQLCipher:
:commit: d5c2bec
:git: https://github.com/sqlcipher/sqlcipher.git
YapDatabase:
:commit: eaff655ebc774105e83f835ead71f8b7a02e4ac1
:git: https://github.com/WhisperSystems/YapDatabase.git
SPEC CHECKSUMS:
AFNetworking: 5e0e199f73d8626b11e79750991f5d173d1f8b67
@ -221,6 +217,6 @@ SPEC CHECKSUMS:
YapDatabase: 299a32de9d350d37a9ac5b0532609d87d5d2a5de
YYImage: 1e1b62a9997399593e4b9c4ecfbbabbf1d3f3b54
PODFILE CHECKSUM: 0d804514eb2db34b9874b653e543255d8c2f5751
PODFILE CHECKSUM: 6da9d13ffba2c3d0ae0a0d376b8881102f41395f
COCOAPODS: 1.3.1

View File

@ -29,6 +29,7 @@
#import <SignalMessaging/Release.h>
#import <SignalMessaging/SignalMessaging.h>
#import <SignalMessaging/VersionMigrations.h>
#import <SignalServiceKit/AppReadiness.h>
#import <SignalServiceKit/NSUserDefaults+OWS.h>
#import <SignalServiceKit/OWSBatchMessageProcessor.h>
#import <SignalServiceKit/OWSDisappearingMessagesJob.h>
@ -134,7 +135,6 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
[self startupLogging];
// If a backup restore is in progress, try to complete it.
// Otherwise, cleanup backup state.
[OWSBackup applicationDidFinishLaunching];
@ -197,7 +197,6 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
return YES;
}
/**
* The user must unlock the device once after reboot before the database encryption key can be accessed.
*/
@ -394,7 +393,6 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
- (void)applicationDidBecomeActive:(UIApplication *)application {
DDLogWarn(@"%@ applicationDidBecomeActive.", self.logTag);
if (CurrentAppContext().isRunningTests) {
return;
}
@ -403,6 +401,19 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
[self ensureRootViewController];
[AppReadiness.sharedManager runNowOrWhenAppIsReady:^{
[self handleActivation];
}];
DDLogInfo(@"%@ applicationDidBecomeActive completed.", self.logTag);
}
- (void)handleActivation
{
OWSAssertIsOnMainThread();
DDLogWarn(@"%@ handleActivation.", self.logTag);
// Always check prekeys after app launches, and sometimes check on app activation.
[TSPreKeyManager checkPreKeysIfNecessary];
@ -472,10 +483,9 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
preferences:[Environment preferences]];
}
});
}
DDLogInfo(@"%@ applicationDidBecomeActive completed.", self.logTag);
DDLogInfo(@"%@ handleActivation completed.", self.logTag);
}
- (void)applicationWillResignActive:(UIApplication *)application {
@ -753,10 +763,13 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
- (void)storageIsReady
{
OWSAssertIsOnMainThread();
DDLogInfo(@"%@ storageIsReady", self.logTag);
[OWSPreferences setIsRegistered:[TSAccountManager isRegistered]];
[AppReadiness.sharedManager setAppIsReady];
if ([TSAccountManager isRegistered]) {
DDLogInfo(@"localNumber: %@", [TSAccountManager localNumber]);
@ -833,6 +846,8 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
- (void)ensureRootViewController
{
OWSAssertIsOnMainThread();
DDLogInfo(@"%@ ensureRootViewController", self.logTag);
if (![OWSStorage isStorageReady] || self.hasInitialRootViewController) {

View File

@ -338,6 +338,65 @@ NS_ASSUME_NONNULL_BEGIN
NSString *_Nullable databaseFilePath = [self createUnconvertedDatabase:databasePassword];
XCTAssertTrue([YapDatabaseCryptoUtils doesDatabaseNeedToBeConverted:databaseFilePath]);
__block NSData *_Nullable databaseSalt = nil;
YapDatabaseSaltBlock saltBlock = ^(NSData *saltData) {
OWSAssert(!databaseSalt);
OWSAssert(saltData);
databaseSalt = saltData;
};
__block NSData *_Nullable databaseKeySpec = nil;
YapDatabaseKeySpecBlock keySpecBlock = ^(NSData *keySpecData) {
OWSAssert(!databaseKeySpec);
OWSAssert(keySpecData);
databaseKeySpec = keySpecData;
};
NSError *_Nullable error = [YapDatabaseCryptoUtils convertDatabaseIfNecessary:databaseFilePath
databasePassword:databasePassword
saltBlock:saltBlock
keySpecBlock:keySpecBlock];
if (error) {
DDLogError(@"%s error: %@", __PRETTY_FUNCTION__, error);
}
XCTAssertNil(error);
XCTAssertFalse([YapDatabaseCryptoUtils doesDatabaseNeedToBeConverted:databaseFilePath]);
XCTAssertNotNil(databaseSalt);
XCTAssertEqual(databaseSalt.length, kSQLCipherSaltLength);
XCTAssertNotNil(databaseKeySpec);
XCTAssertEqual(databaseKeySpec.length, kSQLCipherKeySpecLength);
BOOL isValid = [self verifyTestDatabase:databaseFilePath
databasePassword:nil
databaseSalt:nil
databaseKeySpec:databaseKeySpec];
XCTAssertTrue(isValid);
}
// Verifies that legacy users with non-converted databases can convert.
- (void)testDatabaseConversionPerformance_WithKeyspec
{
NSData *databasePassword = [self randomDatabasePassword];
NSString *databaseFilePath = [self createTempDatabaseFilePath];
const int kItemCount = 50 * 1000;
// Create an populate the unconverted database.
[self openYapDatabase:databaseFilePath
databasePassword:databasePassword
databaseSalt:nil
databaseKeySpec:nil
databaseBlock:^(YapDatabase *database) {
YapDatabaseConnection *dbConnection = database.newConnection;
[dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
for (int i = 0; i < kItemCount; i++) {
[transaction setObject:@(i) forKey:@"test_key_name" inCollection:@"test_collection_name"];
}
}];
}];
XCTAssertTrue([YapDatabaseCryptoUtils doesDatabaseNeedToBeConverted:databaseFilePath]);
__block NSData *_Nullable databaseSalt = nil;
YapDatabaseSaltBlock saltBlock = ^(NSData *saltData) {
OWSAssert(!databaseSalt);
@ -366,10 +425,16 @@ NS_ASSUME_NONNULL_BEGIN
XCTAssertNotNil(databaseKeySpec);
XCTAssertEqual(databaseKeySpec.length, kSQLCipherKeySpecLength);
BOOL isValid = [self verifyTestDatabase:databaseFilePath
databasePassword:nil
databaseSalt:nil
databaseKeySpec:databaseKeySpec];
// Verify the contents of the unconverted database.
__block BOOL isValid = NO;
[self openYapDatabase:databaseFilePath
databasePassword:databasePassword
databaseSalt:nil
databaseKeySpec:nil
databaseBlock:^(YapDatabase *database) {
YapDatabaseConnection *dbConnection = database.newConnection;
isValid = [dbConnection numberOfKeysInCollection:@"test_collection_name"] == kItemCount;
}];
XCTAssertTrue(isValid);
}

View File

@ -1,5 +1,5 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import "OWS104CreateRecipientIdentities.h"
@ -24,42 +24,36 @@ static NSString *const OWS104CreateRecipientIdentitiesMigrationId = @"104";
return OWS104CreateRecipientIdentitiesMigrationId;
}
// Overriding runUp instead of runUpWithTransaction in order to implement a blocking migration.
- (void)runUp
- (void)runUpWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
{
[[OWSRecipientIdentity dbReadWriteConnection] readWriteWithBlock:^(
YapDatabaseReadWriteTransaction *_Nonnull transaction) {
NSMutableDictionary<NSString *, NSData *> *identityKeys = [NSMutableDictionary new];
NSMutableDictionary<NSString *, NSData *> *identityKeys = [NSMutableDictionary new];
[transaction
enumerateKeysAndObjectsInCollection:TSStorageManagerTrustedKeysCollection
usingBlock:^(
NSString *_Nonnull recipientId, id _Nonnull object, BOOL *_Nonnull stop) {
if (![object isKindOfClass:[NSData class]]) {
OWSFail(
@"%@ Unexpected object in trusted keys collection key: %@ object: %@",
self.logTag,
recipientId,
object);
return;
}
NSData *identityKey = (NSData *)object;
[identityKeys setObject:identityKey forKey:recipientId];
}];
[transaction
enumerateKeysAndObjectsInCollection:TSStorageManagerTrustedKeysCollection
usingBlock:^(NSString *_Nonnull recipientId, id _Nonnull object, BOOL *_Nonnull stop) {
if (![object isKindOfClass:[NSData class]]) {
OWSFail(@"%@ Unexpected object in trusted keys collection key: %@ object: %@",
self.logTag,
recipientId,
object);
return;
}
NSData *identityKey = (NSData *)object;
[identityKeys setObject:identityKey forKey:recipientId];
}];
[identityKeys enumerateKeysAndObjectsUsingBlock:^(
NSString *_Nonnull recipientId, NSData *_Nonnull identityKey, BOOL *_Nonnull stop) {
DDLogInfo(@"%@ Migrating identity key for recipient: %@", self.logTag, recipientId);
[[[OWSRecipientIdentity alloc] initWithRecipientId:recipientId
identityKey:identityKey
isFirstKnownKey:NO
createdAt:[NSDate dateWithTimeIntervalSince1970:0]
verificationState:OWSVerificationStateDefault]
saveWithTransaction:transaction];
}];
[self saveWithTransaction:transaction];
[identityKeys enumerateKeysAndObjectsUsingBlock:^(
NSString *_Nonnull recipientId, NSData *_Nonnull identityKey, BOOL *_Nonnull stop) {
DDLogInfo(@"%@ Migrating identity key for recipient: %@", self.logTag, recipientId);
[[[OWSRecipientIdentity alloc] initWithRecipientId:recipientId
identityKey:identityKey
isFirstKnownKey:NO
createdAt:[NSDate dateWithTimeIntervalSince1970:0]
verificationState:OWSVerificationStateDefault]
saveWithTransaction:transaction];
}];
[self saveWithTransaction:transaction];
}
@end

View File

@ -58,6 +58,42 @@ NS_ASSUME_NONNULL_BEGIN
}];
}
#pragma mark - Database Connections
#ifdef DEBUG
+ (YapDatabaseConnection *)dbReadConnection
{
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 = [TSStorageManager sharedManager].newDatabaseConnection;
OWSAssert([sharedDBConnection isKindOfClass:[OWSDatabaseConnection class]]);
((OWSDatabaseConnection *)sharedDBConnection).canWriteBeforeStorageReady = YES;
});
return sharedDBConnection;
}
- (YapDatabaseConnection *)dbReadConnection
{
return OWSDatabaseMigration.dbReadConnection;
}
- (YapDatabaseConnection *)dbReadWriteConnection
{
return OWSDatabaseMigration.dbReadWriteConnection;
}
#endif
@end
NS_ASSUME_NONNULL_END

View File

@ -1,5 +1,5 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import "OWSDatabaseMigrationRunner.h"
@ -36,7 +36,7 @@ NS_ASSUME_NONNULL_BEGIN
[[OWS100RemoveTSRecipientsMigration alloc] initWithStorageManager:storageManager],
[[OWS102MoveLoggingPreferenceToUserDefaults alloc] initWithStorageManager:storageManager],
[[OWS103EnableVideoCalling alloc] initWithStorageManager:storageManager],
// OWS104CreateRecipientIdentities is run separately. See runSafeBlockingMigrations.
[[OWS104CreateRecipientIdentities alloc] initWithStorageManager:storageManager],
[[OWS105AttachmentFilePaths alloc] initWithStorageManager:storageManager],
[[OWS106EnsureProfileComplete alloc] initWithStorageManager:storageManager]
];
@ -51,7 +51,7 @@ NS_ASSUME_NONNULL_BEGIN
{
TSStorageManager *storageManager = TSStorageManager.sharedManager;
return @[
[[OWS104CreateRecipientIdentities alloc] initWithStorageManager:storageManager],
// [[OWS104CreateRecipientIdentities alloc] initWithStorageManager:storageManager],
];
}

View File

@ -13,7 +13,7 @@ NS_ASSUME_NONNULL_BEGIN
@interface OWSBatchMessageProcessor : NSObject
+ (instancetype)sharedInstance;
+ (void)syncRegisterDatabaseExtension:(OWSStorage *)storage;
+ (void)asyncRegisterDatabaseExtension:(OWSStorage *)storage;
- (void)enqueueEnvelopeData:(NSData *)envelopeData plaintextData:(NSData *_Nullable)plaintextData;
- (void)handleAnyUnprocessedEnvelopesAsync;

View File

@ -200,7 +200,7 @@ NSString *const OWSMessageContentJobFinderExtensionGroup = @"OWSMessageContentJo
}
+ (void)syncRegisterDatabaseExtension:(OWSStorage *)storage
+ (void)asyncRegisterDatabaseExtension:(OWSStorage *)storage
{
YapDatabaseView *existingView = [storage registeredExtension:OWSMessageContentJobFinderExtensionName];
if (existingView) {
@ -208,7 +208,7 @@ NSString *const OWSMessageContentJobFinderExtensionGroup = @"OWSMessageContentJo
// already initialized
return;
}
[storage registerExtension:[self databaseExtension] withName:OWSMessageContentJobFinderExtensionName];
[storage asyncRegisterExtension:[self databaseExtension] withName:OWSMessageContentJobFinderExtensionName];
}
#pragma mark Logging
@ -443,9 +443,9 @@ NSString *const OWSMessageContentJobFinderExtensionGroup = @"OWSMessageContentJo
#pragma mark - class methods
+ (void)syncRegisterDatabaseExtension:(OWSStorage *)storage
+ (void)asyncRegisterDatabaseExtension:(OWSStorage *)storage
{
[OWSMessageContentJobFinder syncRegisterDatabaseExtension:storage];
[OWSMessageContentJobFinder asyncRegisterDatabaseExtension:storage];
}
#pragma mark - instance methods

View File

@ -1,9 +1,10 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
NS_ASSUME_NONNULL_BEGIN
extern NSString *const kNSNotificationName_BlockedPhoneNumbersDidChange;
// This class can be safely accessed and used from any thread.

View File

@ -30,10 +30,12 @@ NS_ASSUME_NONNULL_BEGIN
*/
+ (void)asyncRegisterDatabaseExtensions:(OWSStorage *)storage;
#ifdef DEBUG
/**
* Only use the sync version for testing, generally we'll want to register extensions async
*/
+ (void)blockingRegisterDatabaseExtensions:(OWSStorage *)storage;
#endif
@end

View File

@ -188,23 +188,17 @@ static NSString *const OWSDisappearingMessageFinderExpiresAtIndex = @"index_mess
return [[YapDatabaseSecondaryIndex alloc] initWithSetup:setup handler:handler];
}
#ifdef DEBUG
// Useful for tests, don't use in app startup path because it's slow.
+ (void)blockingRegisterDatabaseExtensions:(OWSStorage *)storage
{
[storage registerExtension:[self indexDatabaseExtension] withName:OWSDisappearingMessageFinderExpiresAtIndex];
}
#endif
+ (void)asyncRegisterDatabaseExtensions:(OWSStorage *)storage
{
[storage asyncRegisterExtension:[self indexDatabaseExtension]
withName:OWSDisappearingMessageFinderExpiresAtIndex
completionBlock:^(BOOL ready) {
if (ready) {
DDLogDebug(@"%@ completed registering extension async.", self.logTag);
} else {
DDLogError(@"%@ failed registering extension async.", self.logTag);
}
}];
[storage asyncRegisterExtension:[self indexDatabaseExtension] withName:OWSDisappearingMessageFinderExpiresAtIndex];
}
@end

View File

@ -19,10 +19,12 @@ NS_ASSUME_NONNULL_BEGIN
*/
+ (void)asyncRegisterDatabaseExtensionsWithStorageManager:(OWSStorage *)storage;
#ifdef DEBUG
/**
* Only use the sync version for testing, generally we'll want to register extensions async
*/
- (void)blockingRegisterDatabaseExtensions;
#endif
@end

View File

@ -117,24 +117,19 @@ static NSString *const OWSFailedAttachmentDownloadsJobAttachmentStateIndex = @"i
return [[YapDatabaseSecondaryIndex alloc] initWithSetup:setup handler:handler];
}
#ifdef DEBUG
// Useful for tests, don't use in app startup path because it's slow.
- (void)blockingRegisterDatabaseExtensions
{
[self.storageManager registerExtension:[self.class indexDatabaseExtension]
withName:OWSFailedAttachmentDownloadsJobAttachmentStateIndex];
}
#endif
+ (void)asyncRegisterDatabaseExtensionsWithStorageManager:(OWSStorage *)storage
{
[storage asyncRegisterExtension:[self indexDatabaseExtension]
withName:OWSFailedAttachmentDownloadsJobAttachmentStateIndex
completionBlock:^(BOOL ready) {
if (ready) {
DDLogDebug(@"%@ completed registering extension async.", self.logTag);
} else {
DDLogError(@"%@ failed registering extension async.", self.logTag);
}
}];
withName:OWSFailedAttachmentDownloadsJobAttachmentStateIndex];
}
@end

View File

@ -19,10 +19,12 @@ NS_ASSUME_NONNULL_BEGIN
*/
+ (void)asyncRegisterDatabaseExtensionsWithStorageManager:(OWSStorage *)storage;
#ifdef DEBUG
/**
* Only use the sync version for testing, generally we'll want to register extensions async
*/
- (void)blockingRegisterDatabaseExtensions;
#endif
@end

View File

@ -127,24 +127,18 @@ static NSString *const OWSFailedMessagesJobMessageStateIndex = @"index_outoing_m
return [[YapDatabaseSecondaryIndex alloc] initWithSetup:setup handler:handler];
}
#ifdef DEBUG
// Useful for tests, don't use in app startup path because it's slow.
- (void)blockingRegisterDatabaseExtensions
{
[self.storageManager registerExtension:[self.class indexDatabaseExtension]
withName:OWSFailedMessagesJobMessageStateIndex];
}
#endif
+ (void)asyncRegisterDatabaseExtensionsWithStorageManager:(OWSStorage *)storage
{
[storage asyncRegisterExtension:[self indexDatabaseExtension]
withName:OWSFailedMessagesJobMessageStateIndex
completionBlock:^(BOOL ready) {
if (ready) {
DDLogDebug(@"%@ completed registering extension async.", self.logTag);
} else {
DDLogError(@"%@ failed registering extension async.", self.logTag);
}
}];
[storage asyncRegisterExtension:[self indexDatabaseExtension] withName:OWSFailedMessagesJobMessageStateIndex];
}
@end

View File

@ -4,6 +4,7 @@
#import "OWSIdentityManager.h"
#import "AppContext.h"
#import "AppReadiness.h"
#import "NSDate+OWS.h"
#import "NSNotificationCenter+OWS.h"
#import "NotificationsProtocol.h"
@ -448,13 +449,13 @@ NSString *const kNSNotificationName_IdentityStateDidChange = @"kNSNotificationNa
{
OWSAssertIsOnMainThread();
if (!CurrentAppContext().isMainAppAndActive) {
// Only try to sync if the main app is active to avoid interfering with startup.
//
// applicationDidBecomeActive: will try to sync again when the main app becomes active.
return;
}
[AppReadiness runNowOrWhenAppIsReady:^{
[self syncQueuedVerificationStates];
}];
}
- (void)syncQueuedVerificationStates
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
@synchronized(self)
{

View File

@ -14,7 +14,7 @@ NS_ASSUME_NONNULL_BEGIN
@interface OWSMessageReceiver : NSObject
+ (instancetype)sharedInstance;
+ (void)syncRegisterDatabaseExtension:(OWSStorage *)storage;
+ (void)asyncRegisterDatabaseExtension:(OWSStorage *)storage;
- (void)handleReceivedEnvelope:(OWSSignalServiceProtosEnvelope *)envelope;
- (void)handleAnyUnprocessedEnvelopesAsync;

View File

@ -190,7 +190,7 @@ NSString *const OWSMessageDecryptJobFinderExtensionGroup = @"OWSMessageProcessin
});
}
+ (void)syncRegisterDatabaseExtension:(OWSStorage *)storage
+ (void)asyncRegisterDatabaseExtension:(OWSStorage *)storage
{
[self registerLegacyClasses];
@ -200,7 +200,7 @@ NSString *const OWSMessageDecryptJobFinderExtensionGroup = @"OWSMessageProcessin
// already initialized
return;
}
[storage registerExtension:[self databaseExtension] withName:OWSMessageDecryptJobFinderExtensionName];
[storage asyncRegisterExtension:[self databaseExtension] withName:OWSMessageDecryptJobFinderExtensionName];
}
@end
@ -422,9 +422,9 @@ NSString *const OWSMessageDecryptJobFinderExtensionGroup = @"OWSMessageProcessin
#pragma mark - class methods
+ (void)syncRegisterDatabaseExtension:(OWSStorage *)storage
+ (void)asyncRegisterDatabaseExtension:(OWSStorage *)storage
{
[OWSMessageDecryptJobFinder syncRegisterDatabaseExtension:storage];
[OWSMessageDecryptJobFinder asyncRegisterDatabaseExtension:storage];
}
#pragma mark - instance methods

View File

@ -95,11 +95,7 @@ NSString *const OWSIncomingMessageFinderColumnSourceDeviceId = @"OWSIncomingMess
+ (void)asyncRegisterExtensionWithStorageManager:(OWSStorage *)storage
{
DDLogInfo(@"%@ registering async.", self.logTag);
[storage asyncRegisterExtension:self.indexExtension
withName:OWSIncomingMessageFinderExtensionName
completionBlock:^(BOOL ready) {
DDLogInfo(@"%@ finished registering async.", self.logTag);
}];
[storage asyncRegisterExtension:self.indexExtension withName:OWSIncomingMessageFinderExtensionName];
}
// We should not normally hit this, as we should have prefer registering async, but it is useful for testing.

View File

@ -10,6 +10,30 @@ extern NSString *const StorageIsReadyNotification;
@class YapDatabaseExtension;
@protocol OWSDatabaseConnectionDelegate <NSObject>
- (BOOL)areAllRegistrationsComplete;
@end
#pragma mark -
@interface OWSDatabaseConnection : YapDatabaseConnection
@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;
@end
#pragma mark -
@interface OWSStorage : NSObject
- (instancetype)init NS_UNAVAILABLE;
@ -37,14 +61,16 @@ extern NSString *const StorageIsReadyNotification;
// TODO: Deprecate?
- (nullable YapDatabaseConnection *)newDatabaseConnection;
#ifdef DEBUG
- (BOOL)registerExtension:(YapDatabaseExtension *)extension withName:(NSString *)extensionName;
- (void)asyncRegisterExtension:(YapDatabaseExtension *)extension
withName:(NSString *)extensionName
completionBlock:(nullable void (^)(BOOL ready))completionBlock;
#endif
- (void)asyncRegisterExtension:(YapDatabaseExtension *)extension withName:(NSString *)extensionName;
- (nullable id)registeredExtension:(NSString *)extensionName;
- (unsigned long long)databaseFileSize;
- (YapDatabaseConnection *)registrationConnection;
#pragma mark - Password
/**

View File

@ -38,14 +38,6 @@ typedef NSData *_Nullable (^CreateDatabaseMetadataBlock)(void);
#pragma mark -
@protocol OWSDatabaseConnectionDelegate <NSObject>
- (BOOL)areSyncRegistrationsComplete;
@end
#pragma mark -
@interface YapDatabaseConnection ()
- (id)initWithDatabase:(YapDatabase *)database;
@ -54,18 +46,6 @@ typedef NSData *_Nullable (^CreateDatabaseMetadataBlock)(void);
#pragma mark -
@interface OWSDatabaseConnection : YapDatabaseConnection
@property (atomic, weak) id<OWSDatabaseConnectionDelegate> delegate;
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithDatabase:(YapDatabase *)database
delegate:(id<OWSDatabaseConnectionDelegate>)delegate NS_DESIGNATED_INITIALIZER;
@end
#pragma mark -
@implementation OWSDatabaseConnection
- (id)initWithDatabase:(YapDatabase *)database delegate:(id<OWSDatabaseConnectionDelegate>)delegate
@ -94,28 +74,20 @@ typedef NSData *_Nullable (^CreateDatabaseMetadataBlock)(void);
{
id<OWSDatabaseConnectionDelegate> delegate = self.delegate;
OWSAssert(delegate);
OWSAssert(delegate.areSyncRegistrationsComplete);
OWSAssert(delegate.areAllRegistrationsComplete || self.canWriteBeforeStorageReady);
[super readWriteWithBlock:block];
}
- (void)asyncReadWriteWithBlock:(void (^)(YapDatabaseReadWriteTransaction *transaction))block
{
id<OWSDatabaseConnectionDelegate> delegate = self.delegate;
OWSAssert(delegate);
OWSAssert(delegate.areSyncRegistrationsComplete);
[super asyncReadWriteWithBlock:block];
[self asyncReadWriteWithBlock:block completionQueue:NULL completionBlock:NULL];
}
- (void)asyncReadWriteWithBlock:(void (^)(YapDatabaseReadWriteTransaction *transaction))block
completionBlock:(nullable dispatch_block_t)completionBlock
{
id<OWSDatabaseConnectionDelegate> delegate = self.delegate;
OWSAssert(delegate);
OWSAssert(delegate.areSyncRegistrationsComplete);
[super asyncReadWriteWithBlock:block completionBlock:completionBlock];
[self asyncReadWriteWithBlock:block completionQueue:NULL completionBlock:completionBlock];
}
- (void)asyncReadWriteWithBlock:(void (^)(YapDatabaseReadWriteTransaction *transaction))block
@ -124,7 +96,7 @@ typedef NSData *_Nullable (^CreateDatabaseMetadataBlock)(void);
{
id<OWSDatabaseConnectionDelegate> delegate = self.delegate;
OWSAssert(delegate);
OWSAssert(delegate.areSyncRegistrationsComplete);
OWSAssert(delegate.areAllRegistrationsComplete || self.canWriteBeforeStorageReady);
[super asyncReadWriteWithBlock:block completionQueue:completionQueue completionBlock:completionBlock];
}
@ -138,6 +110,8 @@ typedef NSData *_Nullable (^CreateDatabaseMetadataBlock)(void);
- (void)addConnection:(YapDatabaseConnection *)connection;
- (YapDatabaseConnection *)registrationConnection;
@end
#pragma mark -
@ -146,6 +120,8 @@ typedef NSData *_Nullable (^CreateDatabaseMetadataBlock)(void);
@property (atomic, weak) id<OWSDatabaseConnectionDelegate> delegate;
@property (atomic, nullable) YapDatabaseConnection *registrationConnectionCached;
- (instancetype)init NS_UNAVAILABLE;
- (id)initWithPath:(NSString *)inPath
serializer:(nullable YapDatabaseSerializer)inSerializer
@ -192,6 +168,25 @@ typedef NSData *_Nullable (^CreateDatabaseMetadataBlock)(void);
return connection;
}
- (YapDatabaseConnection *)registrationConnection
{
@synchronized(self)
{
if (!self.registrationConnectionCached) {
YapDatabaseConnection *connection = [super registrationConnection];
#ifdef DEBUG
// Flag the registration connection as such.
OWSAssert([connection isKindOfClass:[OWSDatabaseConnection class]]);
((OWSDatabaseConnection *)connection).canWriteBeforeStorageReady = YES;
#endif
self.registrationConnectionCached = connection;
}
return self.registrationConnectionCached;
}
}
@end
#pragma mark -
@ -317,6 +312,16 @@ typedef NSData *_Nullable (^CreateDatabaseMetadataBlock)(void);
return NO;
}
- (BOOL)areAllRegistrationsComplete
{
DDLogInfo(@"%@ areAllRegistrationsComplete: %d %d = %d",
self.logTag,
self.areSyncRegistrationsComplete,
self.areAsyncRegistrationsComplete,
self.areSyncRegistrationsComplete && self.areAsyncRegistrationsComplete);
return self.areSyncRegistrationsComplete && self.areAsyncRegistrationsComplete;
}
- (void)runSyncRegistrations
{
OWS_ABSTRACT_METHOD();
@ -352,11 +357,19 @@ typedef NSData *_Nullable (^CreateDatabaseMetadataBlock)(void);
for (OWSStorage *storage in self.allStorages) {
[storage runAsyncRegistrationsWithCompletion:^{
[self postRegistrationCompleteNotificationIfPossible];
}];
((OWSDatabase *)storage.database).registrationConnectionCached = nil;
}
}
- (YapDatabaseConnection *)registrationConnection
{
return self.database.registrationConnection;
}
+ (void)postRegistrationCompleteNotificationIfPossible
{
if (!self.isStorageReady) {
@ -374,7 +387,7 @@ typedef NSData *_Nullable (^CreateDatabaseMetadataBlock)(void);
+ (BOOL)isStorageReady
{
for (OWSStorage *storage in self.allStorages) {
if (!storage.areAsyncRegistrationsComplete) {
if (!storage.areAllRegistrationsComplete) {
return NO;
}
}
@ -458,16 +471,25 @@ typedef NSData *_Nullable (^CreateDatabaseMetadataBlock)(void);
return self.database.newConnection;
}
#ifdef DEBUG
- (BOOL)registerExtension:(YapDatabaseExtension *)extension withName:(NSString *)extensionName
{
return [self.database registerExtension:extension withName:extensionName];
}
#endif
- (void)asyncRegisterExtension:(YapDatabaseExtension *)extension
withName:(NSString *)extensionName
completionBlock:(nullable void (^)(BOOL ready))completionBlock
{
[self.database asyncRegisterExtension:extension withName:extensionName completionBlock:completionBlock];
[self.database asyncRegisterExtension:extension
withName:extensionName
completionBlock:^(BOOL ready) {
if (!ready) {
OWSFail(@"%@ asyncRegisterExtension failed: %@", self.logTag, extensionName);
} else {
DDLogVerbose(@"%@ asyncRegisterExtension succeeded: %@", self.logTag, extensionName);
}
}];
}
- (nullable id)registeredExtension:(NSString *)extensionName

View File

@ -23,16 +23,16 @@ extern NSString *const TSSecondaryDevicesDatabaseViewExtensionName;
+ (void)registerCrossProcessNotifier:(OWSStorage *)storage;
// This method must be called _AFTER_ registerThreadInteractionsDatabaseView.
+ (void)registerThreadDatabaseView:(OWSStorage *)storage;
// This method must be called _AFTER_ asyncRegisterThreadInteractionsDatabaseView.
+ (void)asyncRegisterThreadDatabaseView:(OWSStorage *)storage;
+ (void)registerThreadInteractionsDatabaseView:(OWSStorage *)storage;
+ (void)asyncRegisterThreadInteractionsDatabaseView:(OWSStorage *)storage;
+ (void)asyncRegisterThreadOutgoingMessagesDatabaseView:(OWSStorage *)storage;
// Instances of OWSReadTracking for wasRead is NO and shouldAffectUnreadCounts is YES.
//
// Should be used for "unread message counts".
+ (void)registerUnreadDatabaseView:(OWSStorage *)storage;
+ (void)asyncRegisterUnreadDatabaseView:(OWSStorage *)storage;
// Should be used for "unread indicator".
//

View File

@ -72,19 +72,13 @@ NSString *const TSSecondaryDevicesDatabaseViewExtensionName = @"TSSecondaryDevic
options:options];
if (async) {
[storage asyncRegisterExtension:view
withName:viewName
completionBlock:^(BOOL ready) {
OWSCAssert(ready);
DDLogInfo(@"%@ asyncRegisterExtension: %@ -> %d", self.logTag, viewName, ready);
}];
[storage asyncRegisterExtension:view withName:viewName];
} else {
[storage registerExtension:view withName:viewName];
}
}
+ (void)registerUnreadDatabaseView:(OWSStorage *)storage
+ (void)asyncRegisterUnreadDatabaseView:(OWSStorage *)storage
{
YapDatabaseViewGrouping *viewGrouping = [YapDatabaseViewGrouping withObjectBlock:^NSString *(
YapDatabaseReadTransaction *transaction, NSString *collection, NSString *key, id object) {
@ -100,7 +94,7 @@ NSString *const TSSecondaryDevicesDatabaseViewExtensionName = @"TSSecondaryDevic
[self registerMessageDatabaseViewWithName:TSUnreadDatabaseViewExtensionName
viewGrouping:viewGrouping
version:@"1"
async:NO
async:YES
storage:storage];
}
@ -152,7 +146,7 @@ NSString *const TSSecondaryDevicesDatabaseViewExtensionName = @"TSSecondaryDevic
storage:storage];
}
+ (void)registerThreadInteractionsDatabaseView:(OWSStorage *)storage
+ (void)asyncRegisterThreadInteractionsDatabaseView:(OWSStorage *)storage
{
YapDatabaseViewGrouping *viewGrouping = [YapDatabaseViewGrouping withObjectBlock:^NSString *(
YapDatabaseReadTransaction *transaction, NSString *collection, NSString *key, id object) {
@ -165,7 +159,7 @@ NSString *const TSSecondaryDevicesDatabaseViewExtensionName = @"TSSecondaryDevic
[self registerMessageDatabaseViewWithName:TSMessageDatabaseViewExtensionName
viewGrouping:viewGrouping
version:@"1"
async:NO
async:YES
storage:storage];
}
@ -186,7 +180,7 @@ NSString *const TSSecondaryDevicesDatabaseViewExtensionName = @"TSSecondaryDevic
storage:storage];
}
+ (void)registerThreadDatabaseView:(OWSStorage *)storage
+ (void)asyncRegisterThreadDatabaseView:(OWSStorage *)storage
{
YapDatabaseView *threadView = [storage registeredExtension:TSThreadDatabaseViewExtensionName];
if (threadView) {
@ -234,7 +228,7 @@ NSString *const TSSecondaryDevicesDatabaseViewExtensionName = @"TSSecondaryDevic
YapDatabaseView *databaseView =
[[YapDatabaseAutoView alloc] initWithGrouping:viewGrouping sorting:viewSorting versionTag:@"3" options:options];
[storage registerExtension:databaseView withName:TSThreadDatabaseViewExtensionName];
[storage asyncRegisterExtension:databaseView withName:TSThreadDatabaseViewExtensionName];
}
/**
@ -347,15 +341,7 @@ NSString *const TSSecondaryDevicesDatabaseViewExtensionName = @"TSSecondaryDevic
YapDatabaseView *view =
[[YapDatabaseAutoView alloc] initWithGrouping:viewGrouping sorting:viewSorting versionTag:@"3" options:options];
[storage asyncRegisterExtension:view
withName:TSSecondaryDevicesDatabaseViewExtensionName
completionBlock:^(BOOL ready) {
if (ready) {
DDLogDebug(@"%@ Successfully set up extension: %@", self.logTag, TSSecondaryDevicesGroup);
} else {
DDLogError(@"%@ Unable to setup extension: %@", self.logTag, TSSecondaryDevicesGroup);
}
}];
[storage asyncRegisterExtension:view withName:TSSecondaryDevicesDatabaseViewExtensionName];
}
+ (id)unseenDatabaseViewExtension:(YapDatabaseReadTransaction *)transaction

View File

@ -29,12 +29,6 @@ void runSyncRegistrationsForStorage(OWSStorage *storage)
// Synchronously register extensions which are essential for views.
[TSDatabaseView registerCrossProcessNotifier:storage];
[TSDatabaseView registerThreadInteractionsDatabaseView:storage];
[TSDatabaseView registerThreadDatabaseView:storage];
[TSDatabaseView registerUnreadDatabaseView:storage];
[storage registerExtension:[TSDatabaseSecondaryIndexes registerTimeStampIndex] withName:@"idx"];
[OWSMessageReceiver syncRegisterDatabaseExtension:storage];
[OWSBatchMessageProcessor syncRegisterDatabaseExtension:storage];
}
void runAsyncRegistrationsForStorage(OWSStorage *storage)
@ -45,6 +39,14 @@ void runAsyncRegistrationsForStorage(OWSStorage *storage)
//
// All sync registrations must be done before all async registrations,
// or the sync registrations will block on the async registrations.
[TSDatabaseView asyncRegisterThreadInteractionsDatabaseView:storage];
[TSDatabaseView asyncRegisterThreadDatabaseView:storage];
[TSDatabaseView asyncRegisterUnreadDatabaseView:storage];
[storage asyncRegisterExtension:[TSDatabaseSecondaryIndexes registerTimeStampIndex] withName:@"idx"];
[OWSMessageReceiver asyncRegisterDatabaseExtension:storage];
[OWSBatchMessageProcessor asyncRegisterDatabaseExtension:storage];
[TSDatabaseView asyncRegisterUnseenDatabaseView:storage];
[TSDatabaseView asyncRegisterThreadOutgoingMessagesDatabaseView:storage];
[TSDatabaseView asyncRegisterThreadSpecialMessagesDatabaseView:storage];
@ -127,12 +129,26 @@ void runAsyncRegistrationsForStorage(OWSStorage *storage)
runAsyncRegistrationsForStorage(self);
DDLogVerbose(@"%@ async registrations enqueued.", self.logTag);
// Block until all async registrations are complete.
YapDatabaseConnection *dbConnection = self.newDatabaseConnection;
YapDatabaseConnection *dbConnection = self.registrationConnection;
OWSAssert(self.registrationConnection);
// [dbConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction * _Nonnull transaction) {
// OWSAssert(!self.areAsyncRegistrationsComplete);
//
// DDLogVerbose(@"%@ async registrations flushed.", self.logTag);
//
// self.areAsyncRegistrationsComplete = YES;
//
// completion();
// }];
[dbConnection flushTransactionsWithCompletionQueue:dispatch_get_main_queue()
completionBlock:^{
OWSAssert(!self.areAsyncRegistrationsComplete);
DDLogVerbose(@"%@ async registrations complete.", self.logTag);
self.areAsyncRegistrationsComplete = YES;
completion();

View File

@ -0,0 +1,21 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
NS_ASSUME_NONNULL_BEGIN
typedef void (^AppReadyBlock)(void);
@interface AppReadiness : NSObject
- (instancetype)init NS_UNAVAILABLE;
+ (BOOL)isAppReady;
+ (void)setAppIsReady;
+ (void)runNowOrWhenAppIsReady:(AppReadyBlock)block;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,100 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import "AppReadiness.h"
NS_ASSUME_NONNULL_BEGIN
@interface AppReadiness ()
@property (atomic) BOOL isAppReady;
@property (nonatomic, nullable) NSMutableArray<AppReadyBlock> *appReadyBlocks;
@end
#pragma mark -
@implementation AppReadiness
+ (instancetype)sharedManager
{
static AppReadiness *sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] initDefault];
});
return sharedMyManager;
}
- (instancetype)initDefault
{
self = [super init];
if (!self) {
return self;
}
OWSSingletonAssert();
return self;
}
+ (BOOL)isAppReady
{
return self.sharedManager.isAppReady;
}
+ (void)runNowOrWhenAppIsReady:(AppReadyBlock)block
{
OWSAssertIsOnMainThread();
[self.sharedManager runNowOrWhenAppIsReady:block];
}
- (void)runNowOrWhenAppIsReady:(AppReadyBlock)block
{
OWSAssertIsOnMainThread();
OWSAssert(block);
if (self.isAppReady) {
block();
return;
}
if (!self.appReadyBlocks) {
self.appReadyBlocks = [NSMutableArray new];
}
[self.appReadyBlocks addObject:block];
}
+ (void)setAppIsReady
{
[self.sharedManager setAppIsReady];
}
- (void)setAppIsReady
{
OWSAssertIsOnMainThread();
OWSAssert(!self.isAppReady);
self.isAppReady = YES;
[self runAppReadyBlocks];
}
- (void)runAppReadyBlocks
{
OWSAssertIsOnMainThread();
OWSAssert(self.isAppReady);
for (AppReadyBlock block in self.appReadyBlocks) {
block();
}
self.appReadyBlocks = nil;
}
@end
NS_ASSUME_NONNULL_END

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
@ -18,12 +18,16 @@ NS_ASSUME_NONNULL_BEGIN
#define kWeekInMs (kDayInMs * 7)
#define kMonthInMs (kDayInMs * 30)
@interface NSDate (millisecondTimeStamp)
@interface NSDate (OWS)
+ (uint64_t)ows_millisecondTimeStamp;
+ (NSDate *)ows_dateWithMillisecondsSince1970:(uint64_t)milliseconds;
+ (uint64_t)ows_millisecondsSince1970ForDate:(NSDate *)date;
#pragma mark - Debugging
+ (NSString *)debugTimestamp;
@end
NS_ASSUME_NONNULL_END

View File

@ -1,5 +1,5 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import "NSDate+OWS.h"
@ -7,7 +7,7 @@
NS_ASSUME_NONNULL_BEGIN
@implementation NSDate (millisecondTimeStamp)
@implementation NSDate (OWS)
+ (uint64_t)ows_millisecondTimeStamp
{
@ -26,6 +26,31 @@ NS_ASSUME_NONNULL_BEGIN
return (uint64_t)(date.timeIntervalSince1970 * 1000);
}
#pragma mark - Debugging
+ (NSDateFormatter *)dateFormatterForDebugTimestamps
{
static NSDateFormatter *formatter;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
formatter = [NSDateFormatter new];
[formatter setLocale:[NSLocale currentLocale]];
[formatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSS"];
});
return formatter;
}
+ (NSString *)debugTimestamp
{
// We use ows_millisecondTimeStamp for its higher precision.
return [[NSDate ows_dateWithMillisecondsSince1970:[NSDate ows_millisecondTimeStamp]] formattedAsDebugTimestamp];
}
- (NSString *)formattedAsDebugTimestamp
{
return [[NSDate dateFormatterForDebugTimestamps] stringFromDate:self];
}
@end
NS_ASSUME_NONNULL_END

View File

@ -58,12 +58,13 @@ NS_ASSUME_NONNULL_BEGIN
sharedDataFilePath:(NSString *)newFilePath
exceptionName:(NSString *)exceptionName
{
DDLogInfo(@"%@ Moving file or directory from: %@ to: %@", self.logTag, oldFilePath, newFilePath);
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:oldFilePath]) {
return;
}
DDLogInfo(@"%@ Moving file or directory from: %@ to: %@", self.logTag, oldFilePath, newFilePath);
if ([fileManager fileExistsAtPath:newFilePath]) {
OWSFail(@"%@ Can't move file or directory from: %@ to: %@; destination already exists.",
self.logTag,