Improve perf for backup info in debuglogs.

This commit is contained in:
Greyson Parrelli
2026-05-19 14:38:55 -04:00
committed by jeffrey-signal
parent 1661f3b5f7
commit 5941ff814d
8 changed files with 93 additions and 33 deletions
@@ -3557,38 +3557,41 @@ class AttachmentTable(
)
.readToSingleLong(0)
val archiveStatusMediaNameCounts: Map<ArchiveTransferState, Long> = ArchiveTransferState.entries.associateWith { state ->
val archiveStatusMediaNameCounts: Map<ArchiveTransferState, Long> = ArchiveTransferState.entries.associateWith { 0L }.toMutableMap().apply {
readableDatabase.query(
"""
SELECT COUNT(*) FROM (
SELECT DISTINCT $DATA_HASH_END, $REMOTE_KEY
SELECT $ARCHIVE_TRANSFER_STATE, COUNT(*) FROM (
SELECT DISTINCT $DATA_HASH_END, $REMOTE_KEY, $ARCHIVE_TRANSFER_STATE
FROM $TABLE_NAME LEFT JOIN ${MessageTable.TABLE_NAME} ON $TABLE_NAME.$MESSAGE_ID = ${MessageTable.TABLE_NAME}.${MessageTable.ID}
WHERE ${buildAttachmentsThatCanArchiveQuery(archiveTransferStateFilter = "$ARCHIVE_TRANSFER_STATE = ${state.value}")}
)
WHERE ${buildAttachmentsThatCanArchiveQuery(archiveTransferStateFilter = "1=1")}
) GROUP BY $ARCHIVE_TRANSFER_STATE
"""
)
.readToSingleLong(0)
).forEach { cursor ->
this[ArchiveTransferState.deserialize(cursor.getInt(0))] = cursor.getLong(1)
}
}
val uniqueEligibleMediaNamesWithThumbnailsCount =
readableDatabase.query("SELECT COUNT(*) FROM (SELECT DISTINCT $DATA_HASH_END, $REMOTE_KEY FROM $TABLE_NAME WHERE $DATA_HASH_END NOT NULL AND $REMOTE_KEY NOT NULL AND $THUMBNAIL_FILE NOT NULL AND $QUOTE = 0 AND $MESSAGE_ID != $WALLPAPER_MESSAGE_ID)")
.readToSingleLong(-1L)
val archiveStatusMediaNameThumbnailCounts: Map<ArchiveTransferState, Long> = ArchiveTransferState.entries.associateWith { state ->
val archiveStatusMediaNameThumbnailCounts: Map<ArchiveTransferState, Long> = ArchiveTransferState.entries.associateWith { 0L }.toMutableMap().apply {
readableDatabase.query(
"""
SELECT COUNT(*) FROM (
SELECT DISTINCT $DATA_HASH_END, $REMOTE_KEY
SELECT $ARCHIVE_THUMBNAIL_TRANSFER_STATE, COUNT(*) FROM (
SELECT DISTINCT $DATA_HASH_END, $REMOTE_KEY, $ARCHIVE_THUMBNAIL_TRANSFER_STATE
FROM $TABLE_NAME LEFT JOIN ${MessageTable.TABLE_NAME} ON $TABLE_NAME.$MESSAGE_ID = ${MessageTable.TABLE_NAME}.${MessageTable.ID}
WHERE
${buildAttachmentsThatCanArchiveQuery("$ARCHIVE_THUMBNAIL_TRANSFER_STATE = ${state.value}")} AND
WHERE
${buildAttachmentsThatCanArchiveQuery("1=1")} AND
$QUOTE = 0 AND
($CONTENT_TYPE LIKE 'image/%' OR $CONTENT_TYPE LIKE 'video/%') AND
$CONTENT_TYPE != 'image/svg+xml' AND
$MESSAGE_ID != $WALLPAPER_MESSAGE_ID
)
) GROUP BY $ARCHIVE_THUMBNAIL_TRANSFER_STATE
"""
)
.readToSingleLong(0)
).forEach { cursor ->
this[ArchiveTransferState.deserialize(cursor.getInt(0))] = cursor.getLong(1)
}
}
val pendingAttachmentUploadBytes = getPendingArchiveUploadBytes()
@@ -3599,9 +3602,9 @@ class AttachmentTable(
FROM (
SELECT DISTINCT $DATA_HASH_END, $REMOTE_KEY, $DATA_SIZE
FROM $TABLE_NAME
WHERE
$DATA_FILE NOT NULL AND
$DATA_HASH_END NOT NULL AND
WHERE
$DATA_FILE NOT NULL AND
$DATA_HASH_END NOT NULL AND
$REMOTE_KEY NOT NULL AND
$ARCHIVE_TRANSFER_STATE = ${ArchiveTransferState.FINISHED.value}
)
@@ -33,6 +33,11 @@ final class LogSectionBadges implements LogSection {
InAppPaymentTable.InAppPayment latestRecurringDonation = SignalDatabase.inAppPayments().getLatestInAppPaymentByType(InAppPaymentType.RECURRING_DONATION);
if (latestRecurringDonation != null) {
InAppPaymentSubscriberRecord donationSubscriber = InAppPaymentsRepository.getSubscriber(InAppPaymentSubscriberRecord.Type.DONATION);
boolean shouldCancel = donationSubscriber != null
? donationSubscriber.getRequiresCancel()
: SignalStore.inAppPayments().getShouldCancelSubscriptionBeforeNextSubscribeAttempt();
return new StringBuilder().append("Badge Count : ").append(Recipient.self().getBadges().size()).append("\n")
.append("ExpiredBadge : ").append(SignalStore.inAppPayments().getExpiredBadge() != null).append("\n")
.append("LastKeepAliveLaunchTime : ").append(SignalStore.inAppPayments().getLastKeepAliveLaunchTime()).append("\n")
@@ -44,7 +49,7 @@ final class LogSectionBadges implements LogSection {
.append("InAppPaymentData.Error : ").append(getError(latestRecurringDonation.getData())).append("\n")
.append("InAppPaymentData.Cancellation : ").append(getCancellation(latestRecurringDonation.getData())).append("\n")
.append("DisplayBadgesOnProfile : ").append(SignalStore.inAppPayments().getDisplayBadgesOnProfile()).append("\n")
.append("ShouldCancelBeforeNextAttempt : ").append(InAppPaymentsRepository.getShouldCancelSubscriptionBeforeNextSubscribeAttempt(InAppPaymentSubscriberRecord.Type.DONATION)).append("\n")
.append("ShouldCancelBeforeNextAttempt : ").append(shouldCancel).append("\n")
.append("IsUserManuallyCancelledDonation : ").append(SignalStore.inAppPayments().isDonationSubscriptionManuallyCancelled()).append("\n");
} else {
@@ -105,7 +105,12 @@ class LogSectionRemoteBackups : LogSection {
}
output.append("\n -- Attachment Stats\n")
output.append(SignalDatabase.attachments.debugGetAttachmentStats().prettyString())
val backupInProgress = SignalStore.backup.archiveUploadState?.state?.let { it != ArchiveUploadProgressState.State.None && it != ArchiveUploadProgressState.State.UserCanceled } ?: false
if (SignalStore.backup.hasBackupCreationError || backupInProgress) {
output.append(SignalDatabase.attachments.debugGetAttachmentStats().prettyString())
} else {
output.append("Skipped (last backup succeeded and no upload in progress)\n")
}
return output
}
@@ -354,6 +354,16 @@ public class SubmitDebugLogActivity extends BaseActivity {
private void initViewModel() {
viewModel.getMode().observe(this, this::presentMode);
viewModel.getEvents().observe(this, this::presentEvents);
viewModel.getSlowPrefixWarning().observe(this, this::presentSlowPrefixWarning);
}
private void presentSlowPrefixWarning(@NonNull Long durationMillis) {
int durationSeconds = (int) Math.round(durationMillis / 1000.0);
new MaterialAlertDialogBuilder(this)
.setTitle(R.string.SubmitDebugLogActivity_slow_log_title)
.setMessage(getString(R.string.SubmitDebugLogActivity_slow_log_message, durationSeconds))
.setPositiveButton(android.R.string.ok, null)
.show();
}
private void subscribeToLogLines() {
@@ -373,18 +373,28 @@ public class SubmitDebugLogRepository {
int maxTitleLength = SECTIONS.stream().reduce(0, (max, section) -> Math.max(max, section.getTitle().length()), Integer::sum);
List<LogLine> allLines = new ArrayList<>();
List<Future<List<LogLine>>> futures = new ArrayList<>(SECTIONS.size());
for (LogSection section : SECTIONS) {
List<LogLine> lines = getLinesForSection(context, section, maxTitleLength);
futures.add(SignalExecutors.BOUNDED.submit(() -> getLinesForSection(context, section, maxTitleLength)));
}
if (SECTIONS.indexOf(section) != SECTIONS.size() - 1) {
for (int i = 0; i < SECTION_SPACING; i++) {
lines.add(SimpleLogLine.EMPTY);
}
List<LogLine> allLines = new ArrayList<>();
for (int i = 0; i < futures.size(); i++) {
List<LogLine> lines;
try {
lines = futures.get(i).get();
} catch (InterruptedException | ExecutionException e) {
Log.w(TAG, "Failed to read section " + SECTIONS.get(i).getTitle(), e);
lines = new ArrayList<>();
}
allLines.addAll(lines);
if (i != futures.size() - 1) {
for (int j = 0; j < SECTION_SPACING; j++) {
allLines.add(SimpleLogLine.EMPTY);
}
}
}
List<LogLine> withIds = new ArrayList<>(allLines.size());
@@ -15,6 +15,7 @@ import org.signal.core.util.tracing.Tracer;
import org.signal.debuglogsviewer.DebugLogsViewer;
import org.thoughtcrime.securesms.database.LogDatabase;
import org.thoughtcrime.securesms.dependencies.AppDependencies;
import org.thoughtcrime.securesms.util.RemoteConfig;
import org.thoughtcrime.securesms.util.SingleLiveEvent;
import java.util.ArrayList;
@@ -28,20 +29,23 @@ public class SubmitDebugLogViewModel extends ViewModel {
private static final String TAG = Log.tag(SubmitDebugLogViewModel.class);
private static final int CHUNK_SIZE = 10_000;
private static final int CHUNK_SIZE = 10_000;
private static final long SLOW_PREFIX_THRESHOLD_MILLIS = 3_000L;
private final SubmitDebugLogRepository repo;
private final MutableLiveData<Mode> mode;
private final SingleLiveEvent<Event> event;
private final SingleLiveEvent<Long> slowPrefixWarning;
private final long firstViewTime;
private final byte[] trace;
private SubmitDebugLogViewModel() {
this.repo = new SubmitDebugLogRepository();
this.mode = new MutableLiveData<>();
this.trace = Tracer.getInstance().serialize();
this.firstViewTime = System.currentTimeMillis();
this.event = new SingleLiveEvent<>();
this.repo = new SubmitDebugLogRepository();
this.mode = new MutableLiveData<>();
this.trace = Tracer.getInstance().serialize();
this.firstViewTime = System.currentTimeMillis();
this.event = new SingleLiveEvent<>();
this.slowPrefixWarning = new SingleLiveEvent<>();
}
@NonNull Observable<List<String>> getLogLinesObservable() {
@@ -50,7 +54,13 @@ public class SubmitDebugLogViewModel extends ViewModel {
try {
mode.postValue(Mode.LOADING);
long prefixStartTime = System.currentTimeMillis();
repo.getPrefixLogLines(prefixLines -> {
long prefixDurationMillis = System.currentTimeMillis() - prefixStartTime;
if (prefixDurationMillis > SLOW_PREFIX_THRESHOLD_MILLIS && RemoteConfig.showSlowDebugLogWarning()) {
slowPrefixWarning.postValue(prefixDurationMillis);
}
try {
List<String> prefixStrings = new ArrayList<>();
for (LogLine line : prefixLines) {
@@ -142,6 +152,10 @@ public class SubmitDebugLogViewModel extends ViewModel {
return event;
}
@NonNull LiveData<Long> getSlowPrefixWarning() {
return slowPrefixWarning;
}
void onDiskSaveLocationReady(@Nullable Uri uri) {
if (uri == null) {
Log.w(TAG, "Null URI!");
@@ -674,6 +674,15 @@ object RemoteConfig {
hotSwappable = true
)
/** Whether to surface a warning dialog when debug log prefix generation exceeds a threshold. */
@JvmStatic
@get:JvmName("showSlowDebugLogWarning")
val showSlowDebugLogWarning: Boolean by remoteBoolean(
key = "android.showSlowDebugLogWarning",
defaultValue = false,
hotSwappable = true
)
/** How often we allow an automatic session reset. */
@JvmStatic
@get:JvmName("automaticSessionResetIntervalSeconds")
+4
View File
@@ -3114,6 +3114,10 @@
<string name="SubmitDebugLogActivity_info" translatable="false">Info</string>
<string name="SubmitDebugLogActivity_warning" translatable="false">Warn</string>
<string name="SubmitDebugLogActivity_error" translatable="false">Error</string>
<!-- Title of dialog shown when debug log prefix generation is unusually slow -->
<string name="SubmitDebugLogActivity_slow_log_title" translatable="false">Slow log generation</string>
<!-- Body of dialog shown when debug log prefix generation is unusually slow. %1$d is duration in seconds. -->
<string name="SubmitDebugLogActivity_slow_log_message" translatable="false">Generating the debug log header took %1$d seconds. We should figure out what\'s slowing things down.</string>
<!-- SupportEmailUtil -->
<string name="SupportEmailUtil_support_email" translatable="false">support@signal.org</string>