Files
coder/coderd
J. Scott Miller 62f4afbb60 perf(coderd): build the workspace build fan-out maps once per batch (#28074)
`convertWorkspaceBuild` rebuilt seven maps from the caller's global
slices on every call and rescanned the provisioner daemon rows to filter
by job ID. `convertWorkspaceBuilds` calls it once per build with
identical slices, so map construction cost `O(builds x rows)` where
`O(rows)` suffices — quadratic in the number of workspaces on `GET
/api/v2/workspaces`.

The maps move into a `workspaceBuildIndex` built once per batch, keyed
exactly as before and now including daemons by job ID.
`convertWorkspaceBuild` takes the index instead of eight slices. Its
parent already hoisted `workspaceByID`, `jobByID`, and
`templateVersionByID` out of the same loop; this makes the rest
consistent.

Agents are sorted while the index is built, so a resource read by
several builds is sorted once rather than once per build. Same
comparator over the same rows, so the order is unchanged;
`TestConvertWorkspaceBuildsAgentOrder` covers it.

`BenchmarkConvertWorkspaceBuilds`, 5 resources x 2 agents x 4 apps per
build:

| builds | ns/op | B/op | allocs/op |
| --- | --- | --- | --- |
| 1 | 48.3k -> 48.1k | 121k -> 125k | 342 -> 359 |
| 25 | 12.7M -> 1.34M | 38.0MB -> 3.2MB | 69,621 -> 8,347 |
| 100 | 186M -> 5.67M | 596MB -> 13.0MB | 1,024,941 -> 33,080 |

Single-build conversion is a wash (one extra struct allocation); the
quadratic term is gone.

Addresses the map-allocation half of PLAT-386 / #27205. Bounding the
page size is separate (#28040) and does not remove this cost: at 100
workspaces per page it is still 100 passes over every resource, agent,
app, script, log source, status, and daemon row in the page.

---

Created with Coder Agents on behalf of @jscottmiller.
2026-08-18 11:33:01 -05:00
..