diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/InternalConversationSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/InternalConversationSettingsFragment.kt index 50484c24ed..e3bf36faff 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/InternalConversationSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/InternalConversationSettingsFragment.kt @@ -11,6 +11,7 @@ import org.signal.core.util.Base64 import org.signal.core.util.Hex import org.signal.core.util.concurrent.SignalExecutors import org.signal.core.util.isAbsent +import org.signal.core.util.roundedString import org.signal.libsignal.zkgroup.profiles.ProfileKey import org.thoughtcrime.securesms.MainActivity import org.thoughtcrime.securesms.R @@ -33,6 +34,8 @@ import org.thoughtcrime.securesms.util.Util import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter import org.thoughtcrime.securesms.util.livedata.Store import java.util.Objects +import kotlin.time.Duration.Companion.nanoseconds +import kotlin.time.DurationUnit /** * Shows internal details about a recipient that you can view from the conversation settings. @@ -151,6 +154,17 @@ class InternalConversationSettingsFragment : DSLSettingsFragment( ) } + clickPref( + title = DSLSettingsText.from("Trigger Thread Update"), + summary = DSLSettingsText.from("Triggers a thread update. Useful for testing perf."), + onClick = { + val startTimeNanos = System.nanoTime() + SignalDatabase.threads.update(state.threadId ?: -1, true) + val endTimeNanos = System.nanoTime() + Toast.makeText(context, "Thread update took ${(endTimeNanos - startTimeNanos).nanoseconds.toDouble(DurationUnit.MILLISECONDS).roundedString(2)} ms", Toast.LENGTH_SHORT).show() + } + ) + if (!recipient.isGroup) { sectionHeaderPref(DSLSettingsText.from("Actions")) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.kt index 895469d33e..591158676b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.kt @@ -269,6 +269,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat private const val INDEX_THREAD_STORY_SCHEDULED_DATE_LATEST_REVISION_ID = "message_thread_story_parent_story_scheduled_date_latest_revision_id_index" private const val INDEX_DATE_SENT_FROM_TO_THREAD = "message_date_sent_from_to_thread_index" private const val INDEX_THREAD_COUNT = "message_thread_count_index" + private const val INDEX_THREAD_UNREAD_COUNT = "message_thread_unread_count_index" @JvmField val CREATE_INDEXS = arrayOf( @@ -289,7 +290,9 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat "CREATE INDEX IF NOT EXISTS message_to_recipient_id_index ON $TABLE_NAME ($TO_RECIPIENT_ID)", "CREATE UNIQUE INDEX IF NOT EXISTS message_unique_sent_from_thread ON $TABLE_NAME ($DATE_SENT, $FROM_RECIPIENT_ID, $THREAD_ID)", // This index is created specifically for getting the number of messages in a thread and therefore needs to be kept in sync with that query - "CREATE INDEX IF NOT EXISTS $INDEX_THREAD_COUNT ON $TABLE_NAME ($THREAD_ID) WHERE $STORY_TYPE = 0 AND $PARENT_STORY_ID <= 0 AND $SCHEDULED_DATE = -1 AND $LATEST_REVISION_ID IS NULL" + "CREATE INDEX IF NOT EXISTS $INDEX_THREAD_COUNT ON $TABLE_NAME ($THREAD_ID) WHERE $STORY_TYPE = 0 AND $PARENT_STORY_ID <= 0 AND $SCHEDULED_DATE = -1 AND $LATEST_REVISION_ID IS NULL", + // This index is created specifically for getting the number of unread messages in a thread and therefore needs to be kept in sync with that query + "CREATE INDEX IF NOT EXISTS $INDEX_THREAD_UNREAD_COUNT ON $TABLE_NAME ($THREAD_ID) WHERE $STORY_TYPE = 0 AND $PARENT_STORY_ID <= 0 AND $SCHEDULED_DATE = -1 AND $LATEST_REVISION_ID IS NULL AND $READ = 0" ) private val MMS_PROJECTION_BASE = arrayOf( @@ -2274,8 +2277,8 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat fun getOldestUnreadMentionDetails(threadId: Long): Pair? { return readableDatabase .select(FROM_RECIPIENT_ID, DATE_RECEIVED) - .from("$TABLE_NAME INDEXED BY $INDEX_THREAD_STORY_SCHEDULED_DATE_LATEST_REVISION_ID") - .where("$THREAD_ID = ? AND $STORY_TYPE = 0 AND $PARENT_STORY_ID <= 0 AND $LATEST_REVISION_ID IS NULL AND $READ = 0 AND $MENTIONS_SELF = 1", threadId) + .from("$TABLE_NAME INDEXED BY $INDEX_THREAD_UNREAD_COUNT") + .where("$THREAD_ID = ? AND $STORY_TYPE = 0 AND $PARENT_STORY_ID <= 0 AND $LATEST_REVISION_ID IS NULL AND $SCHEDULED_DATE = -1 AND $READ = 0 AND $MENTIONS_SELF = 1", threadId) .orderBy("$DATE_RECEIVED ASC") .limit(1) .run() @@ -2290,8 +2293,8 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat fun getUnreadMentionCount(threadId: Long): Int { return readableDatabase .count() - .from("$TABLE_NAME INDEXED BY $INDEX_THREAD_STORY_SCHEDULED_DATE_LATEST_REVISION_ID") - .where("$THREAD_ID = ? AND $STORY_TYPE = 0 AND $PARENT_STORY_ID <= 0 AND $LATEST_REVISION_ID IS NULL AND $READ = 0 AND $MENTIONS_SELF = 1", threadId) + .from("$TABLE_NAME INDEXED BY $INDEX_THREAD_UNREAD_COUNT") + .where("$THREAD_ID = ? AND $STORY_TYPE = 0 AND $PARENT_STORY_ID <= 0 AND $LATEST_REVISION_ID IS NULL AND $SCHEDULED_DATE = -1 AND $READ = 0 AND $MENTIONS_SELF = 1", threadId) .run() .readToSingleInt() } @@ -4053,8 +4056,8 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat fun getUnreadCount(threadId: Long): Int { return readableDatabase .select("COUNT(*)") - .from("$TABLE_NAME INDEXED BY $INDEX_THREAD_STORY_SCHEDULED_DATE_LATEST_REVISION_ID") - .where("$READ = 0 AND $STORY_TYPE = 0 AND $THREAD_ID = $threadId AND $PARENT_STORY_ID <= 0 AND $LATEST_REVISION_ID IS NULL") + .from("$TABLE_NAME INDEXED BY $INDEX_THREAD_UNREAD_COUNT") + .where("$THREAD_ID = $threadId AND $STORY_TYPE = 0 AND $PARENT_STORY_ID <= 0 AND $LATEST_REVISION_ID IS NULL AND $SCHEDULED_DATE = -1 AND $READ = 0") .run() .readToSingleInt() } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt index 27ba81ff5d..8dd4929fac 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt @@ -87,6 +87,7 @@ import org.thoughtcrime.securesms.database.helpers.migration.V226_AddAttachmentM import org.thoughtcrime.securesms.database.helpers.migration.V227_AddAttachmentArchiveTransferState import org.thoughtcrime.securesms.database.helpers.migration.V228_AddNameCollisionTables import org.thoughtcrime.securesms.database.helpers.migration.V229_MarkMissedCallEventsNotified +import org.thoughtcrime.securesms.database.helpers.migration.V230_UnreadCountIndices /** * Contains all of the database migrations for [SignalDatabase]. Broken into a separate file for cleanliness. @@ -176,10 +177,11 @@ object SignalDatabaseMigrations { 226 to V226_AddAttachmentMediaIdIndex, 227 to V227_AddAttachmentArchiveTransferState, 228 to V228_AddNameCollisionTables, - 229 to V229_MarkMissedCallEventsNotified + 229 to V229_MarkMissedCallEventsNotified, + 230 to V230_UnreadCountIndices ) - const val DATABASE_VERSION = 229 + const val DATABASE_VERSION = 230 @JvmStatic fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V230_UnreadCountIndices.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V230_UnreadCountIndices.kt new file mode 100644 index 0000000000..6200d28b71 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V230_UnreadCountIndices.kt @@ -0,0 +1,19 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.database.helpers.migration + +import android.app.Application +import net.zetetic.database.sqlcipher.SQLiteDatabase + +/** + * Adds a partial index for some performance-critical queries around unread counts. + */ +@Suppress("ClassName") +object V230_UnreadCountIndices : SignalDatabaseMigration { + override fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { + db.execSQL("CREATE INDEX IF NOT EXISTS message_thread_unread_count_index ON message (thread_id) WHERE story_type = 0 AND parent_story_id <= 0 AND scheduled_date = -1 AND latest_revision_id IS NULL AND read = 0") + } +}