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

@@ -8,8 +8,10 @@ import androidx.core.content.contentValuesOf
import net.zetetic.database.sqlcipher.SQLiteDatabase
import net.zetetic.database.sqlcipher.SQLiteOpenHelper
import org.signal.core.util.CursorUtil
import org.signal.core.util.SqlUtil
import org.signal.core.util.concurrent.SignalExecutors
import org.signal.core.util.delete
import org.signal.core.util.forEach
import org.signal.core.util.insertInto
import org.signal.core.util.logging.Log
import org.signal.core.util.readToList
@@ -31,6 +33,7 @@ import org.thoughtcrime.securesms.jobmanager.persistence.DependencySpec
import org.thoughtcrime.securesms.jobmanager.persistence.FullSpec
import org.thoughtcrime.securesms.jobmanager.persistence.JobSpec
import org.thoughtcrime.securesms.jobs.MinimalJobSpec
import java.util.function.Predicate
class JobDatabase(
application: Application,
@@ -184,28 +187,32 @@ class JobDatabase(
}
@Synchronized
fun getAllJobSpecs(): List<JobSpec> {
return readableDatabase
.select()
.from(Jobs.TABLE_NAME)
.orderBy("${Jobs.CREATE_TIME}, ${Jobs.ID} ASC")
.run()
.readToList { cursor ->
jobSpecFromCursor(cursor)
}
}
@Synchronized
fun getOldestJobSpecs(limit: Int): List<JobSpec> {
fun getJobSpecs(limit: Int): List<JobSpec> {
return readableDatabase
.select()
.from(Jobs.TABLE_NAME)
.orderBy("${Jobs.CREATE_TIME}, ${Jobs.ID} ASC")
.limit(limit)
.run()
.readToList { it.toJobSpec() }
}
@Synchronized
fun getAllMatchingFilter(predicate: Predicate<JobSpec>): List<JobSpec> {
val output: MutableList<JobSpec> = mutableListOf()
readableDatabase
.select()
.from(Jobs.TABLE_NAME)
.run()
.readToList { cursor ->
jobSpecFromCursor(cursor)
val jobSpec = cursor.toJobSpec()
if (predicate.test(jobSpec)) {
output += jobSpec
}
}
return output
}
@Synchronized
@@ -215,9 +222,7 @@ class JobDatabase(
.from(Jobs.TABLE_NAME)
.where("${Jobs.JOB_SPEC_ID} = ?", id)
.run()
.readToSingleObject {
jobSpecFromCursor(it)
}
.readToSingleObject { it.toJobSpec() }
}
@Synchronized
@@ -296,26 +301,41 @@ class JobDatabase(
.filterNot { it.isMemoryOnly }
.forEach { job ->
db.update(Jobs.TABLE_NAME)
.values(
Jobs.JOB_SPEC_ID to job.id,
Jobs.FACTORY_KEY to job.factoryKey,
Jobs.QUEUE_KEY to job.queueKey,
Jobs.CREATE_TIME to job.createTime,
Jobs.LAST_RUN_ATTEMPT_TIME to job.lastRunAttemptTime,
Jobs.NEXT_BACKOFF_INTERVAL to job.nextBackoffInterval,
Jobs.RUN_ATTEMPT to job.runAttempt,
Jobs.MAX_ATTEMPTS to job.maxAttempts,
Jobs.LIFESPAN to job.lifespan,
Jobs.SERIALIZED_DATA to job.serializedData,
Jobs.SERIALIZED_INPUT_DATA to job.serializedInputData,
Jobs.IS_RUNNING to if (job.isRunning) 1 else 0
)
.values(job.toContentValues())
.where("${Jobs.JOB_SPEC_ID} = ?", job.id)
.run()
}
}
}
@Synchronized
fun transformJobs(transformer: (JobSpec) -> JobSpec): List<JobSpec> {
val transformed: MutableList<JobSpec> = mutableListOf()
writableDatabase.withinTransaction { db ->
readableDatabase
.select()
.from(Jobs.TABLE_NAME)
.run()
.forEach { cursor ->
val jobSpec = cursor.toJobSpec()
val updated = transformer(jobSpec)
if (updated != jobSpec) {
transformed += updated
}
}
for (job in transformed) {
db.update(Jobs.TABLE_NAME)
.values(job.toContentValues())
.where("${Jobs.JOB_SPEC_ID} = ?", job.id)
.run()
}
}
return transformed
}
@Synchronized
fun deleteJobs(jobIds: List<String>) {
writableDatabase.withinTransaction { db ->
@@ -340,14 +360,30 @@ class JobDatabase(
}
@Synchronized
fun getAllConstraintSpecs(): List<ConstraintSpec> {
fun getConstraintSpecs(limit: Int): List<ConstraintSpec> {
return readableDatabase
.select()
.from(Constraints.TABLE_NAME)
.limit(limit)
.run()
.readToList { cursor ->
constraintSpecFromCursor(cursor)
}
.readToList { it.toConstraintSpec() }
}
fun getConstraintSpecsForJobs(jobIds: Collection<String>): List<ConstraintSpec> {
val output: MutableList<ConstraintSpec> = mutableListOf()
for (query in SqlUtil.buildCollectionQuery(Constraints.JOB_SPEC_ID, jobIds)) {
readableDatabase
.select()
.from(Constraints.TABLE_NAME)
.where(query.where, query.whereArgs)
.run()
.forEach {
output += it.toConstraintSpec()
}
}
return output
}
@Synchronized
@@ -356,9 +392,7 @@ class JobDatabase(
.select()
.from(Dependencies.TABLE_NAME)
.run()
.readToList { cursor ->
dependencySpecFromCursor(cursor)
}
.readToList { it.toDependencySpec() }
}
private fun insertJobSpec(db: SQLiteDatabase, job: JobSpec) {
@@ -369,21 +403,7 @@ class JobDatabase(
check(db.inTransaction())
db.insertInto(Jobs.TABLE_NAME)
.values(
Jobs.JOB_SPEC_ID to job.id,
Jobs.FACTORY_KEY to job.factoryKey,
Jobs.QUEUE_KEY to job.queueKey,
Jobs.CREATE_TIME to job.createTime,
Jobs.LAST_RUN_ATTEMPT_TIME to job.lastRunAttemptTime,
Jobs.NEXT_BACKOFF_INTERVAL to job.nextBackoffInterval,
Jobs.RUN_ATTEMPT to job.runAttempt,
Jobs.MAX_ATTEMPTS to job.maxAttempts,
Jobs.LIFESPAN to job.lifespan,
Jobs.SERIALIZED_DATA to job.serializedData,
Jobs.SERIALIZED_INPUT_DATA to job.serializedInputData,
Jobs.IS_RUNNING to if (job.isRunning) 1 else 0,
Jobs.PRIORITY to job.priority
)
.values(job.toContentValues())
.run(SQLiteDatabase.CONFLICT_IGNORE)
}
@@ -417,37 +437,37 @@ class JobDatabase(
}
}
private fun jobSpecFromCursor(cursor: Cursor): JobSpec {
private fun Cursor.toJobSpec(): JobSpec {
return JobSpec(
id = cursor.requireNonNullString(Jobs.JOB_SPEC_ID),
factoryKey = cursor.requireNonNullString(Jobs.FACTORY_KEY),
queueKey = cursor.requireString(Jobs.QUEUE_KEY),
createTime = cursor.requireLong(Jobs.CREATE_TIME),
lastRunAttemptTime = cursor.requireLong(Jobs.LAST_RUN_ATTEMPT_TIME),
nextBackoffInterval = cursor.requireLong(Jobs.NEXT_BACKOFF_INTERVAL),
runAttempt = cursor.requireInt(Jobs.RUN_ATTEMPT),
maxAttempts = cursor.requireInt(Jobs.MAX_ATTEMPTS),
lifespan = cursor.requireLong(Jobs.LIFESPAN),
serializedData = cursor.requireBlob(Jobs.SERIALIZED_DATA),
serializedInputData = cursor.requireBlob(Jobs.SERIALIZED_INPUT_DATA),
isRunning = cursor.requireBoolean(Jobs.IS_RUNNING),
id = this.requireNonNullString(Jobs.JOB_SPEC_ID),
factoryKey = this.requireNonNullString(Jobs.FACTORY_KEY),
queueKey = this.requireString(Jobs.QUEUE_KEY),
createTime = this.requireLong(Jobs.CREATE_TIME),
lastRunAttemptTime = this.requireLong(Jobs.LAST_RUN_ATTEMPT_TIME),
nextBackoffInterval = this.requireLong(Jobs.NEXT_BACKOFF_INTERVAL),
runAttempt = this.requireInt(Jobs.RUN_ATTEMPT),
maxAttempts = this.requireInt(Jobs.MAX_ATTEMPTS),
lifespan = this.requireLong(Jobs.LIFESPAN),
serializedData = this.requireBlob(Jobs.SERIALIZED_DATA),
serializedInputData = this.requireBlob(Jobs.SERIALIZED_INPUT_DATA),
isRunning = this.requireBoolean(Jobs.IS_RUNNING),
isMemoryOnly = false,
priority = cursor.requireInt(Jobs.PRIORITY)
priority = this.requireInt(Jobs.PRIORITY)
)
}
private fun constraintSpecFromCursor(cursor: Cursor): ConstraintSpec {
private fun Cursor.toConstraintSpec(): ConstraintSpec {
return ConstraintSpec(
jobSpecId = cursor.requireNonNullString(Constraints.JOB_SPEC_ID),
factoryKey = cursor.requireNonNullString(Constraints.FACTORY_KEY),
jobSpecId = this.requireNonNullString(Constraints.JOB_SPEC_ID),
factoryKey = this.requireNonNullString(Constraints.FACTORY_KEY),
isMemoryOnly = false
)
}
private fun dependencySpecFromCursor(cursor: Cursor): DependencySpec {
private fun Cursor.toDependencySpec(): DependencySpec {
return DependencySpec(
jobId = cursor.requireNonNullString(Dependencies.JOB_SPEC_ID),
dependsOnJobId = cursor.requireNonNullString(Dependencies.DEPENDS_ON_JOB_SPEC_ID),
jobId = this.requireNonNullString(Dependencies.JOB_SPEC_ID),
dependsOnJobId = this.requireNonNullString(Dependencies.DEPENDS_ON_JOB_SPEC_ID),
isMemoryOnly = false
)
}
@@ -468,6 +488,24 @@ class JobDatabase(
writableDatabase.update(Jobs.TABLE_NAME, contentValuesOf(Jobs.NEXT_BACKOFF_INTERVAL to 0), null, null)
}
private fun JobSpec.toContentValues(): ContentValues {
return contentValuesOf(
Jobs.JOB_SPEC_ID to this.id,
Jobs.FACTORY_KEY to this.factoryKey,
Jobs.QUEUE_KEY to this.queueKey,
Jobs.CREATE_TIME to this.createTime,
Jobs.LAST_RUN_ATTEMPT_TIME to this.lastRunAttemptTime,
Jobs.NEXT_BACKOFF_INTERVAL to this.nextBackoffInterval,
Jobs.RUN_ATTEMPT to this.runAttempt,
Jobs.MAX_ATTEMPTS to this.maxAttempts,
Jobs.LIFESPAN to this.lifespan,
Jobs.SERIALIZED_DATA to this.serializedData,
Jobs.SERIALIZED_INPUT_DATA to this.serializedInputData,
Jobs.IS_RUNNING to if (this.isRunning) 1 else 0,
Jobs.PRIORITY to this.priority
)
}
companion object {
private val TAG = Log.tag(JobDatabase::class.java)
private const val DATABASE_VERSION = 3