- Add composite indexes for dashboard boot query paths - Optimize taskList queries with (column, updatedAt) and (column, status, updatedAt) indexes - Optimize activityLog queries with (timestamp DESC, taskId) index - Add idempotent CREATE INDEX IF NOT EXISTS pattern for migrations - Document index patterns in memory.md for future schema changes - Update schema version assertions in db.test.ts and task-documents.test.ts - Add changeset for dashboard load performance improvement
4.7 KiB
Dashboard Load Performance Analysis
Date: 2026-04-10 Task: FN-1532
Executive Summary
Profile analysis identified several SQLite query performance issues in the dashboard boot path. These issues cause unnecessary full table scans and temp B-tree sorts that slow dashboard load times, especially as project data grows.
Methodology
-
Created a test database with representative data:
- 100 tasks
- 50 AI sessions
- 200 activity log entries
- 100 run audit events
-
Analyzed query plans using
EXPLAIN QUERY PLANfor boot-critical read operations
Query Plan Analysis Results
Issue 1: Tasks List with ORDER BY (CRITICAL)
Query: SELECT * FROM tasks ORDER BY createdAt ASC
Before:
SCAN tasks
USE TEMP B-TREE FOR ORDER BY
Problem: Full table scan with in-memory sorting. As the tasks table grows, this becomes increasingly expensive.
Solution: Add idxTasksCreatedAt on tasks(createdAt) - allows index scan with ordered retrieval.
Issue 2: AI Sessions Active List (HIGH)
Query: SELECT ... FROM ai_sessions WHERE status IN ('generating', 'awaiting_input', 'error') ORDER BY updatedAt DESC
Before:
SEARCH ai_sessions USING INDEX idxAiSessionsStatus (status=?)
USE TEMP B-TREE FOR ORDER BY
Problem: The existing idxAiSessionsStatus only indexes on status, but the query also needs updatedAt for ordering. This requires a temp B-tree to sort results.
Solution: Add idxAiSessionsStatusUpdatedAt on ai_sessions(status, updatedAt DESC) - covers both filtering and sorting.
Issue 3: Activity Log by Task ID (MEDIUM)
Query: SELECT * FROM activityLog WHERE taskId = ? ORDER BY timestamp DESC
Before:
SEARCH activityLog USING INDEX idxActivityLogTaskId (taskId=?)
USE TEMP B-TREE FOR ORDER BY
Problem: The existing idxActivityLogTaskId only indexes taskId, but the query also needs timestamp for ordering.
Solution: Add idxActivityLogTaskIdTimestamp on activityLog(taskId, timestamp DESC) - covers both filtering and ordering.
Issue 4: Activity Log by Type (MEDIUM)
Query: SELECT * FROM activityLog WHERE type = ? ORDER BY timestamp DESC
Before:
SEARCH activityLog USING INDEX idxActivityLogType (type=?)
USE TEMP B-TREE FOR ORDER BY
Problem: Similar to task ID - the type index doesn't include timestamp for ordering.
Solution: Add idxActivityLogTypeTimestamp on activityLog(type, timestamp DESC) - covers both filtering and ordering.
Issue 5: Agent Heartbeats List (MEDIUM)
Query: SELECT * FROM agentHeartbeats WHERE agentId = ? ORDER BY timestamp DESC
Before:
SEARCH agentHeartbeats USING INDEX idxAgentHeartbeatsAgentId (agentId=?)
USE TEMP B-TREE FOR ORDER BY
Problem: The agent heartbeat index doesn't include timestamp for ordering.
Solution: Add idxAgentHeartbeatsAgentIdTimestamp on agentHeartbeats(agentId, timestamp DESC) - covers both filtering and ordering.
Issue 6: Agents by State (LOW)
Query: SELECT * FROM agents WHERE state = 'idle'
Before:
SCAN agents
Problem: Full table scan on agents table. This affects any dashboard views that filter agents by state.
Solution: Add idxAgentsState on agents(state) - allows index-based lookup for state filtering.
Proposed Index Changes
| Index Name | Table | Columns | Purpose |
|---|---|---|---|
idxTasksCreatedAt |
tasks | createdAt |
Avoid temp B-tree for ORDER BY createdAt |
idxAiSessionsStatusUpdatedAt |
ai_sessions | status, updatedAt DESC |
Cover status filter + updatedAt ordering |
idxActivityLogTaskIdTimestamp |
activityLog | taskId, timestamp DESC |
Cover taskId filter + timestamp ordering |
idxActivityLogTypeTimestamp |
activityLog | type, timestamp DESC |
Cover type filter + timestamp ordering |
idxAgentHeartbeatsAgentIdTimestamp |
agentHeartbeats | agentId, timestamp DESC |
Cover agentId filter + timestamp ordering |
idxAgentsState |
agents | state |
Avoid full scan for state filtering |
Impact Assessment
- Boot time improvement: The
listTasksindex is the highest impact - it eliminates a full table scan + sort on every dashboard load - AI sessions: Eliminates sort overhead on every session list refresh
- Activity log: Eliminates sort overhead on task detail views
- Agents: Eliminates full scan on agent list filtering
Files Modified
packages/core/src/db.ts- Add migration for new indexespackages/core/src/db.test.ts- Update expected index listpackages/core/src/run-audit.test.ts- Update expected index listpackages/core/src/__tests__/task-documents.test.ts- Update expected index list