perf: index chat messages by their room
The query in the previous commit reads the newest line of every room the user is in, once per room, and Room re-runs the whole thing every time a message lands anywhere. Without an index on `ChatMessage.chatRoomId` each of those lookups is a scan of every message on the device: work that grows with the entire history rather than with the room, on the hot path of every arriving message. A device with ten rooms and a few thousand messages does tens of thousands of row reads to redraw a list whose visible change is one line of text. Room has wanted this index since the foreign key was declared and has said so on every build -- `chatRoomId column references a foreign key but it is not part of an index. This may trigger full table scans whenever parent table is modified` -- which is the same warning it still emits for a dozen other `chatRoomId` columns. Those stay as they are; this one now has a reader that makes it matter. **Schema v15, and Room writes the migration itself.** Adding an index changes no columns and moves no rows, which is one of the shapes `AutoMigration` handles without a spec, so this is an entry in the list rather than another manual migration alongside MIGRATION_13_14. The generated 15.json differs from 14.json in exactly one place, checked rather than assumed: `index_ChatMessage_chatRoomId` appears on ChatMessage, and no table's fields, createSql or other indices move at all. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -177,7 +177,7 @@ val GENESIS_AT = Instant.fromEpochMilliseconds(1231006505000L)
|
||||
UnsignedNostrEvent::class,
|
||||
Zap::class
|
||||
],
|
||||
version = 14,
|
||||
version = 15,
|
||||
autoMigrations = [
|
||||
// v2 only adds the DkgSession/DkgParticipantMessage tables, so Room can
|
||||
// generate the migration itself — nothing existing changes shape.
|
||||
@@ -255,6 +255,12 @@ val GENESIS_AT = Instant.fromEpochMilliseconds(1231006505000L)
|
||||
// meanings would have met. Room can rename a column and cannot rewrite the
|
||||
// rows in the same breath, so this is a manual migration passed to the
|
||||
// builder rather than an entry here. See MIGRATION_13_14.
|
||||
//
|
||||
// v15 adds an index on ChatMessage.chatRoomId. No column changes and no
|
||||
// rows move -- the chat list now looks up each room's newest line, and
|
||||
// without the index that lookup reads every message on the device. Room
|
||||
// creates an index on its own.
|
||||
AutoMigration(from = 14, to = 15),
|
||||
]
|
||||
)
|
||||
@ColumnTypeConverters(MantraConverters::class)
|
||||
|
||||
@@ -2,6 +2,7 @@ package press.mantra.compose.database.model
|
||||
|
||||
import androidx.room3.Entity
|
||||
import androidx.room3.ForeignKey
|
||||
import androidx.room3.Index
|
||||
import androidx.room3.PrimaryKey
|
||||
import com.vitorpamplona.quartz.experimental.nip82SoftwareApps.application.name
|
||||
import press.mantra.compose.database.MantraDatabase
|
||||
@@ -69,6 +70,11 @@ import kotlin.time.Instant
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
),
|
||||
],
|
||||
// The chat list reads the newest line of every room the user is in, once per
|
||||
// room, and re-reads all of them each time a message lands anywhere. Without
|
||||
// this that is a scan of every message this device holds, per room, per
|
||||
// arrival -- work that grows with the whole history rather than with the room.
|
||||
indices = [Index("chatRoomId")],
|
||||
)
|
||||
data class ChatMessage(
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
|
||||
Reference in New Issue
Block a user