From 668cc22af383541c53e8aafe5875a4d3bef45815 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Thu, 13 Dec 2018 09:52:15 -0700 Subject: [PATCH] Perf: Insert outgoing message into conversation before save completes. This was a huge savings - e.g. 50-70% of the time between tapping send and rendering the message takes place in the save/postWriteTransaction blocks --- .../ConversationViewController.m | 3 +- .../ConversationView/ConversationViewModel.h | 2 ++ .../ConversationView/ConversationViewModel.m | 28 +++++++++++++++++++ SignalMessaging/utils/ThreadUtil.h | 2 +- SignalMessaging/utils/ThreadUtil.m | 12 +++++--- .../src/Network/MessageSenderJobQueue.swift | 1 + 6 files changed, 42 insertions(+), 6 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index e67c3d57e..c6f4d56ef 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -3887,13 +3887,14 @@ typedef enum : NSUInteger { inThread:self.thread quotedReplyModel:self.inputToolbar.quotedReply]; } else { - [self.editingDatabaseConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { + [self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) { message = [ThreadUtil enqueueMessageWithText:text inThread:self.thread quotedReplyModel:self.inputToolbar.quotedReply transaction:transaction]; }]; } + [self.conversationViewModel appendUnsavedOutgoingMessage:message]; [self messageWasSent:message]; diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewModel.h b/Signal/src/ViewControllers/ConversationView/ConversationViewModel.h index 4b915b759..834568765 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewModel.h +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewModel.h @@ -7,6 +7,7 @@ NS_ASSUME_NONNULL_BEGIN @class ConversationStyle; @class ConversationViewModel; @class OWSQuotedReplyModel; +@class TSOutgoingMessage; @class TSThread; @class ThreadDynamicInteractions; @@ -101,6 +102,7 @@ typedef NS_ENUM(NSUInteger, ConversationUpdateItemType) { - (BOOL)canLoadMoreItems; - (nullable NSIndexPath *)ensureLoadWindowContainsQuotedReply:(OWSQuotedReplyModel *)quotedReply; +- (void)appendUnsavedOutgoingMessage:(TSOutgoingMessage *)outgoingMessage; @end diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewModel.m b/Signal/src/ViewControllers/ConversationView/ConversationViewModel.m index 31ec67170..b5f0ddf20 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewModel.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewModel.m @@ -12,6 +12,7 @@ #import #import #import +#import #import #import #import @@ -1491,6 +1492,33 @@ static const int kYapDatabaseRangeMinLength = 0; return !hasError; } +- (void)appendUnsavedOutgoingMessage:(TSOutgoingMessage *)outgoingMessage +{ +// id viewItem = [[OutgoingUnsavedConversationViewItem alloc] initWithOutgoingMessage:outgoingMessage]; + + __block ConversationInteractionViewItem *viewItem; + [self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction * _Nonnull transaction) { + viewItem = + [[ConversationInteractionViewItem alloc] initWithInteraction:outgoingMessage + isGroupThread:self.thread.isGroupThread + transaction:transaction + conversationStyle:self.delegate.conversationStyle]; + }]; + + ConversationUpdateItem *updateItem = [[ConversationUpdateItem alloc] initWithUpdateItemType:ConversationUpdateItemType_Insert + oldIndex:0 + newIndex:self.viewItems.count + viewItem:viewItem]; + + NSMutableArray> *viewItems = [self.viewItems mutableCopy]; + [viewItems addObject:viewItem]; + self.viewItems = viewItems; + ConversationUpdate *conversationUpdate = [[ConversationUpdate alloc] initWithConversationUpdateType:ConversationUpdateType_Diff + updateItems:@[updateItem] + shouldAnimateUpdates:NO]; + [self.delegate conversationViewModelDidUpdate:conversationUpdate]; +} + // Whenever an interaction is modified, we need to reload it from the DB // and update the corresponding view item. - (void)reloadInteractionForViewItem:(id)viewItem diff --git a/SignalMessaging/utils/ThreadUtil.h b/SignalMessaging/utils/ThreadUtil.h index 1c2cdbc1a..fe6348fc6 100644 --- a/SignalMessaging/utils/ThreadUtil.h +++ b/SignalMessaging/utils/ThreadUtil.h @@ -47,7 +47,7 @@ NS_ASSUME_NONNULL_BEGIN + (TSOutgoingMessage *)enqueueMessageWithText:(NSString *)text inThread:(TSThread *)thread quotedReplyModel:(nullable OWSQuotedReplyModel *)quotedReplyModel - transaction:(YapDatabaseReadWriteTransaction *)transaction; + transaction:(YapDatabaseReadTransaction *)transaction; + (TSOutgoingMessage *)enqueueMessageWithAttachment:(SignalAttachment *)attachment inThread:(TSThread *)thread diff --git a/SignalMessaging/utils/ThreadUtil.m b/SignalMessaging/utils/ThreadUtil.m index fe65d0bb8..87bff957e 100644 --- a/SignalMessaging/utils/ThreadUtil.m +++ b/SignalMessaging/utils/ThreadUtil.m @@ -68,7 +68,7 @@ NS_ASSUME_NONNULL_BEGIN + (TSOutgoingMessage *)enqueueMessageWithText:(NSString *)text inThread:(TSThread *)thread quotedReplyModel:(nullable OWSQuotedReplyModel *)quotedReplyModel - transaction:(YapDatabaseReadWriteTransaction *)transaction + transaction:(YapDatabaseReadTransaction *)transaction { OWSDisappearingMessagesConfiguration *configuration = [OWSDisappearingMessagesConfiguration fetchObjectWithUniqueID:thread.uniqueId transaction:transaction]; @@ -82,9 +82,13 @@ NS_ASSUME_NONNULL_BEGIN expiresInSeconds:expiresInSeconds quotedMessage:[quotedReplyModel buildQuotedMessageForSending]]; - [message saveWithTransaction:transaction]; - - [self.messageSenderJobQueue addMessage:message transaction:transaction]; + [BenchManager benchWithTitle:@"Saving outgoing message" block:^{ + // To avoid blocking the send flow, we disapatch an async write from within this read transaction + [self.dbConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction * _Nonnull writeTransaction) { + [message saveWithTransaction:writeTransaction]; + [self.messageSenderJobQueue addMessage:message transaction:writeTransaction]; + }]; + }]; return message; } diff --git a/SignalServiceKit/src/Network/MessageSenderJobQueue.swift b/SignalServiceKit/src/Network/MessageSenderJobQueue.swift index ea121ca0c..6f93f05eb 100644 --- a/SignalServiceKit/src/Network/MessageSenderJobQueue.swift +++ b/SignalServiceKit/src/Network/MessageSenderJobQueue.swift @@ -38,6 +38,7 @@ public class MessageSenderJobQueue: NSObject, JobQueue { @objc(addMessage:transaction:) public func add(message: TSOutgoingMessage, transaction: YapDatabaseReadWriteTransaction) { + message.save(with: transaction) self.add(message: message, removeMessageAfterSending: false, transaction: transaction) }