Clean up ahead of PR.

pull/1/head
Matthew Chen 8 years ago
parent 18d39f15f2
commit 08ba7c85ed

@ -28,14 +28,12 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic) NSString *recordName; @property (nonatomic) NSString *recordName;
// This property is optional and represents the location of this
// item relative to the root directory for items of this type.
//@property (nonatomic, nullable) NSString *fileRelativePath;
// This property is optional and is only used for attachments. // This property is optional and is only used for attachments.
@property (nonatomic, nullable) OWSAttachmentExport *attachmentExport; @property (nonatomic, nullable) OWSAttachmentExport *attachmentExport;
// This property is optional. // This property is optional.
//
// See comments in `OWSBackupIO`.
@property (nonatomic, nullable) NSNumber *uncompressedDataLength; @property (nonatomic, nullable) NSNumber *uncompressedDataLength;
- (instancetype)init NS_UNAVAILABLE; - (instancetype)init NS_UNAVAILABLE;
@ -63,6 +61,19 @@ NS_ASSUME_NONNULL_BEGIN
#pragma mark - #pragma mark -
// Used to serialize database snapshot contents.
// Writes db entities using protobufs into snapshot fragments.
// Snapshot fragments are compressed (they compress _very well_,
// around 20x smaller) then encrypted. Ordering matters in
// snapshot contents (entities should we restored in the same
// order they are serialized), so we are always careful to preserve
// ordering of entities within a snapshot AND ordering of snapshot
// fragments within a bakckup.
//
// This stream is used to write entities one at a time and takes
// care of sharding them into fragments, compressing and encrypting
// those fragments. Fragment size is fixed to reduce worst case
// memory usage.
@interface OWSDBExportStream : NSObject @interface OWSDBExportStream : NSObject
@property (nonatomic) OWSBackupIO *backupIO; @property (nonatomic) OWSBackupIO *backupIO;
@ -97,6 +108,10 @@ NS_ASSUME_NONNULL_BEGIN
return self; return self;
} }
// It isn't strictly necessary to capture the entity type (the importer doesn't
// use this state), but I think it'll be helpful to have around to future-proof
// this work, help with debugging issue, etc.
- (BOOL)writeObject:(TSYapDatabaseObject *)object - (BOOL)writeObject:(TSYapDatabaseObject *)object
entityType:(OWSSignalServiceProtosBackupSnapshotBackupEntityType)entityType entityType:(OWSSignalServiceProtosBackupSnapshotBackupEntityType)entityType
{ {
@ -133,15 +148,17 @@ NS_ASSUME_NONNULL_BEGIN
} }
// Write cached data to disk, if necessary. // Write cached data to disk, if necessary.
//
// Returns YES on success.
- (BOOL)flush - (BOOL)flush
{ {
if (!self.backupSnapshotBuilder) { if (!self.backupSnapshotBuilder) {
// No data to flush to disk.
return YES; return YES;
} }
// Try to release allocated buffers ASAP. // Try to release allocated buffers ASAP.
@autoreleasepool { @autoreleasepool {
NSData *_Nullable uncompressedData = [self.backupSnapshotBuilder build].data; NSData *_Nullable uncompressedData = [self.backupSnapshotBuilder build].data;
NSUInteger uncompressedDataLength = uncompressedData.length; NSUInteger uncompressedDataLength = uncompressedData.length;
self.backupSnapshotBuilder = nil; self.backupSnapshotBuilder = nil;
@ -171,6 +188,12 @@ NS_ASSUME_NONNULL_BEGIN
#pragma mark - #pragma mark -
// This class is used to:
//
// * Lazy-encrypt and eagerly cleanup attachment uploads.
// To reduce disk footprint of backup export process,
// we only want to have one attachment export on disk
// at a time.
@interface OWSAttachmentExport : NSObject @interface OWSAttachmentExport : NSObject
@property (nonatomic) OWSBackupIO *backupIO; @property (nonatomic) OWSBackupIO *backupIO;
@ -210,9 +233,13 @@ NS_ASSUME_NONNULL_BEGIN
{ {
// Surface memory leaks by logging the deallocation. // Surface memory leaks by logging the deallocation.
DDLogVerbose(@"Dealloc: %@", self.class); DDLogVerbose(@"Dealloc: %@", self.class);
[self cleanUp];
} }
// On success, encryptedItem will be non-nil. // On success, encryptedItem will be non-nil.
//
// Returns YES on success.
- (BOOL)prepareForUpload - (BOOL)prepareForUpload
{ {
OWSAssert(self.attachmentId.length > 0); OWSAssert(self.attachmentId.length > 0);
@ -241,6 +268,12 @@ NS_ASSUME_NONNULL_BEGIN
return YES; return YES;
} }
// Returns YES on success.
- (BOOL)cleanUp
{
return [OWSFileSystem deleteFileIfExists:self.encryptedItem.filePath];
}
@end @end
#pragma mark - #pragma mark -
@ -669,6 +702,11 @@ NS_ASSUME_NONNULL_BEGIN
return; return;
} }
if (![attachmentExport cleanUp]) {
DDLogError(@"%@ couldn't clean up attachment export.", self.logTag);
// Attachment files are non-critical so any error uploading them is recoverable.
}
OWSBackupExportItem *exportItem = [OWSBackupExportItem new]; OWSBackupExportItem *exportItem = [OWSBackupExportItem new];
exportItem.encryptedItem = attachmentExport.encryptedItem; exportItem.encryptedItem = attachmentExport.encryptedItem;
exportItem.recordName = recordName; exportItem.recordName = recordName;
@ -685,6 +723,11 @@ NS_ASSUME_NONNULL_BEGIN
failure:^(NSError *error) { failure:^(NSError *error) {
// Ensure that we continue to work off the main thread. // Ensure that we continue to work off the main thread.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
if (![attachmentExport cleanUp]) {
DDLogError(@"%@ couldn't clean up attachment export.", self.logTag);
// Attachment files are non-critical so any error uploading them is recoverable.
}
// Attachment files are non-critical so any error uploading them is recoverable. // Attachment files are non-critical so any error uploading them is recoverable.
[weakSelf saveNextFileToCloudWithCompletion:completion]; [weakSelf saveNextFileToCloudWithCompletion:completion];
}); });

