Speed up roadmap rendering when milestones are shown
What does this MR do and why?
MilestoneTimeline creates a MilestoneItem for every milestone in every column, though only the one in the milestone's start column draws. By week over three years, 100 milestones make 16,200 instances, and on a production-mode build in Chromium the longest task takes 2.1 to 2.3 s.
Each MilestoneItem is now created once, in its start column, and the longest task drops to 0.11 to 0.13 s. EpicItem has rendered EpicItemTimeline this way since !50652 (merged), where @euko offered to do the same for milestones if the gain was meaningful.
milestone_timeline.vue repeats the three hasStartDateFor* mixin checks to find the start column. Moving them into shared helpers touches epic code, so it is tracked in #632774.
Render path
flowchart TB
subgraph before["master: per column"]
direction TB
A1["MilestoneTimeline<br/>T timeline columns"] --> A2["each column: v-for over all M milestones"]
A2 --> A3["M x T MilestoneItem instances"]
A3 --> A4{"hasStartDate<br/>in this column?"}
A4 -->|"1 of T"| A5["draw the bar"]
A4 -->|"T - 1 of T"| A6["empty 32 px wrapper<br/>keeps the row"]
end
subgraph after["this MR: once"]
direction TB
B1["MilestoneTimeline<br/>T timeline columns"] --> B2["milestonesByTimeframeIndex:<br/>start column of each milestone"]
B2 --> B3["M MilestoneItem instances,<br/>each in its start column"]
B3 --> B4["absolute, top = row x 32 px<br/>bar drawn as before"]
B1 --> B5["one spacer per column,<br/>M x 32 px tall"]
end
before ~~~ afterReferences
- Related to #631411 (closed)
- !50652 (comment 478023600)
Screenshots or screen recordings
No visual change: in Chromium, with the same 100 milestones seeded, the roadmap renders identical pixels in all six layouts (by month and by week this year, by week this quarter, and by quarter, month and week within 3 years), and hovering a milestone shows the same popover. Firefox renders identical pixels by month this year and by week within 3 years.
| Before | After |
|---|---|
![]() |
![]() |
How to set up and validate locally
-
In a group with an epic dated this year, create 100 milestones from the Rails console, with
<group-path>replaced by the group's path:group = Group.find_by_full_path('<group-path>'); 100.times { |i| group.milestones.create!(title: "Roadmap #{i}", start_date: Date.current.beginning_of_year + (3 * i).days, due_date: Date.current.beginning_of_year + (3 * i + 14).days) } -
Open the group's roadmap, and in Settings choose Within 3 years and By week.
-
Each milestone renders once at its start date, with one
.timeline-bar-wrapperper milestone.
MR acceptance checklist
Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

