From 46dd7f8a06a0c63b61a727eebd6bd41fa8a604b0 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Tue, 21 Dec 2021 14:47:53 -0500 Subject: [PATCH] Improve performance of processing read syncs. --- .../securesms/database/MessageDatabase.java | 2 +- .../securesms/database/MmsDatabase.java | 26 ++++---- .../securesms/database/MmsSmsDatabase.java | 64 +++++++++++++++++++ .../securesms/database/SmsDatabase.java | 27 ++++---- .../messages/MessageContentProcessor.java | 24 ++----- 5 files changed, 91 insertions(+), 52 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MessageDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/MessageDatabase.java index 6fc3edd28..67269a4ca 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MessageDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MessageDatabase.java @@ -121,7 +121,7 @@ public abstract class MessageDatabase extends Database implements MmsSmsColumns public abstract void markIncomingNotificationReceived(long threadId); public abstract Set incrementReceiptCount(SyncMessageId messageId, long timestamp, @NonNull ReceiptType receiptType); - public abstract List> setTimestampRead(SyncMessageId messageId, long proposedExpireStarted, @NonNull Map threadToLatestRead); + abstract @NonNull MmsSmsDatabase.TimestampReadResult setTimestampRead(SyncMessageId messageId, long proposedExpireStarted, @NonNull Map threadToLatestRead); public abstract List setEntireThreadRead(long threadId); public abstract List setMessagesReadSince(long threadId, long timestamp); public abstract List setAllMessagesRead(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java index f0762350f..0cf1c7a01 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java @@ -1000,14 +1000,15 @@ public class MmsDatabase extends MessageDatabase { } @Override - public List> setTimestampRead(SyncMessageId messageId, long proposedExpireStarted, @NonNull Map threadToLatestRead) { - SQLiteDatabase database = databaseHelper.getSignalWritableDatabase(); - List> expiring = new LinkedList<>(); - Cursor cursor = null; - - try { - cursor = database.query(TABLE_NAME, new String[] {ID, THREAD_ID, MESSAGE_BOX, EXPIRES_IN, EXPIRE_STARTED, RECIPIENT_ID}, DATE_SENT + " = ?", new String[] {String.valueOf(messageId.getTimetamp())}, null, null, null, null); + @NonNull MmsSmsDatabase.TimestampReadResult setTimestampRead(SyncMessageId messageId, long proposedExpireStarted, @NonNull Map threadToLatestRead) { + SQLiteDatabase database = databaseHelper.getSignalWritableDatabase(); + List> expiring = new LinkedList<>(); + String[] projection = new String[] { ID, THREAD_ID, MESSAGE_BOX, EXPIRES_IN, EXPIRE_STARTED, RECIPIENT_ID }; + String query = DATE_SENT + " = ?"; + String[] args = SqlUtil.buildArgs(messageId.getTimetamp()); + List threads = new LinkedList<>(); + try (Cursor cursor = database.query(TABLE_NAME, projection, query, args, null, null, null)) { while (cursor.moveToNext()) { RecipientId theirRecipientId = RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(RECIPIENT_ID))); RecipientId ourRecipientId = messageId.getRecipientId(); @@ -1030,22 +1031,17 @@ public class MmsDatabase extends MessageDatabase { expiring.add(new Pair<>(id, expiresIn)); } - database.update(TABLE_NAME, values, ID_WHERE, new String[]{String.valueOf(id)}); + database.update(TABLE_NAME, values, ID_WHERE, SqlUtil.buildArgs(id)); - SignalDatabase.threads().updateReadState(threadId); - SignalDatabase.threads().setLastSeen(threadId); - notifyConversationListeners(threadId); + threads.add(threadId); Long latest = threadToLatestRead.get(threadId); threadToLatestRead.put(threadId, (latest != null) ? Math.max(latest, messageId.getTimetamp()) : messageId.getTimetamp()); } } - } finally { - if (cursor != null) - cursor.close(); } - return expiring; + return new MmsSmsDatabase.TimestampReadResult(expiring, threads); } @Override diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsDatabase.java index 2a36ee286..bc8a59e56 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsDatabase.java @@ -31,19 +31,23 @@ import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.database.MessageDatabase.SyncMessageId; import org.thoughtcrime.securesms.database.MessageDatabase.ThreadUpdate; import org.thoughtcrime.securesms.database.model.MessageRecord; +import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.notifications.v2.MessageNotifierV2; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.util.CursorUtil; import org.thoughtcrime.securesms.util.SqlUtil; import org.whispersystems.libsignal.util.Pair; +import org.whispersystems.signalservice.api.messages.multidevice.ReadMessage; import java.io.Closeable; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; +import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.stream.Collectors; @@ -489,6 +493,56 @@ public class MmsSmsDatabase extends Database { return threadUpdates; } + + public void setTimestampRead(@NonNull Recipient senderRecipient, @NonNull List readMessages, long proposedExpireStarted, @NonNull Map threadToLatestRead) { + SQLiteDatabase db = getWritableDatabase(); + + List> expiringText = new LinkedList<>(); + List> expiringMedia = new LinkedList<>(); + Set updatedThreads = new HashSet<>(); + + db.beginTransaction(); + try { + for (ReadMessage readMessage : readMessages) { + TimestampReadResult textResult = SignalDatabase.sms().setTimestampRead(new SyncMessageId(senderRecipient.getId(), readMessage.getTimestamp()), + proposedExpireStarted, + threadToLatestRead); + TimestampReadResult mediaResult = SignalDatabase.mms().setTimestampRead(new SyncMessageId(senderRecipient.getId(), readMessage.getTimestamp()), + proposedExpireStarted, + threadToLatestRead); + + expiringText.addAll(textResult.expiring); + expiringMedia.addAll(mediaResult.expiring); + + updatedThreads.addAll(textResult.threads); + updatedThreads.addAll(mediaResult.threads); + } + + for (long threadId : updatedThreads) { + SignalDatabase.threads().updateReadState(threadId); + SignalDatabase.threads().setLastSeen(threadId); + } + + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + + for (Pair expiringMessage : expiringText) { + ApplicationDependencies.getExpiringMessageManager() + .scheduleDeletion(expiringMessage.first(), false, proposedExpireStarted, expiringMessage.second()); + } + + for (Pair expiringMessage : expiringMedia) { + ApplicationDependencies.getExpiringMessageManager() + .scheduleDeletion(expiringMessage.first(), true, proposedExpireStarted, expiringMessage.second()); + } + + for (long threadId : updatedThreads) { + notifyConversationListeners(threadId); + } + } + public int getQuotedMessagePosition(long threadId, long quoteId, @NonNull RecipientId recipientId) { String order = MmsSmsColumns.NORMALIZED_DATE_RECEIVED + " DESC"; String selection = MmsSmsColumns.THREAD_ID + " = " + threadId; @@ -862,4 +916,14 @@ public class MmsSmsDatabase extends Database { cursor.close(); } } + + static final class TimestampReadResult { + final List> expiring; + final List threads; + + TimestampReadResult(@NonNull List> expiring, @NonNull List threads) { + this.expiring = expiring; + this.threads = threads; + } + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java index f447a1435..3326f9415 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java @@ -526,16 +526,15 @@ public class SmsDatabase extends MessageDatabase { } @Override - public List> setTimestampRead(SyncMessageId messageId, long proposedExpireStarted, @NonNull Map threadToLatestRead) { - SQLiteDatabase database = databaseHelper.getSignalWritableDatabase(); - List> expiring = new LinkedList<>(); - Cursor cursor = null; - - try { - cursor = database.query(TABLE_NAME, new String[] {ID, THREAD_ID, RECIPIENT_ID, TYPE, EXPIRES_IN, EXPIRE_STARTED}, - DATE_SENT + " = ?", new String[] {String.valueOf(messageId.getTimetamp())}, - null, null, null, null); + @NonNull MmsSmsDatabase.TimestampReadResult setTimestampRead(SyncMessageId messageId, long proposedExpireStarted, @NonNull Map threadToLatestRead) { + SQLiteDatabase database = databaseHelper.getSignalWritableDatabase(); + List> expiring = new LinkedList<>(); + String[] projection = new String[] {ID, THREAD_ID, RECIPIENT_ID, TYPE, EXPIRES_IN, EXPIRE_STARTED}; + String query = DATE_SENT + " = ?"; + String[] args = SqlUtil.buildArgs(messageId.getTimetamp()); + List threads = new LinkedList<>(); + try (Cursor cursor = database.query(TABLE_NAME, projection, query, args, null, null, null)) { while (cursor.moveToNext()) { RecipientId theirRecipientId = messageId.getRecipientId(); RecipientId outRecipientId = RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(RECIPIENT_ID))); @@ -558,21 +557,17 @@ public class SmsDatabase extends MessageDatabase { expiring.add(new Pair<>(id, expiresIn)); } - database.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {cursor.getLong(cursor.getColumnIndexOrThrow(ID)) + ""}); + database.update(TABLE_NAME, contentValues, ID_WHERE, SqlUtil.buildArgs(id)); - SignalDatabase.threads().updateReadState(threadId); - SignalDatabase.threads().setLastSeen(threadId); - notifyConversationListeners(threadId); + threads.add(threadId); Long latest = threadToLatestRead.get(threadId); threadToLatestRead.put(threadId, (latest != null) ? Math.max(latest, messageId.getTimetamp()) : messageId.getTimetamp()); } } - } finally { - if (cursor != null) cursor.close(); } - return expiring; + return new MmsSmsDatabase.TimestampReadResult(expiring, threads); } @Override diff --git a/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessor.java index 3c40b1361..330add10c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessor.java @@ -121,7 +121,6 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.Util; import org.whispersystems.libsignal.SignalProtocolAddress; import org.whispersystems.libsignal.protocol.DecryptionErrorMessage; -import org.whispersystems.libsignal.util.Pair; import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; import org.whispersystems.signalservice.api.messages.SignalServiceContent; @@ -1199,29 +1198,14 @@ public final class MessageContentProcessor { private void handleSynchronizeReadMessage(@NonNull List readMessages, long envelopeTimestamp, @NonNull Recipient senderRecipient) { - log(envelopeTimestamp, "Synchronize read message."); + log(envelopeTimestamp, "Synchronize read message. Count: " + readMessages.size()); Map threadToLatestRead = new HashMap<>(); - for (ReadMessage readMessage : readMessages) { - List> expiringText = SignalDatabase.sms().setTimestampRead(new SyncMessageId(senderRecipient.getId(), readMessage.getTimestamp()), - envelopeTimestamp, - threadToLatestRead); - List> expiringMedia = SignalDatabase.mms().setTimestampRead(new SyncMessageId(senderRecipient.getId(), readMessage.getTimestamp()), - envelopeTimestamp, - threadToLatestRead); - for (Pair expiringMessage : expiringText) { - ApplicationDependencies.getExpiringMessageManager() - .scheduleDeletion(expiringMessage.first(), false, envelopeTimestamp, expiringMessage.second()); - } - - for (Pair expiringMessage : expiringMedia) { - ApplicationDependencies.getExpiringMessageManager() - .scheduleDeletion(expiringMessage.first(), true, envelopeTimestamp, expiringMessage.second()); - } - } + SignalDatabase.mmsSms().setTimestampRead(senderRecipient, readMessages, envelopeTimestamp, threadToLatestRead); List markedMessages = SignalDatabase.threads().setReadSince(threadToLatestRead, false); + if (Util.hasItems(markedMessages)) { Log.i(TAG, "Updating past messages: " + markedMessages.size()); MarkReadReceiver.process(context, markedMessages); @@ -1234,7 +1218,7 @@ public final class MessageContentProcessor { } private void handleSynchronizeViewedMessage(@NonNull List viewedMessages, long envelopeTimestamp) { - log(envelopeTimestamp, "Synchronize viewed message."); + log(envelopeTimestamp, "Synchronize viewed message. Count: " + viewedMessages.size()); List toMarkViewed = Stream.of(viewedMessages) .map(message -> {