From 4b41d7afaa761d0159a56da46dfa6c6fb5638518 Mon Sep 17 00:00:00 2001 From: andrew-signal Date: Thu, 29 May 2025 11:00:15 -0400 Subject: [PATCH] Only fetch profiles if we haven't fetched them in the last five minutes. --- .../securesms/database/RecipientTable.kt | 5 ++++ .../securesms/jobs/RetrieveProfileJob.kt | 27 ++++++++++++++++--- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt index 853c7bec24..d5d30374b2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt @@ -3662,6 +3662,11 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da db.update(TABLE_NAME, values, query.where, query.whereArgs) } } + + // Invalidate recipient cache so that updated timestamps are reflected + ids.forEach { id -> + AppDependencies.databaseObserver.notifyRecipientChanged(id) + } } fun applyBlockedUpdate(blockedE164s: List, blockedAcis: List, blockedGroupIds: List) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.kt index 6166a25459..ccb6511cf4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.kt @@ -46,6 +46,7 @@ import org.whispersystems.signalservice.internal.ServiceResponse import java.io.IOException import java.util.Optional import java.util.concurrent.TimeUnit +import kotlin.time.Duration.Companion.minutes /** * Retrieves a users profile and sets the appropriate local fields. @@ -92,11 +93,27 @@ class RetrieveProfileJob private constructor(parameters: Parameters, private val val recipients = Recipient.resolvedList(recipientIds) stopwatch.split("resolve-ensure") - val requests: List>>> = recipients - .filter { it.hasServiceId } + val currentTime = System.currentTimeMillis() + val debounceThreshold = currentTime - PROFILE_FETCH_DEBOUNCE_TIME_MS + val recipientsToFetch = recipients.filter { recipient -> + recipient.hasServiceId && recipient.lastProfileFetchTime < debounceThreshold + } + + if (recipientsToFetch.isEmpty()) { + Log.i(TAG, "All ${recipients.size} recipients have been fetched recently (within ${PROFILE_FETCH_DEBOUNCE_TIME_MS}ms). Skipping network requests.") + return + } + + if (recipientsToFetch.size < recipients.size) { + Log.i(TAG, "Debouncing: Fetching ${recipientsToFetch.size} of ${recipients.size} recipients (${recipients.size - recipientsToFetch.size} were fetched recently)") + } + + val requests: List>>> = recipientsToFetch .map { ProfileUtil.retrieveProfile(context, it, getRequestType(it)).toObservable() } stopwatch.split("requests") + val fetchingRecipientIds = recipientsToFetch.map { it.id }.toSet() + val operationState = Observable.mergeDelayError(requests, 16, 1) .observeOn(Schedulers.io(), true) .scan(OperationState()) { state: OperationState, pair: Pair> -> @@ -121,11 +138,11 @@ class RetrieveProfileJob private constructor(parameters: Parameters, private val .safeBlockingGet() stopwatch.split("responses") - val localRecords = SignalDatabase.recipients.getExistingRecords(recipientIds) + val localRecords = SignalDatabase.recipients.getExistingRecords(fetchingRecipientIds) Log.d(TAG, "Fetched ${localRecords.size} existing records.") stopwatch.split("disk-fetch") - val successIds: Set = recipientIds - operationState.retries + val successIds: Set = fetchingRecipientIds - operationState.retries val newlyRegisteredIds: Set = operationState.profiles .map { it.first() } .filterNot { it.isRegistered } @@ -529,6 +546,8 @@ class RetrieveProfileJob private constructor(parameters: Parameters, private val private const val DEDUPE_KEY_RETRIEVE_AVATAR = KEY + "_RETRIEVE_PROFILE_AVATAR" private const val QUEUE_PREFIX = "RetrieveProfileJob_" + private val PROFILE_FETCH_DEBOUNCE_TIME_MS = 5.minutes.inWholeMilliseconds + /** * Submits the necessary job to refresh the profile of the requested recipient. Works for any * RecipientId, including individuals, groups, or yourself.