* perf: add composite index on session_id and timestamp to chat_messages Fixes a high-severity performance issue (PERF-08) described in issue #276. A full table scan and in-memory sort were previously performed during each session-switch or cache-load due to a missing index. By adding a composite index on `(session_id, timestamp)` in the `ChatMessageEntity` and bumping the database version from 1 to 2 with a migration, the query for loading messages now avoids the full table scan, making chat loading latency independent of total message count. Changes made: - Added `indices` to `@Entity` in `ChatMessageEntity.kt` for `session_id` and `timestamp`. - Bumped `version` to 2 in `HermesDatabase.kt`. - Added explicit migration `MIGRATION_1_2` to create the index if it doesn't exist and added it to `.addMigrations()`. Fixes #276 * style: fix ktlint formatting errors in HermesDatabase.kt Fixed `standard:argument-list-wrapping` and `standard:max-line-length` ktlint errors in `app/src/main/java/com/m57/hermescontrol/data/local/HermesDatabase.kt`. This ensures that the repository meets standard code style guidelines and fixes the GitHub CI build check failures.
This commit is contained in:
3 files changed
+102
-3
No files matched your search
@@ -0,0 +1,83 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 2,
|
||||
"identityHash": "b0a33c5fd32118982236698f395f519b",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "chat_messages",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `session_id` TEXT NOT NULL, `role` TEXT NOT NULL, `content` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `tool_name` TEXT, `tool_status` TEXT, `is_streaming` INTEGER NOT NULL, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "sessionId",
|
||||
"columnName": "session_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "role",
|
||||
"columnName": "role",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "content",
|
||||
"columnName": "content",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "timestamp",
|
||||
"columnName": "timestamp",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "toolName",
|
||||
"columnName": "tool_name",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "toolStatus",
|
||||
"columnName": "tool_status",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isStreaming",
|
||||
"columnName": "is_streaming",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_chat_messages_session_id_timestamp",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"session_id",
|
||||
"timestamp"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_chat_messages_session_id_timestamp` ON `${TABLE_NAME}` (`session_id`, `timestamp`)"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'b0a33c5fd32118982236698f395f519b')"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,10 @@ import androidx.room.PrimaryKey
|
||||
* thread. The [timestamp] field preserves original ordering even if Room
|
||||
* reorders internally.
|
||||
*/
|
||||
@Entity(tableName = "chat_messages")
|
||||
@Entity(
|
||||
tableName = "chat_messages",
|
||||
indices = [androidx.room.Index(value = ["session_id", "timestamp"])],
|
||||
)
|
||||
data class ChatMessageEntity(
|
||||
@PrimaryKey
|
||||
val id: String,
|
||||
|
||||
@@ -4,11 +4,13 @@ import android.content.Context
|
||||
import androidx.room.Database
|
||||
import androidx.room.Room
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import com.m57.hermescontrol.BuildConfig
|
||||
|
||||
@Database(
|
||||
entities = [ChatMessageEntity::class],
|
||||
version = 1,
|
||||
version = 2,
|
||||
exportSchema = true,
|
||||
)
|
||||
abstract class HermesDatabase : RoomDatabase() {
|
||||
@@ -18,6 +20,16 @@ abstract class HermesDatabase : RoomDatabase() {
|
||||
@Volatile
|
||||
private var instance: HermesDatabase? = null
|
||||
|
||||
private val MIGRATION_1_2 =
|
||||
object : Migration(1, 2) {
|
||||
override fun migrate(db: SupportSQLiteDatabase) {
|
||||
db.execSQL(
|
||||
"CREATE INDEX IF NOT EXISTS `index_chat_messages_session_id_timestamp` " +
|
||||
"ON `chat_messages` (`session_id`, `timestamp`)",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun get(context: Context): HermesDatabase =
|
||||
instance ?: synchronized(this) {
|
||||
instance ?: Room
|
||||
@@ -25,7 +37,8 @@ abstract class HermesDatabase : RoomDatabase() {
|
||||
context.applicationContext,
|
||||
HermesDatabase::class.java,
|
||||
"hermes_control.db",
|
||||
).apply {
|
||||
).addMigrations(MIGRATION_1_2)
|
||||
.apply {
|
||||
if (BuildConfig.DEBUG) {
|
||||
fallbackToDestructiveMigration(false)
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user