Add more efficient message pagination index to eliminate temporary b-tree indexing

This commit is contained in:
Jack Kingsman
2026-02-28 21:00:16 -08:00
parent a55166989e
commit 727ac913de
3 changed files with 52 additions and 20 deletions
-1
View File
@@ -84,7 +84,6 @@ CREATE TABLE IF NOT EXISTS contact_name_history (
FOREIGN KEY (public_key) REFERENCES contacts(public_key)
);
CREATE INDEX IF NOT EXISTS idx_messages_conversation ON messages(type, conversation_key);
CREATE INDEX IF NOT EXISTS idx_messages_received ON messages(received_at);
CREATE UNIQUE INDEX IF NOT EXISTS idx_messages_dedup_null_safe
ON messages(type, conversation_key, text, COALESCE(sender_timestamp, 0));
+33
View File
@@ -240,6 +240,13 @@ async def run_migrations(conn: aiosqlite.Connection) -> int:
await set_version(conn, 29)
applied += 1
# Migration 30: Add pagination index, drop redundant idx_messages_conversation
if version < 30:
logger.info("Applying migration 30: add pagination index for message queries")
await _migrate_030_add_pagination_index(conn)
await set_version(conn, 30)
applied += 1
if applied > 0:
logger.info(
"Applied %d migration(s), schema now at version %d", applied, await get_version(conn)
@@ -1819,3 +1826,29 @@ async def _migrate_029_add_unread_covering_index(conn: aiosqlite.Connection) ->
"ON messages(type, conversation_key, outgoing, received_at)"
)
await conn.commit()
async def _migrate_030_add_pagination_index(conn: aiosqlite.Connection) -> None:
"""
Add a composite index for message pagination and drop the now-redundant
idx_messages_conversation.
The pagination query (ORDER BY received_at DESC, id DESC LIMIT N) hits a
temp B-tree sort without this index. With it, SQLite walks the index in
order and stops after N rows — critical for channels with 30K+ messages.
idx_messages_conversation(type, conversation_key) is a strict prefix of
both this index and idx_messages_unread_covering, so SQLite never picks it.
Dropping it saves ~6 MB and one index to maintain per INSERT.
"""
# Guard: table or columns may not exist in partial-schema test setups
cursor = await conn.execute("PRAGMA table_info(messages)")
columns = {row[1] for row in await cursor.fetchall()}
required = {"type", "conversation_key", "received_at", "id"}
if required <= columns:
await conn.execute(
"CREATE INDEX IF NOT EXISTS idx_messages_pagination "
"ON messages(type, conversation_key, received_at DESC, id DESC)"
)
await conn.execute("DROP INDEX IF EXISTS idx_messages_conversation")
await conn.commit()