Remove cases where all jobs were expected to be in memory.

This commit is contained in:
Greyson Parrelli
2024-07-17 11:58:09 -04:00
committed by Nicholas Tinsley
parent 973dc72cfa
commit 86cf8200b5
10 changed files with 422 additions and 239 deletions

View File

@@ -231,26 +231,13 @@ class JobController {
@WorkerThread
synchronized void update(@NonNull JobUpdater updater) {
List<JobSpec> allJobs = jobStorage.getAllJobSpecs();
List<JobSpec> updatedJobs = new LinkedList<>();
for (JobSpec job : allJobs) {
JobSpec updated = updater.update(job);
if (updated != job) {
updatedJobs.add(updated);
}
}
jobStorage.updateJobs(updatedJobs);
jobStorage.transformJobs(updater::update);
notifyAll();
}
@WorkerThread
synchronized List<JobSpec> findJobs(@NonNull Predicate<JobSpec> predicate) {
return Stream.of(jobStorage.getAllJobSpecs())
.filter(predicate::test)
.toList();
return jobStorage.getAllMatchingFilter(predicate);
}
@WorkerThread
@@ -360,9 +347,9 @@ class JobController {
*/
@WorkerThread
synchronized @NonNull String getDebugInfo() {
List<JobSpec> jobs = jobStorage.getAllJobSpecs();
List<ConstraintSpec> constraints = jobStorage.getAllConstraintSpecs();
List<DependencySpec> dependencies = jobStorage.getAllDependencySpecs();
List<JobSpec> jobs = jobStorage.debugGetJobSpecs(1000);
List<ConstraintSpec> constraints = jobStorage.debugGetConstraintSpecs(1000);
List<DependencySpec> dependencies = jobStorage.debugGetAllDependencySpecs();
StringBuilder info = new StringBuilder();

View File

@@ -33,6 +33,33 @@ abstract class JobMigration protected constructor(val endVersion: Int) {
return copy(data = newData)
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as JobData
if (factoryKey != other.factoryKey) return false
if (queueKey != other.queueKey) return false
if (maxAttempts != other.maxAttempts) return false
if (lifespan != other.lifespan) return false
if (data != null) {
if (other.data == null) return false
if (!data.contentEquals(other.data)) return false
} else if (other.data != null) return false
return true
}
override fun hashCode(): Int {
var result = factoryKey.hashCode()
result = 31 * result + (queueKey?.hashCode() ?: 0)
result = 31 * result + maxAttempts
result = 31 * result + lifespan.hashCode()
result = 31 * result + (data?.contentHashCode() ?: 0)
return result
}
companion object {
@JvmField
val FAILING_JOB_DATA = JobData("FailingJob", null, -1, -1, null)

View File

@@ -11,7 +11,6 @@ import org.thoughtcrime.securesms.jobmanager.persistence.JobStorage;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
@SuppressLint("UseSparseArrays")
@@ -47,41 +46,36 @@ public class JobMigrator {
* @return The version that has been migrated to.
*/
int migrate(@NonNull JobStorage jobStorage) {
List<JobSpec> jobSpecs = jobStorage.getAllJobSpecs();
for (int i = lastSeenVersion; i < currentVersion; i++) {
Log.i(TAG, "Migrating from " + i + " to " + (i + 1));
ListIterator<JobSpec> iter = jobSpecs.listIterator();
JobMigration migration = migrations.get(i + 1);
JobMigration migration = migrations.get(i + 1);
assert migration != null;
while (iter.hasNext()) {
JobSpec jobSpec = iter.next();
JobData originalJobData = new JobData(jobSpec.getFactoryKey(), jobSpec.getQueueKey(), jobSpec.getMaxAttempts(), jobSpec.getLifespan(), jobSpec.getSerializedData());
JobData updatedJobData = migration.migrate(originalJobData);
JobSpec updatedJobSpec = new JobSpec(jobSpec.getId(),
updatedJobData.getFactoryKey(),
updatedJobData.getQueueKey(),
jobSpec.getCreateTime(),
jobSpec.getLastRunAttemptTime(),
jobSpec.getNextBackoffInterval(),
jobSpec.getRunAttempt(),
updatedJobData.getMaxAttempts(),
updatedJobData.getLifespan(),
updatedJobData.getData(),
jobSpec.getSerializedInputData(),
jobSpec.isRunning(),
jobSpec.isMemoryOnly(),
jobSpec.getPriority());
jobStorage.transformJobs(jobSpec -> {
JobData originalJobData = new JobData(jobSpec.getFactoryKey(), jobSpec.getQueueKey(), jobSpec.getMaxAttempts(), jobSpec.getLifespan(), jobSpec.getSerializedData());
JobData updatedJobData = migration.migrate(originalJobData);
iter.set(updatedJobSpec);
}
if (updatedJobData == originalJobData) {
return jobSpec;
}
return new JobSpec(jobSpec.getId(),
updatedJobData.getFactoryKey(),
updatedJobData.getQueueKey(),
jobSpec.getCreateTime(),
jobSpec.getLastRunAttemptTime(),
jobSpec.getNextBackoffInterval(),
jobSpec.getRunAttempt(),
updatedJobData.getMaxAttempts(),
updatedJobData.getLifespan(),
updatedJobData.getData(),
jobSpec.getSerializedInputData(),
jobSpec.isRunning(),
jobSpec.isMemoryOnly(),
jobSpec.getPriority());
});
}
jobStorage.updateJobs(jobSpecs);
return currentVersion;
}
}

View File

@@ -1,68 +0,0 @@
package org.thoughtcrime.securesms.jobmanager.persistence;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import java.util.List;
import java.util.Set;
public interface JobStorage {
@WorkerThread
void init();
@WorkerThread
void insertJobs(@NonNull List<FullSpec> fullSpecs);
@WorkerThread
@Nullable JobSpec getJobSpec(@NonNull String id);
@WorkerThread
@NonNull List<JobSpec> getAllJobSpecs();
@WorkerThread
@NonNull List<JobSpec> getPendingJobsWithNoDependenciesInCreatedOrder(long currentTime);
@WorkerThread
@NonNull List<JobSpec> getJobsInQueue(@NonNull String queue);
@WorkerThread
int getJobCountForFactory(@NonNull String factoryKey);
@WorkerThread
int getJobCountForFactoryAndQueue(@NonNull String factoryKey, @NonNull String queueKey);
@WorkerThread
boolean areQueuesEmpty(@NonNull Set<String> queueKeys);
@WorkerThread
void markJobAsRunning(@NonNull String id, long currentTime);
@WorkerThread
void updateJobAfterRetry(@NonNull String id, long currentTime, int runAttempt, long nextBackoffInterval, @Nullable byte[] serializedData);
@WorkerThread
void updateAllJobsToBePending();
@WorkerThread
void updateJobs(@NonNull List<JobSpec> jobSpecs);
@WorkerThread
void deleteJob(@NonNull String id);
@WorkerThread
void deleteJobs(@NonNull List<String> ids);
@WorkerThread
@NonNull List<ConstraintSpec> getConstraintSpecs(@NonNull String jobId);
@WorkerThread
@NonNull List<ConstraintSpec> getAllConstraintSpecs();
@WorkerThread
@NonNull List<DependencySpec> getDependencySpecsThatDependOnJob(@NonNull String jobSpecId);
@WorkerThread
@NonNull List<DependencySpec> getAllDependencySpecs();
}

View File

@@ -0,0 +1,69 @@
package org.thoughtcrime.securesms.jobmanager.persistence
import androidx.annotation.WorkerThread
import java.util.function.Predicate
interface JobStorage {
@WorkerThread
fun init()
@WorkerThread
fun insertJobs(fullSpecs: List<FullSpec>)
@WorkerThread
fun getJobSpec(id: String): JobSpec?
@WorkerThread
fun getAllMatchingFilter(predicate: Predicate<JobSpec>): List<JobSpec>
@WorkerThread
fun getPendingJobsWithNoDependenciesInCreatedOrder(currentTime: Long): List<JobSpec>
@WorkerThread
fun getJobsInQueue(queue: String): List<JobSpec>
@WorkerThread
fun getJobCountForFactory(factoryKey: String): Int
@WorkerThread
fun getJobCountForFactoryAndQueue(factoryKey: String, queueKey: String): Int
@WorkerThread
fun areQueuesEmpty(queueKeys: Set<String>): Boolean
@WorkerThread
fun markJobAsRunning(id: String, currentTime: Long)
@WorkerThread
fun updateJobAfterRetry(id: String, currentTime: Long, runAttempt: Int, nextBackoffInterval: Long, serializedData: ByteArray?)
@WorkerThread
fun updateAllJobsToBePending()
@WorkerThread
fun updateJobs(jobSpecs: List<JobSpec>)
@WorkerThread
fun transformJobs(transformer: (JobSpec) -> JobSpec)
@WorkerThread
fun deleteJob(id: String)
@WorkerThread
fun deleteJobs(ids: List<String>)
@WorkerThread
fun getConstraintSpecs(jobId: String): List<ConstraintSpec>
@WorkerThread
fun getDependencySpecsThatDependOnJob(jobSpecId: String): List<DependencySpec>
@WorkerThread
fun debugGetJobSpecs(limit: Int): List<JobSpec>
@WorkerThread
fun debugGetConstraintSpecs(limit: Int): List<ConstraintSpec>
@WorkerThread
fun debugGetAllDependencySpecs(): List<DependencySpec>
}