Remove old deleted folders from storage service.

This commit is contained in:
Michelle Tang
2025-04-09 14:10:36 -04:00
parent 94d5fe3e43
commit 6006c047d8
5 changed files with 50 additions and 5 deletions

View File

@@ -10,7 +10,6 @@ import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.Guideline
import androidx.core.content.withStyledAttributes
import androidx.core.graphics.Insets
import androidx.core.view.OnApplyWindowInsetsListener
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsAnimationCompat
import androidx.core.view.WindowInsetsCompat

View File

@@ -285,6 +285,12 @@ private fun StorageRecordRow(record: SignalStorageRecord) {
ManifestItemRow("ID", Hex.toStringCondensed(record.id.raw))
}
}
record.proto.chatFolder != null -> {
Column {
Text("Chat Folder", fontWeight = FontWeight.Bold)
ManifestItemRow("ID", Hex.toStringCondensed(record.id.raw))
}
}
else -> {
Column {
Text("Unknown!")

View File

@@ -39,6 +39,7 @@ import org.whispersystems.signalservice.api.push.SignalServiceAddress
import org.whispersystems.signalservice.api.storage.SignalChatFolderRecord
import org.whispersystems.signalservice.api.storage.StorageId
import org.whispersystems.signalservice.api.util.UuidUtil
import java.util.concurrent.TimeUnit
import org.whispersystems.signalservice.internal.storage.protos.ChatFolderRecord as RemoteChatFolderRecord
/**
@@ -48,6 +49,7 @@ class ChatFolderTables(context: Context?, databaseHelper: SignalDatabase?) : Dat
companion object {
private val TAG = Log.tag(ChatFolderTable::class.java)
private val DELETED_LIFESPAN: Long = TimeUnit.DAYS.toMillis(30)
@JvmField
val CREATE_TABLE: Array<String> = arrayOf(ChatFolderTable.CREATE_TABLE, ChatFolderMembershipTable.CREATE_TABLE)
@@ -544,6 +546,36 @@ class ChatFolderTables(context: Context?, databaseHelper: SignalDatabase?) : Dat
updateFolder(remoteChatFolderRecordToLocal(record))
}
/**
* Removes storageIds from folders that have been deleted for [DELETED_LIFESPAN].
*/
fun removeStorageIdsFromOldDeletedFolders(now: Long): Int {
return writableDatabase
.update(ChatFolderTable.TABLE_NAME)
.values(ChatFolderTable.STORAGE_SERVICE_ID to null)
.where("${ChatFolderTable.STORAGE_SERVICE_ID} NOT NULL AND ${ChatFolderTable.DELETED_TIMESTAMP_MS} > 0 AND ${ChatFolderTable.DELETED_TIMESTAMP_MS} < ?", now - DELETED_LIFESPAN)
.run()
}
/**
* Removes storageIds of folders from the given collection because those folders are local only and deleted
*/
fun removeStorageIdsFromLocalOnlyDeletedFolders(storageIds: Collection<StorageId>): Int {
var updated = 0
SqlUtil.buildCollectionQuery(ChatFolderTable.STORAGE_SERVICE_ID, storageIds.map { Base64.encodeWithPadding(it.raw) }, "${ChatFolderTable.DELETED_TIMESTAMP_MS} > 0 AND")
.forEach {
updated += writableDatabase.update(
ChatFolderTable.TABLE_NAME,
contentValuesOf(ChatFolderTable.STORAGE_SERVICE_ID to null),
it.where,
it.whereArgs
)
}
return updated
}
/**
* Maps a chat folder id to all of its corresponding included chats.
* If an id is not specified, all chat folder ids will be mapped.

View File

@@ -1,9 +1,11 @@
package org.thoughtcrime.securesms.database.helpers.migration
import android.app.Application
import org.signal.core.util.Base64
import org.signal.core.util.readToList
import org.signal.core.util.requireLong
import org.thoughtcrime.securesms.database.SQLiteDatabase
import org.thoughtcrime.securesms.storage.StorageSyncHelper
import java.util.UUID
/**
@@ -30,7 +32,7 @@ object V270_FixChatFolderColumnsForStorageSync : SignalDatabaseMigration {
db.rawQuery(
"""
UPDATE chat_folder
SET chat_folder_id = '${UUID.randomUUID()}', position = $newPosition
SET chat_folder_id = '${UUID.randomUUID()}', position = $newPosition, storage_service_id = '${Base64.encodeWithPadding(StorageSyncHelper.generateKey())}'
WHERE _id = $id
"""
)

View File

@@ -257,10 +257,11 @@ class StorageSyncJob private constructor(parameters: Parameters) : BaseJob(param
Log.i(TAG, "[Remote Sync] Pre-Merge ID Difference :: $idDifference")
if (idDifference.localOnlyIds.isNotEmpty()) {
val updated = SignalDatabase.recipients.removeStorageIdsFromLocalOnlyUnregisteredRecipients(idDifference.localOnlyIds)
val updatedRecipients = SignalDatabase.recipients.removeStorageIdsFromLocalOnlyUnregisteredRecipients(idDifference.localOnlyIds)
val updatedFolders = SignalDatabase.chatFolders.removeStorageIdsFromLocalOnlyDeletedFolders(idDifference.localOnlyIds)
if (updated > 0) {
Log.w(TAG, "Found $updated records that were deleted remotely but only marked unregistered locally. Removed those from local store. Recalculating diff.")
if (updatedRecipients > 0 || updatedFolders > 0) {
Log.w(TAG, "Found $updatedRecipients recipients and $updatedFolders folders that were deleted remotely but only marked unregistered/deleted locally. Removed those from local store. Recalculating diff.")
localStorageIdsBeforeMerge = getAllLocalStorageIds(self)
idDifference = StorageSyncHelper.findIdDifference(remoteManifest.storageIds, localStorageIdsBeforeMerge)
@@ -335,6 +336,11 @@ class StorageSyncJob private constructor(parameters: Parameters) : BaseJob(param
Log.i(TAG, "Removed $removedUnregistered recipients from storage service that have been unregistered for longer than 30 days.")
}
val removedDeletedFolders = SignalDatabase.chatFolders.removeStorageIdsFromOldDeletedFolders(System.currentTimeMillis())
if (removedDeletedFolders > 0) {
Log.i(TAG, "Removed $removedDeletedFolders folders from storage service that have been deleted for longer than 30 days.")
}
val localStorageIds = getAllLocalStorageIds(self)
val idDifference = StorageSyncHelper.findIdDifference(remoteManifest.storageIds, localStorageIds)
val remoteInserts = buildLocalStorageRecords(context, self, idDifference.localOnlyIds.stream().filter { it: StorageId -> !it.isUnknown }.collect(Collectors.toList()))