Improve performance of marking chats read.

SQLite isn't always smart enough to use the best index for a query.
The main improvement here was to force it to use a better index than the
one it was using (which, on my device, happened to by the story index,
which was only minimally useful here).
main
Greyson Parrelli 2023-01-04 14:21:41 -05:00
rodzic a513e93d18
commit 59f05e0815
3 zmienionych plików z 29 dodań i 33 usunięć

Wyświetl plik

@ -993,17 +993,26 @@ public class ConversationListFragment extends MainFragment implements ActionMode
}
private void handleMarkAsRead(@NonNull Collection<Long> ids) {
Context context = requireContext();
Context context = requireContext();
Stopwatch stopwatch = new Stopwatch("mark-read");
SimpleTask.run(getViewLifecycleOwner().getLifecycle(), () -> {
stopwatch.split("task-start");
List<MarkedMessageInfo> messageIds = SignalDatabase.threads().setRead(ids, false);
stopwatch.split("db");
ApplicationDependencies.getMessageNotifier().updateNotification(context);
stopwatch.split("notification");
MarkReadReceiver.process(context, messageIds);
stopwatch.split("process");
return null;
}, none -> {
endActionModeIfActive();
stopwatch.stop(TAG);
});
}

Wyświetl plik

@ -232,12 +232,14 @@ public class MessageTable extends DatabaseTable implements MessageTypes, Recipie
EXPORT_STATE + " BLOB DEFAULT NULL, " +
EXPORTED + " INTEGER DEFAULT 0);";
private static final String INDEX_THREAD_DATE = "mms_thread_date_index";
public static final String[] CREATE_INDEXS = {
"CREATE INDEX IF NOT EXISTS mms_read_and_notified_and_thread_id_index ON " + TABLE_NAME + "(" + READ + "," + NOTIFIED + "," + THREAD_ID + ");",
"CREATE INDEX IF NOT EXISTS mms_type_index ON " + TABLE_NAME + " (" + TYPE + ");",
"CREATE INDEX IF NOT EXISTS mms_date_sent_index ON " + TABLE_NAME + " (" + DATE_SENT + ", " + RECIPIENT_ID + ", " + THREAD_ID + ");",
"CREATE INDEX IF NOT EXISTS mms_date_server_index ON " + TABLE_NAME + " (" + DATE_SERVER + ");",
"CREATE INDEX IF NOT EXISTS mms_thread_date_index ON " + TABLE_NAME + " (" + THREAD_ID + ", " + DATE_RECEIVED + ");",
"CREATE INDEX IF NOT EXISTS " + INDEX_THREAD_DATE + " ON " + TABLE_NAME + " (" + THREAD_ID + ", " + DATE_RECEIVED + ");",
"CREATE INDEX IF NOT EXISTS mms_reactions_unread_index ON " + TABLE_NAME + " (" + REACTIONS_UNREAD + ");",
"CREATE INDEX IF NOT EXISTS mms_story_type_index ON " + TABLE_NAME + " (" + STORY_TYPE + ");",
"CREATE INDEX IF NOT EXISTS mms_parent_story_id_index ON " + TABLE_NAME + " (" + PARENT_STORY_ID + ");",
@ -2020,7 +2022,7 @@ public class MessageTable extends DatabaseTable implements MessageTypes, Recipie
database.beginTransaction();
try {
cursor = database.query(TABLE_NAME, new String[] { ID, RECIPIENT_ID, DATE_SENT, TYPE, EXPIRES_IN, EXPIRE_STARTED, THREAD_ID, STORY_TYPE }, where, arguments, null, null, null);
cursor = database.query(TABLE_NAME + " INDEXED BY " + INDEX_THREAD_DATE, new String[] { ID, RECIPIENT_ID, DATE_SENT, TYPE, EXPIRES_IN, EXPIRE_STARTED, THREAD_ID, STORY_TYPE }, where, arguments, null, null, null);
while(cursor != null && cursor.moveToNext()) {
if (MessageTypes.isSecureType(CursorUtil.requireLong(cursor, TYPE))) {
@ -2045,7 +2047,7 @@ public class MessageTable extends DatabaseTable implements MessageTypes, Recipie
contentValues.put(REACTIONS_UNREAD, 0);
contentValues.put(REACTIONS_LAST_SEEN, System.currentTimeMillis());
database.update(TABLE_NAME, contentValues, where, arguments);
database.update(TABLE_NAME + " INDEXED BY " + INDEX_THREAD_DATE, contentValues, where, arguments);
database.setTransactionSuccessful();
} finally {
if (cursor != null) cursor.close();
@ -3913,7 +3915,7 @@ public class MessageTable extends DatabaseTable implements MessageTypes, Recipie
public int getUnreadCount(long threadId) {
String selection = READ + " = 0 AND " + STORY_TYPE + " = 0 AND " + THREAD_ID + " = " + threadId + " AND " + PARENT_STORY_ID + " <= 0";
try (Cursor cursor = getReadableDatabase().query(TABLE_NAME, COUNT, selection, null, null, null, null)) {
try (Cursor cursor = getReadableDatabase().query(TABLE_NAME + " INDEXED BY " + INDEX_THREAD_DATE, COUNT, selection, null, null, null, null)) {
if (cursor.moveToFirst()) {
return cursor.getInt(0);
} else {

Wyświetl plik

@ -10,6 +10,7 @@ import androidx.annotation.NonNull;
import com.annimon.stream.Collectors;
import com.annimon.stream.Stream;
import org.signal.core.util.Stopwatch;
import org.signal.core.util.concurrent.SignalExecutors;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.database.MessageTable.ExpirationInfo;
@ -72,21 +73,15 @@ public class MarkReadReceiver extends BroadcastReceiver {
public static void process(@NonNull Context context, @NonNull List<MarkedMessageInfo> markedReadMessages) {
if (markedReadMessages.isEmpty()) return;
List<SyncMessageId> syncMessageIds = Stream.of(markedReadMessages)
.map(MarkedMessageInfo::getSyncMessageId)
.toList();
List<ExpirationInfo> mmsExpirationInfo = Stream.of(markedReadMessages)
.map(MarkedMessageInfo::getExpirationInfo)
.filter(ExpirationInfo::isMms)
.filter(info -> info.getExpiresIn() > 0 && info.getExpireStarted() <= 0)
.toList();
List<ExpirationInfo> smsExpirationInfo = Stream.of(markedReadMessages)
.map(MarkedMessageInfo::getExpirationInfo)
.filterNot(ExpirationInfo::isMms)
.filter(info -> info.getExpiresIn() > 0 && info.getExpireStarted() <= 0)
.toList();
List<SyncMessageId> syncMessageIds = Stream.of(markedReadMessages)
.map(MarkedMessageInfo::getSyncMessageId)
.toList();
List<ExpirationInfo> expirationInfo = Stream.of(markedReadMessages)
.map(MarkedMessageInfo::getExpirationInfo)
.filter(info -> info.getExpiresIn() > 0 && info.getExpireStarted() <= 0)
.toList();
scheduleDeletion(context, smsExpirationInfo, mmsExpirationInfo);
scheduleDeletion(expirationInfo);
MultiDeviceReadUpdateJob.enqueue(syncMessageIds);
@ -108,23 +103,13 @@ public class MarkReadReceiver extends BroadcastReceiver {
});
}
private static void scheduleDeletion(@NonNull Context context,
@NonNull List<ExpirationInfo> smsExpirationInfo,
@NonNull List<ExpirationInfo> mmsExpirationInfo)
{
if (smsExpirationInfo.size() > 0) {
SignalDatabase.messages().markExpireStarted(Stream.of(smsExpirationInfo).map(ExpirationInfo::getId).toList(), System.currentTimeMillis());
}
private static void scheduleDeletion(@NonNull List<ExpirationInfo> expirationInfo) {
if (expirationInfo.size() > 0) {
SignalDatabase.messages().markExpireStarted(Stream.of(expirationInfo).map(ExpirationInfo::getId).toList(), System.currentTimeMillis());
if (mmsExpirationInfo.size() > 0) {
SignalDatabase.messages().markExpireStarted(Stream.of(mmsExpirationInfo).map(ExpirationInfo::getId).toList(), System.currentTimeMillis());
}
if (smsExpirationInfo.size() + mmsExpirationInfo.size() > 0) {
ExpiringMessageManager expirationManager = ApplicationDependencies.getExpiringMessageManager();
Stream.concat(Stream.of(smsExpirationInfo), Stream.of(mmsExpirationInfo))
.forEach(info -> expirationManager.scheduleDeletion(info.getId(), info.isMms(), info.getExpiresIn()));
expirationInfo.stream().forEach(info -> expirationManager.scheduleDeletion(info.getId(), info.isMms(), info.getExpiresIn()));
}
}
}