diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/LogDatabase.kt b/app/src/main/java/org/thoughtcrime/securesms/database/LogDatabase.kt index fc00dab115..e68a8085c6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/LogDatabase.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/LogDatabase.kt @@ -17,6 +17,7 @@ import org.signal.core.util.logging.Log import org.signal.core.util.mebiBytes import org.signal.core.util.readToList import org.signal.core.util.readToSingleInt +import org.signal.core.util.readToSingleLong import org.signal.core.util.requireLong import org.signal.core.util.requireNonNullString import org.signal.core.util.select @@ -147,9 +148,12 @@ class LogDatabase private constructor( ) """ + private const val KEEP_LONGER_INDEX = "keep_longer_index" + private const val CREATED_AT_KEEP_LONGER_INDEX = "log_created_at_keep_longer_index" + val CREATE_INDEXES = arrayOf( - "CREATE INDEX keep_longer_index ON $TABLE_NAME ($KEEP_LONGER)", - "CREATE INDEX log_created_at_keep_longer_index ON $TABLE_NAME ($CREATED_AT, $KEEP_LONGER)" + "CREATE INDEX $KEEP_LONGER_INDEX ON $TABLE_NAME ($KEEP_LONGER)", + "CREATE INDEX $CREATED_AT_KEEP_LONGER_INDEX ON $TABLE_NAME ($CREATED_AT, $KEEP_LONGER)" ) val MAX_FILE_SIZE = 20L.mebiBytes.inWholeBytes @@ -173,8 +177,8 @@ class LogDatabase private constructor( .run() } - db.delete(TABLE_NAME) - .where("($CREATED_AT < ? AND $KEEP_LONGER <= 0) OR ($CREATED_AT < ? AND $KEEP_LONGER => 1)", currentTime - DEFAULT_LIFESPAN, currentTime - LONGER_LIFESPAN) + db.delete("$TABLE_NAME INDEXED BY $CREATED_AT_KEEP_LONGER_INDEX") + .where("($CREATED_AT < ${currentTime - DEFAULT_LIFESPAN} AND $KEEP_LONGER <= 0) OR ($CREATED_AT < ${currentTime - LONGER_LIFESPAN} AND $KEEP_LONGER >= 1)") .run() } } @@ -182,7 +186,7 @@ class LogDatabase private constructor( fun getAllBeforeTime(time: Long): Reader { return readableDatabase .select(BODY) - .from(TABLE_NAME) + .from("$TABLE_NAME INDEXED BY $CREATED_AT_KEEP_LONGER_INDEX") .where("$CREATED_AT < $time") .run() .toReader() @@ -191,7 +195,7 @@ class LogDatabase private constructor( fun getRangeBeforeTime(start: Int, length: Int, time: Long): List { return readableDatabase .select(BODY) - .from(TABLE_NAME) + .from("$TABLE_NAME INDEXED BY $CREATED_AT_KEEP_LONGER_INDEX") .where("$CREATED_AT < $time") .limit(limit = length, offset = start) .run() @@ -202,20 +206,22 @@ class LogDatabase private constructor( val currentTime = System.currentTimeMillis() val stopwatch = Stopwatch("trim") - val sizeOfSpecialLogs: Long = getSize("$KEEP_LONGER = ?", arrayOf("1")) - val remainingSize = MAX_FILE_SIZE - sizeOfSpecialLogs + val sizeOfKeepLongerLogs: Long = getSize("$KEEP_LONGER = ?", arrayOf("1"), KEEP_LONGER_INDEX) + val remainingSizeAfterKeepLonger = MAX_FILE_SIZE - sizeOfKeepLongerLogs stopwatch.split("keepers-size") - if (remainingSize <= 0) { - if (abs(remainingSize) > MAX_FILE_SIZE / 2) { + // If we can't even fit our keep_longer logs within the size limit + if (remainingSizeAfterKeepLonger <= 0) { + if (abs(remainingSizeAfterKeepLonger) > MAX_FILE_SIZE / 2) { // Not only are KEEP_LONGER logs putting us over the storage limit, it's doing it by a lot! Delete half. val logCount = readableDatabase.getTableRowCount(TABLE_NAME) writableDatabase.execSQL("DELETE FROM $TABLE_NAME WHERE $ID < (SELECT MAX($ID) FROM (SELECT $ID FROM $TABLE_NAME LIMIT ${logCount / 2}))") } else { writableDatabase - .delete(TABLE_NAME) + .delete("$TABLE_NAME INDEXED BY $KEEP_LONGER_INDEX") .where("$KEEP_LONGER = 0") + .run() } return } @@ -229,12 +235,12 @@ class LogDatabase private constructor( while (lhs < rhs - 2) { mid = (lhs + rhs) / 2 - sizeOfChunk = getSize("$CREATED_AT > ? AND $CREATED_AT < ? AND $KEEP_LONGER = ?", SqlUtil.buildArgs(mid, currentTime, 0)) + sizeOfChunk = getSize("$CREATED_AT > ? AND $CREATED_AT < ? AND $KEEP_LONGER <= ?", SqlUtil.buildArgs(mid, currentTime, 0), CREATED_AT_KEEP_LONGER_INDEX) - if (sizeOfChunk > remainingSize) { + if (sizeOfChunk > remainingSizeAfterKeepLonger) { lhs = mid - } else if (sizeOfChunk < remainingSize) { - if (remainingSize - sizeOfChunk < sizeDiffThreshold) { + } else if (sizeOfChunk < remainingSizeAfterKeepLonger) { + if (remainingSizeAfterKeepLonger - sizeOfChunk < sizeDiffThreshold) { break } else { rhs = mid @@ -246,7 +252,10 @@ class LogDatabase private constructor( stopwatch.split("binary-search") - writableDatabase.delete(TABLE_NAME, "$CREATED_AT < ? AND $KEEP_LONGER = ?", SqlUtil.buildArgs(mid, 0)) + writableDatabase + .delete("$TABLE_NAME INDEXED BY $CREATED_AT_KEEP_LONGER_INDEX") + .where("$CREATED_AT < $mid AND $KEEP_LONGER <= 0") + .run() stopwatch.split("delete") stopwatch.stop(TAG) @@ -255,7 +264,7 @@ class LogDatabase private constructor( fun getLogCountBeforeTime(time: Long): Int { return readableDatabase .select("COUNT(*)") - .from(TABLE_NAME) + .from("$TABLE_NAME INDEXED BY $CREATED_AT_KEEP_LONGER_INDEX") .where("$CREATED_AT < $time") .run() .readToSingleInt() @@ -263,7 +272,7 @@ class LogDatabase private constructor( fun clearKeepLonger() { writableDatabase - .delete(TABLE_NAME) + .delete("$TABLE_NAME INDEXED BY $KEEP_LONGER_INDEX") .where("$KEEP_LONGER = 1") .run() } @@ -272,14 +281,13 @@ class LogDatabase private constructor( writableDatabase.deleteAll(TABLE_NAME) } - private fun getSize(query: String?, args: Array?): Long { - readableDatabase.query(TABLE_NAME, arrayOf("SUM($SIZE)"), query, args, null, null, null).use { cursor -> - return if (cursor.moveToFirst()) { - cursor.getLong(0) - } else { - 0 - } - } + private fun getSize(query: String, args: Array, index: String): Long { + return readableDatabase + .select("SUM($SIZE)") + .from("$TABLE_NAME INDEXED BY $index") + .where(query, args) + .run() + .readToSingleLong(0) } private fun Cursor.toReader(): CursorReader {