Optimize bd list: replace N+1 label queries with bulk fetch
Problem: In direct mode, bd list was making a separate GetLabels() call for each issue when displaying labels. With 538 issues, this resulted in 538 separate database queries. While investigating the reported 5+ second slowness, discovered this N+1 query issue that would impact performance with many issues. Solution: 1. Added GetLabelsForIssues(issueIDs []string) to Storage interface 2. Implemented bulk fetch in SQLite (already existed, now exposed) 3. Implemented bulk fetch in MemoryStorage 4. Updated list.go to fetch all labels in single query Changes: - internal/storage/storage.go: Add GetLabelsForIssues to interface - internal/storage/memory/memory.go: Implement GetLabelsForIssues - cmd/bd/list.go: Use bulk fetching in all output modes Impact: Eliminates N queries for labels, replacing with 1 bulk query. This optimization applies to direct mode only (daemon mode already uses bulk operations via RPC). Note: The reported 5s slowness was actually caused by daemon auto-start timeout. Use --no-daemon flag or run 'bd migrate --update-repo-id' to resolve the legacy database issue causing daemon startup failures. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -339,18 +339,19 @@ var listCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
if jsonOutput {
|
||||
// Populate labels for JSON output
|
||||
for _, issue := range issues {
|
||||
issue.Labels, _ = store.GetLabels(ctx, issue.ID)
|
||||
}
|
||||
|
||||
// Get dependency counts in bulk (single query instead of N queries)
|
||||
// Get labels and dependency counts in bulk (single query instead of N queries)
|
||||
issueIDs := make([]string, len(issues))
|
||||
for i, issue := range issues {
|
||||
issueIDs[i] = issue.ID
|
||||
}
|
||||
labelsMap, _ := store.GetLabelsForIssues(ctx, issueIDs)
|
||||
depCounts, _ := store.GetDependencyCounts(ctx, issueIDs)
|
||||
|
||||
// Populate labels for JSON output
|
||||
for _, issue := range issues {
|
||||
issue.Labels = labelsMap[issue.ID]
|
||||
}
|
||||
|
||||
// Build response with counts
|
||||
issuesWithCounts := make([]*types.IssueWithCounts, len(issues))
|
||||
for i, issue := range issues {
|
||||
@@ -368,12 +369,18 @@ var listCmd = &cobra.Command{
|
||||
return
|
||||
}
|
||||
|
||||
// Load labels in bulk for display
|
||||
issueIDs := make([]string, len(issues))
|
||||
for i, issue := range issues {
|
||||
issueIDs[i] = issue.ID
|
||||
}
|
||||
labelsMap, _ := store.GetLabelsForIssues(ctx, issueIDs)
|
||||
|
||||
if longFormat {
|
||||
// Long format: multi-line with details
|
||||
fmt.Printf("\nFound %d issues:\n\n", len(issues))
|
||||
for _, issue := range issues {
|
||||
// Load labels for display
|
||||
labels, _ := store.GetLabels(ctx, issue.ID)
|
||||
labels := labelsMap[issue.ID]
|
||||
|
||||
fmt.Printf("%s [P%d] [%s] %s\n", issue.ID, issue.Priority, issue.IssueType, issue.Status)
|
||||
fmt.Printf(" %s\n", issue.Title)
|
||||
@@ -388,8 +395,7 @@ var listCmd = &cobra.Command{
|
||||
} else {
|
||||
// Compact format: one line per issue
|
||||
for _, issue := range issues {
|
||||
// Load labels for display
|
||||
labels, _ := store.GetLabels(ctx, issue.ID)
|
||||
labels := labelsMap[issue.ID]
|
||||
|
||||
labelsStr := ""
|
||||
if len(labels) > 0 {
|
||||
|
||||
Reference in New Issue
Block a user