Fix incorrect transaction batching during conversation delete.

This commit is contained in:
Cody Henthorne
2026-03-03 09:46:39 -05:00
committed by Greyson Parrelli
parent 7fbcd17759
commit e23d575460
8 changed files with 304 additions and 60 deletions

View File

@@ -47,4 +47,10 @@ object BenchmarkMetrics {
get() = listOf(
TraceSectionMetric("ReceiptMessageProcessor#incrementReadReceiptCounts", Mode.Average)
)
val threadDeletion: List<TraceSectionMetric>
get() = listOf(
TraceSectionMetric("ThreadTable#deleteConversations", Mode.Sum),
TraceSectionMetric("MessageTable#deleteMessagesInThread", Mode.Sum)
)
}

View File

@@ -8,10 +8,10 @@ object BenchmarkSetup {
private const val TARGET_PACKAGE = "org.thoughtcrime.securesms.benchmark"
private const val RECEIVER = "org.signal.benchmark.BenchmarkCommandReceiver"
fun setup(type: String, device: UiDevice) {
fun setup(type: String, device: UiDevice, timeout: Long = 25_000L) {
device.executeShellCommand("pm clear $TARGET_PACKAGE")
device.executeShellCommand("am start -W -n $TARGET_PACKAGE/org.signal.benchmark.BenchmarkSetupActivity --es setup-type $type")
device.wait(Until.hasObject(By.textContains("done")), 25_000L)
device.wait(Until.hasObject(By.textContains("done")), timeout)
}
fun setupIndividualSend(device: UiDevice) {
@@ -34,6 +34,10 @@ object BenchmarkSetup {
device.benchmarkCommandBroadcast("release-messages")
}
fun deleteThread(device: UiDevice) {
device.benchmarkCommandBroadcast("delete-thread")
}
private fun UiDevice.benchmarkCommandBroadcast(command: String) {
executeShellCommand("am broadcast -a org.signal.benchmark.action.COMMAND -e command $command -n $TARGET_PACKAGE/$RECEIVER")
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright 2026 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.benchmark
import androidx.annotation.RequiresApi
import androidx.benchmark.macro.CompilationMode
import androidx.benchmark.macro.ExperimentalMetricApi
import androidx.benchmark.macro.junit4.MacrobenchmarkRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
/**
* Macrobenchmark for measuring thread deletion performance.
*
* Inserts 20,000 messages into a conversation, then measures the time
* to delete the entire thread using per-batch transactions.
*
* Two variants:
* - [deleteThread20kMessages]: 1:1 conversation with attachments and reactions
* - [deleteGroupThread20kMessages]: Group conversation with attachments, reactions,
* group receipts (5 members x 20k = 100k rows), and mentions
*/
@OptIn(ExperimentalMetricApi::class)
@RunWith(AndroidJUnit4::class)
@RequiresApi(31)
class ThreadDeletionBenchmarks {
@get:Rule
val benchmarkRule = MacrobenchmarkRule()
@Test
fun deleteThread20kMessages() {
benchmarkRule.measureRepeated(
packageName = "org.thoughtcrime.securesms.benchmark",
metrics = BenchmarkMetrics.threadDeletion,
iterations = 1,
compilationMode = CompilationMode.Partial(),
setupBlock = {
BenchmarkSetup.setup("thread-delete", device, timeout = 120_000L)
killProcess()
startActivityAndWait()
device.waitForIdle()
device.wait(Until.findObject(By.textContains("Buddy")), 10_000)
}
) {
BenchmarkSetup.deleteThread(device)
device.wait(Until.gone(By.textContains("Buddy")), 300_000L)
}
}
@Test
fun deleteGroupThread20kMessages() {
benchmarkRule.measureRepeated(
packageName = "org.thoughtcrime.securesms.benchmark",
metrics = BenchmarkMetrics.threadDeletion,
iterations = 1,
compilationMode = CompilationMode.Partial(),
setupBlock = {
BenchmarkSetup.setup("thread-delete-group", device, timeout = 180_000L)
killProcess()
startActivityAndWait()
device.waitForIdle()
device.wait(Until.findObject(By.textContains("Title")), 10_000)
}
) {
BenchmarkSetup.deleteThread(device)
device.wait(Until.gone(By.textContains("Title")), 300_000L)
}
}
}