Migrate queued jobs during SMS migration.

This commit is contained in:
Greyson Parrelli
2022-12-16 23:29:07 -05:00
parent 4d9dc42868
commit c6f29fc950
14 changed files with 144 additions and 33 deletions

View File

@@ -114,9 +114,10 @@ public class ApplicationMigrations {
static final int THREAD_MESSAGE_SCHEMA_CHANGE = 70;
static final int SMS_MMS_MERGE = 71;
static final int REBUILD_MESSAGE_FTS_INDEX = 72;
static final int UPDATE_SMS_JOBS = 73;
}
public static final int CURRENT_VERSION = 72;
public static final int CURRENT_VERSION = 73;
/**
* This *must* be called after the {@link JobManager} has been instantiated, but *before* the call
@@ -506,6 +507,10 @@ public class ApplicationMigrations {
jobs.put(Version.REBUILD_MESSAGE_FTS_INDEX, new RebuildMessageSearchIndexMigrationJob());
}
if (lastSeenVersion < Version.UPDATE_SMS_JOBS) {
jobs.put(Version.UPDATE_SMS_JOBS, new UpdateSmsJobsMigrationJob());
}
return jobs;
}

View File

@@ -0,0 +1,76 @@
package org.thoughtcrime.securesms.migrations
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.jobmanager.Data
import org.thoughtcrime.securesms.jobmanager.Data.Serializer
import org.thoughtcrime.securesms.jobmanager.Job
import org.thoughtcrime.securesms.jobmanager.persistence.JobSpec
import org.thoughtcrime.securesms.keyvalue.SignalStore
/**
* Updates the data in queued jobs to reflect the new ids SMS messages get assigned during the table merge migration.
* Normally we'd do this in a JobManager migration, but unfortunately this migration requires that a database migration
* happened already, but we don't want the database to be accessed until the [DatabaseMigrationJob] is run, otherwise
* we won't show the progress update.
*
* This ends up being more straightforward regardless because by the time this application migration is being run, it must be the
* case that the database migration is finished (since it's enqueued after the [DatabaseMigrationJob]), so we don't have to
* do any weird wait-notify stuff to guarantee the offset is set.
*/
internal class UpdateSmsJobsMigrationJob(
parameters: Parameters = Parameters.Builder().build()
) : MigrationJob(parameters) {
companion object {
val TAG = Log.tag(UpdateSmsJobsMigrationJob::class.java)
const val KEY = "UpdateSmsJobsMigrationJob"
}
override fun getFactoryKey(): String = KEY
override fun isUiBlocking(): Boolean = false
override fun performMigration() {
val idOffset = SignalStore.plaintext().smsMigrationIdOffset
check(idOffset >= 0) { "Invalid ID offset of $idOffset -- this shouldn't be possible!" }
ApplicationDependencies.getJobManager().update { jobSpec, serializer ->
when (jobSpec.factoryKey) {
"PushTextSendJob" -> jobSpec.updateAndSerialize(serializer, "message_id", null, idOffset)
"ReactionSendJob" -> jobSpec.updateAndSerialize(serializer, "message_id", "is_mms", idOffset)
"RemoteDeleteSendJob" -> jobSpec.updateAndSerialize(serializer, "message_id", "is_mms", idOffset)
"SmsSendJob" -> jobSpec.updateAndSerialize(serializer, "message_id", null, idOffset)
"SmsSentJob" -> jobSpec.updateAndSerialize(serializer, "message_id", null, idOffset)
else -> jobSpec
}
}
}
private fun JobSpec.updateAndSerialize(serializer: Serializer, idKey: String, isMmsKey: String?, offset: Long): JobSpec {
val data = serializer.deserialize(this.serializedData)
if (isMmsKey != null && data.getBooleanOrDefault(isMmsKey, false)) {
return this
}
return if (data.hasLong(idKey)) {
val currentValue: Long = data.getLong(idKey)
val updatedValue: Long = currentValue + offset
val updatedData: Data = data.buildUpon().putLong(idKey, updatedValue).build()
Log.d(TAG, "Updating job with factory ${this.factoryKey} from $currentValue to $updatedValue")
this.withData(serializer.serialize(updatedData))
} else {
this
}
}
override fun shouldRetry(e: Exception): Boolean = false
class Factory : Job.Factory<UpdateSmsJobsMigrationJob> {
override fun create(parameters: Parameters, data: Data): UpdateSmsJobsMigrationJob {
return UpdateSmsJobsMigrationJob(parameters)
}
}
}