perf: Add composite index on dependencies(depends_on_id, type)
The hierarchical blocking query recursively joins on dependencies with a type filter. Without a composite index, SQLite must scan all dependencies for a given depends_on_id and filter by type afterward. With 10k+ issues and many dependencies per issue, this could cause noticeable slowdowns in ready work calculations. Changes: - Added idx_dependencies_depends_on_type composite index to schema - Added automatic migration for existing databases - Index creation is silent and requires no user intervention The recursive CTE now efficiently seeks (depends_on_id, type) pairs directly instead of post-filtering. Resolves: bd-59 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -39,6 +39,7 @@ CREATE TABLE IF NOT EXISTS dependencies (
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_dependencies_issue ON dependencies(issue_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_dependencies_depends_on ON dependencies(depends_on_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_dependencies_depends_on_type ON dependencies(depends_on_id, type);
|
||||
|
||||
-- Labels table
|
||||
CREATE TABLE IF NOT EXISTS labels (
|
||||
|
||||
@@ -60,6 +60,11 @@ func New(path string) (*SQLiteStorage, error) {
|
||||
return nil, fmt.Errorf("failed to migrate external_ref column: %w", err)
|
||||
}
|
||||
|
||||
// Migrate existing databases to add composite index on dependencies
|
||||
if err := migrateCompositeIndexes(db); err != nil {
|
||||
return nil, fmt.Errorf("failed to migrate composite indexes: %w", err)
|
||||
}
|
||||
|
||||
return &SQLiteStorage{
|
||||
db: db,
|
||||
}, nil
|
||||
@@ -201,6 +206,36 @@ func migrateExternalRefColumn(db *sql.DB) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// migrateCompositeIndexes checks if composite indexes exist and creates them if missing.
|
||||
// This ensures existing databases get performance optimizations from new indexes.
|
||||
func migrateCompositeIndexes(db *sql.DB) error {
|
||||
// Check if idx_dependencies_depends_on_type exists
|
||||
var indexName string
|
||||
err := db.QueryRow(`
|
||||
SELECT name FROM sqlite_master
|
||||
WHERE type='index' AND name='idx_dependencies_depends_on_type'
|
||||
`).Scan(&indexName)
|
||||
|
||||
if err == sql.ErrNoRows {
|
||||
// Index doesn't exist, create it
|
||||
_, err := db.Exec(`
|
||||
CREATE INDEX idx_dependencies_depends_on_type ON dependencies(depends_on_id, type)
|
||||
`)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create composite index idx_dependencies_depends_on_type: %w", err)
|
||||
}
|
||||
// Index created successfully
|
||||
return nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check for composite index: %w", err)
|
||||
}
|
||||
|
||||
// Index exists, no migration needed
|
||||
return nil
|
||||
}
|
||||
|
||||
// getNextIDForPrefix atomically generates the next ID for a given prefix
|
||||
// Uses the issue_counters table for atomic, cross-process ID generation
|
||||
func (s *SQLiteStorage) getNextIDForPrefix(ctx context.Context, prefix string) (int, error) {
|
||||
|
||||
Reference in New Issue
Block a user