Skip to content

Commit b80d2f1

Browse files
committed
add pruning of inactive recipient_settings
1 parent 52072f5 commit b80d2f1

File tree

4 files changed

+82
-30
lines changed

4 files changed

+82
-30
lines changed

app/src/main/java/org/session/libsession/avatars/AvatarCacheCleaner.kt

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -38,28 +38,15 @@ class AvatarCacheCleaner @Inject constructor(
3838
suspend fun cleanUpAvatars(): Int = withContext(Dispatchers.IO) {
3939
// 1) Build the set of still-wanted Avatars from:
4040
// -> Config
41-
// -> RecipientSettingsDatabase
4241

4342
// config
4443
val avatarsFromConfig: Set<RemoteFile> = recipientAvatarDownloadManager.getAllAvatars()
4544
// recipient_settings
46-
val recipientAvatars : Map<Address, RemoteFile> = recipientSettingsDatabase.getAllReferencedAvatarFiles()
47-
// mms and sms used to check for active references
48-
val localActiveAddresses : Set<Address> = mmsSmsDatabase.getAllReferencedAddresses()
49-
.mapTo(mutableSetOf()) { Address.fromSerialized(it) }
50-
51-
// 2) Keep only those recipient avatars whose address exists in a community/group
52-
// Filter here since we don't delete rows from recipient_settings
53-
val keepFromRecipients: Set<RemoteFile> =
54-
recipientAvatars
55-
.asSequence()
56-
.filter { (address, _) -> address in localActiveAddresses }
57-
.map { it.value }
58-
.toSet()
45+
val recipientAvatars : Set<RemoteFile> = recipientSettingsDatabase.getAllReferencedAvatarFiles()
5946

6047
// 3) Union of everything we want to keep
6148
val filesToKeep: Set<RemoteFile> =
62-
(avatarsFromConfig + keepFromRecipients).toSet()
49+
(avatarsFromConfig + recipientAvatars).toSet()
6350

6451
// 4) Map to actual files (same hashing/location as downloader)
6552
val wantedFiles: Set<File> = filesToKeep

app/src/main/java/org/thoughtcrime/securesms/configs/ConfigToDatabaseSync.kt

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import kotlinx.coroutines.flow.collectLatest
99
import kotlinx.coroutines.flow.combine
1010
import kotlinx.coroutines.flow.distinctUntilChanged
1111
import kotlinx.coroutines.flow.filterNotNull
12-
import kotlinx.coroutines.flow.first
1312
import kotlinx.coroutines.flow.flatMapLatest
1413
import kotlinx.coroutines.flow.map
1514
import kotlinx.coroutines.flow.onStart
@@ -19,6 +18,7 @@ import network.loki.messenger.R
1918
import network.loki.messenger.libsession_util.ReadableGroupInfoConfig
2019
import network.loki.messenger.libsession_util.util.Conversation
2120
import network.loki.messenger.libsession_util.util.UserPic
21+
import org.session.libsession.avatars.AvatarCacheCleaner
2222
import org.session.libsession.database.StorageProtocol
2323
import org.session.libsession.messaging.sending_receiving.notifications.MessageNotifier
2424
import org.session.libsession.messaging.sending_receiving.notifications.PushRegistryV1
@@ -45,6 +45,7 @@ import org.thoughtcrime.securesms.database.LokiMessageDatabase
4545
import org.thoughtcrime.securesms.database.LokiThreadDatabase
4646
import org.thoughtcrime.securesms.database.MmsDatabase
4747
import org.thoughtcrime.securesms.database.MmsSmsDatabase
48+
import org.thoughtcrime.securesms.database.RecipientSettingsDatabase
4849
import org.thoughtcrime.securesms.database.SmsDatabase
4950
import org.thoughtcrime.securesms.database.ThreadDatabase
5051
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
@@ -83,6 +84,8 @@ class ConfigToDatabaseSync @Inject constructor(
8384
private val mmsSmsDatabase: MmsSmsDatabase,
8485
private val lokiMessageDatabase: LokiMessageDatabase,
8586
private val messageNotifier: MessageNotifier,
87+
private val recipientSettingsDatabase: RecipientSettingsDatabase,
88+
private val avatarCacheCleaner: AvatarCacheCleaner,
8689
@param:ManagerScope private val scope: CoroutineScope,
8790
) : OnAppStartupComponent {
8891
init {
@@ -148,6 +151,16 @@ class ConfigToDatabaseSync @Inject constructor(
148151
}
149152
}
150153
}
154+
155+
// Initiate cleanup in recipient_settings rows that no longer have any messages
156+
val addressesToKeep = mmsSmsDatabase.getAllReferencedAddresses()
157+
.mapTo(mutableSetOf()) { Address.fromSerialized(it) }
158+
159+
val removed = recipientSettingsDatabase.cleanupRecipientSettings(addressesToKeep)
160+
Log.d(TAG, "Recipient settings pruned: $removed orphan rows")
161+
if(removed > 0){
162+
avatarCacheCleaner.launchAvatarCleanup()
163+
}
151164
}
152165

153166
// If we created threads, we need to update the thread database with the creation date.

app/src/main/java/org/thoughtcrime/securesms/database/RecipientSettingsDatabase.kt

Lines changed: 66 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -157,31 +157,85 @@ class RecipientSettingsDatabase @Inject constructor(
157157
}
158158

159159
/**
160-
* This method returns all referenced profile picture URL.
160+
* This method returns all profile pic url and key.
161161
* This will be used to identify which avatars are still being used to exclude
162162
* them from the cleanup.
163163
*/
164-
fun getAllReferencedAvatarFiles(): Map<Address, RemoteFile.Encrypted> {
165-
LinkedHashSet<RemoteFile.Encrypted>()
166-
167-
val sql =
164+
fun getAllReferencedAvatarFiles(): Set<RemoteFile.Encrypted> {
165+
val recipientAvatars = LinkedHashSet<RemoteFile.Encrypted>()
166+
readableDatabase.rawQuery(
168167
"""
169168
SELECT $COL_PROFILE_PIC_URL, $COL_PROFILE_PIC_KEY
170169
FROM $TABLE_NAME
171170
WHERE $COL_PROFILE_PIC_URL IS NOT NULL AND $COL_PROFILE_PIC_URL != ''
172171
AND $COL_PROFILE_PIC_KEY IS NOT NULL AND $COL_PROFILE_PIC_KEY != ''
173-
""".trimIndent()
172+
""".trimIndent(),
173+
null
174+
).use { cursor ->
175+
while (cursor.moveToNext()) {
176+
val url = cursor.getString(0)
177+
val keyB64 = cursor.getString(1)
178+
runCatching {
179+
val keyBytes = Base64.decode(keyB64)
180+
recipientAvatars += RemoteFile.Encrypted(url = url, key = Bytes(keyBytes))
181+
}.onFailure {
182+
// ignore bad rows
183+
}
184+
}
185+
}
186+
return recipientAvatars
187+
}
174188

175-
return readableDatabase.rawQuery(sql, emptyArray()).use { cursor ->
176-
buildMap {
189+
fun getAllRecipientAddresses(): Set<Address> {
190+
return readableDatabase.rawQuery(
191+
"SELECT $COL_ADDRESS FROM $TABLE_NAME", emptyArray()
192+
).use { cursor ->
193+
buildSet {
177194
while (cursor.moveToNext()) {
178-
val address = Address.fromSerialized(cursor.getString(0))
179-
val url = cursor.getString(0)
180-
val key = Base64.decode(cursor.getString(1))
181-
put(address, RemoteFile.Encrypted(url = url, key = Bytes(key)))
195+
val raw = cursor.getString(0)
196+
if (!raw.isNullOrBlank()) add(Address.fromSerialized(raw))
197+
}
198+
}
199+
}
200+
}
201+
202+
/**
203+
* Delete all rows whose address is NOT in [addressesToKeep].
204+
* Returns the number of rows deleted.
205+
*/
206+
fun cleanupRecipientSettings(addressesToKeep: Set<Address>): Int {
207+
if (addressesToKeep.isEmpty()) return 0
208+
209+
// Build a temporary lookup of strings for SQL bind args
210+
val keepSet = addressesToKeep.mapTo(hashSetOf()) { it.toString() }
211+
212+
// Collect all rows, figure out orphans in memory
213+
val allRecipientAddresses = getAllRecipientAddresses()
214+
val orphans = allRecipientAddresses.filter { it.toString() !in keepSet }
215+
216+
if (orphans.isEmpty()) return 0
217+
218+
var deleted = 0
219+
val db = writableDatabase
220+
db.beginTransaction()
221+
try {
222+
for (address in orphans) {
223+
val rows = db.delete(
224+
TABLE_NAME,
225+
"$COL_ADDRESS = ?",
226+
arrayOf(address.toString())
227+
)
228+
if (rows > 0) {
229+
cache.remove(address)
230+
mutableChangeNotification.tryEmit(address)
231+
deleted += rows
182232
}
183233
}
234+
db.setTransactionSuccessful()
235+
} finally {
236+
db.endTransaction()
184237
}
238+
return deleted
185239
}
186240

187241
companion object {

app/src/main/java/org/thoughtcrime/securesms/groups/OpenGroupManager.kt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,6 @@ class OpenGroupManager @Inject constructor(
6565
configs.userGroups.eraseCommunity(server, room)
6666
configs.convoInfoVolatile.eraseCommunity(server, room)
6767
}
68-
69-
avatarCacheCleaner.launchAvatarCleanup()
7068
}
7169

7270
suspend fun addOpenGroup(urlAsString: String) {

0 commit comments

Comments
 (0)