@ -45,6 +45,11 @@ NS_ASSUME_NONNULL_BEGIN
- (nullable NSData *)compressData:(NSData *)srcData; - (nullable NSData *)compressData:(NSData *)srcData;
// I'm using the (new in iOS 9) compressionlib. One of its weaknesses is that it
// requires you to pre-allocate output buffers during compression and decompression.
// During decompression this is particularly tricky since there's no way to safely
// predict how large the output will be based on the input. So, we store the
// uncompressed size for compressed backup items.
- (nullable NSData *)decompressData:(NSData *)srcData uncompressedDataLength:(NSUInteger)uncompressedDataLength; - (nullable NSData *)decompressData:(NSData *)srcData uncompressedDataLength:(NSUInteger)uncompressedDataLength;
@end @end

@ -28,9 +28,9 @@ NS_ASSUME_NONNULL_BEGIN
// Returns NO IFF the directory does not exist and could not be created. // Returns NO IFF the directory does not exist and could not be created.
+ (BOOL)ensureDirectoryExists:(NSString *)dirPath; + (BOOL)ensureDirectoryExists:(NSString *)dirPath;
+ (void)deleteFile:(NSString *)filePath; + (BOOL)deleteFile:(NSString *)filePath;
+ (void)deleteFileIfExists:(NSString *)filePath; + (BOOL)deleteFileIfExists:(NSString *)filePath;
+ (NSArray<NSString *> *_Nullable)allFilesInDirectoryRecursive:(NSString *)dirPath error:(NSError **)error; + (NSArray<NSString *> *_Nullable)allFilesInDirectoryRecursive:(NSString *)dirPath error:(NSError **)error;

@ -227,20 +227,23 @@ NS_ASSUME_NONNULL_BEGIN
} }
} }
+ (void)deleteFile:(NSString *)filePath + (BOOL)deleteFile:(NSString *)filePath
{ {
NSError *error; NSError *error;
[[NSFileManager defaultManager] removeItemAtPath:filePath error:&error]; BOOL success = [[NSFileManager defaultManager] removeItemAtPath:filePath error:&error];
if (error) { if (!success || error) {
DDLogError(@"%@ Failed to delete file: %@", self.logTag, error.description); DDLogError(@"%@ Failed to delete file: %@", self.logTag, error.description);
return NO;
} }
return YES;
} }
+ (void)deleteFileIfExists:(NSString *)filePath + (BOOL)deleteFileIfExists:(NSString *)filePath
{ {
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) { if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
[self deleteFile:filePath]; return YES;
} }
return [self deleteFile:filePath];
} }
+ (NSArray<NSString *> *_Nullable)allFilesInDirectoryRecursive:(NSString *)dirPath error:(NSError **)error + (NSArray<NSString *> *_Nullable)allFilesInDirectoryRecursive:(NSString *)dirPath error:(NSError **)error

Loading…
Cancel
Save