Ensure various log database indexes are used.

This commit is contained in:
Greyson Parrelli
2025-08-07 11:06:44 -04:00
parent d0b53f21bd
commit 1a531548e8

View File

@@ -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<String> {
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<String>?): 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<String>, 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 {