Feature #44409
openSkip the nested set self-join in Issue.load_visible_total_spent_hours when no issues have subtasks
Description
Issue.load_visible_total_spent_hours always self-joins the issues table on the nested set columns to find subtasks, even when none of the given issues has subtasks. For an issue without subtasks, the total spent time is the same as its own spent time, so the join is unnecessary.
The attached patch checks whether any of the given issues has subtasks. If none does, it sums the visible time entries directly by issue_id. Otherwise, the existing query runs unchanged. Visibility rules are preserved because the new query still uses TimeEntry.visible(user).
The patch improves performance in two ways:
1. No nested set self-join. When there are no subtasks, the join only matches each issue itself. Skipping it avoids both the SQL join and the Arel needed to build it.
2. Query cache hit. IssuesController#show, #index, and IssueQuery#issues call Issue.load_visible_spent_hours immediately before load_visible_total_spent_hours. The new query has the same form as the one in load_visible_spent_hours, so the second call is served from the Rails query cache without executing another SQL query.
Measurements on SQLite, using the median of 300 alternating runs. "Cached" means load_visible_spent_hours was called first, as in the controllers. All issues have no subtasks.
| Case | Current | Patched | Change |
|---|---|---|---|
| 1 issue, uncached | 0.289 ms | 0.210 ms | -27% |
| 1 issue, cached | 0.302 ms | 0.181 ms | -40% |
| 100 issues, uncached | 0.766 ms | 0.760 ms | -1% |
| 100 issues, cached | 0.735 ms | 0.629 ms | -14% |
The gain is largest on the issue page (single issue). On the issue list it comes almost entirely from the query cache hit.
Files
No data to display