Skip to content

Commit 1fbce53

Browse files
authored
perf(db): optimize in-progress builds queries by filtering on b.team_id (#1987)
1 parent 2c051f6 commit 1fbce53

File tree

5 files changed

+63
-45
lines changed

5 files changed

+63
-45
lines changed

packages/api/internal/handlers/admin_cancel_team_builds.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ func (a *APIStore) PostAdminTeamsTeamIDBuildsCancel(c *gin.Context, teamID uuid.
2323

2424
logger.L().Info(ctx, "Admin cancelling all builds for team", logger.WithTeamID(teamID.String()))
2525

26-
builds, err := a.sqlcDB.GetCancellableTemplateBuildsByTeam(ctx, teamID)
26+
builds, err := a.sqlcDB.GetCancellableTemplateBuildsByTeam(ctx, &teamID)
2727
if err != nil {
2828
a.sendAPIStoreError(c, http.StatusInternalServerError, "Failed to get builds")
2929

packages/api/internal/template/register_build.go

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,19 @@ func RegisterBuild(
6060
ctx, span := tracer.Start(ctx, "register build")
6161
defer span.End()
6262

63+
// Add default tag if no tags are present
64+
tags := data.Tags
65+
if len(tags) == 0 {
66+
tags = []string{id.DefaultTag}
67+
}
68+
6369
// This is a simple implementation of concurrency limit
6470
// It does not guarantee that the limit is not exceeded, but it should be good enough for now (considering overall low number of total builds)
65-
templateIDs, err := db.GetInProgressTemplateBuildsByTeam(ctx, data.Team.ID)
71+
otherBuildCount, err := db.GetInProgressTemplateBuildsByTeam(ctx, queries.GetInProgressTemplateBuildsByTeamParams{
72+
TeamID: &data.Team.ID,
73+
ExcludeTemplateID: data.TemplateID,
74+
ExcludeTags: tags,
75+
})
6676
if err != nil {
6777
return nil, &api.APIError{
6878
Err: err,
@@ -71,13 +81,8 @@ func RegisterBuild(
7181
}
7282
}
7383

74-
// Exclude the current build if it's a rebuild (it will be canceled)
75-
teamBuildsExcludingCurrent := gutils.Filter(templateIDs, func(templateID string) bool {
76-
return templateID != data.TemplateID
77-
})
78-
7984
totalConcurrentTemplateBuilds := data.Team.Limits.BuildConcurrency
80-
if len(teamBuildsExcludingCurrent) >= int(totalConcurrentTemplateBuilds) {
85+
if otherBuildCount >= totalConcurrentTemplateBuilds {
8186
telemetry.ReportError(ctx, "team has reached max concurrent template builds", nil, telemetry.WithTeamID(data.Team.ID.String()), attribute.Int64("total.concurrent_template_builds", totalConcurrentTemplateBuilds))
8287

8388
return nil, &api.APIError{
@@ -101,12 +106,6 @@ func RegisterBuild(
101106
}
102107
}
103108

104-
// Add default tag if no tags are present
105-
tags := data.Tags
106-
if len(tags) == 0 {
107-
tags = []string{id.DefaultTag}
108-
}
109-
110109
telemetry.SetAttributes(ctx,
111110
attribute.String("env.team.id", data.Team.ID.String()),
112111
attribute.String("env.team.name", data.Team.Name),
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
-- +goose Up
2+
-- +goose NO TRANSACTION
3+
4+
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_env_builds_team_status_group
5+
ON public.env_builds (team_id, status_group);
6+
7+
-- +goose Down
8+
-- +goose NO TRANSACTION
9+
10+
DROP INDEX CONCURRENTLY IF EXISTS idx_env_builds_team_status_group;

packages/db/queries/builds/get_inprogress_builds.sql

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,27 @@ WHERE b.status_group IN ('pending', 'in_progress')
88
AND e.source = 'template'
99
ORDER BY b.id, b.created_at DESC;
1010

11-
-- name: GetInProgressTemplateBuildsByTeam :many
12-
SELECT DISTINCT ON (b.id) e.id as template_id
11+
-- name: GetInProgressTemplateBuildsByTeam :one
12+
SELECT COUNT(DISTINCT b.id) as build_count
1313
FROM public.env_builds b
1414
JOIN public.env_build_assignments eba ON eba.build_id = b.id
1515
JOIN public.envs e ON e.id = eba.env_id
16-
WHERE e.team_id = $1 AND b.status_group IN ('pending', 'in_progress') AND e.source = 'template'
17-
ORDER BY b.id, b.created_at DESC;
16+
WHERE b.team_id = @team_id
17+
AND b.status_group IN ('pending', 'in_progress')
18+
AND e.source = 'template'
19+
AND NOT EXISTS (
20+
SELECT 1 FROM public.env_build_assignments exc
21+
WHERE exc.build_id = b.id
22+
AND exc.env_id = @exclude_template_id
23+
AND exc.tag = ANY(@exclude_tags::text[])
24+
);
1825

1926
-- name: GetCancellableTemplateBuildsByTeam :many
2027
SELECT DISTINCT ON (b.id) b.id as build_id, e.id as template_id, e.cluster_id, b.cluster_node_id
2128
FROM public.env_builds b
2229
JOIN public.env_build_assignments eba ON eba.build_id = b.id
2330
JOIN public.envs e ON e.id = eba.env_id
24-
WHERE e.team_id = $1 AND b.status_group IN ('pending', 'in_progress')
31+
WHERE b.team_id = $1
32+
AND b.status_group IN ('pending', 'in_progress')
2533
AND e.source = 'template'
26-
ORDER BY b.id, b.created_at DESC;
34+
ORDER BY b.id;

packages/db/queries/get_inprogress_builds.sql.go

Lines changed: 26 additions & 25 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)