Skip to content

Commit b904d0c

Browse files
fix: return only the first workspace agent script timing per script (#16203)
Fixes #16124 If a workspace agent crashes, it is possible for any startup scripts to be ran again. This PR makes it so that the `GetWorkspaceAgentScriptTimingsByBuildID` query only returns the first timing recorded per-script.
1 parent 075269a commit b904d0c

File tree

6 files changed

+77
-10
lines changed

6 files changed

+77
-10
lines changed

coderd/database/dbgen/dbgen.go

+11-3
Original file line numberDiff line numberDiff line change
@@ -209,9 +209,17 @@ func WorkspaceAgentScript(t testing.TB, db database.Store, orig database.Workspa
209209
return scripts[0]
210210
}
211211

212-
func WorkspaceAgentScriptTimings(t testing.TB, db database.Store, script database.WorkspaceAgentScript, count int) []database.WorkspaceAgentScriptTiming {
213-
timings := make([]database.WorkspaceAgentScriptTiming, count)
214-
for i := range count {
212+
func WorkspaceAgentScripts(t testing.TB, db database.Store, count int, orig database.WorkspaceAgentScript) []database.WorkspaceAgentScript {
213+
scripts := make([]database.WorkspaceAgentScript, 0, count)
214+
for range count {
215+
scripts = append(scripts, WorkspaceAgentScript(t, db, orig))
216+
}
217+
return scripts
218+
}
219+
220+
func WorkspaceAgentScriptTimings(t testing.TB, db database.Store, scripts []database.WorkspaceAgentScript) []database.WorkspaceAgentScriptTiming {
221+
timings := make([]database.WorkspaceAgentScriptTiming, len(scripts))
222+
for i, script := range scripts {
215223
timings[i] = WorkspaceAgentScriptTiming(t, db, database.WorkspaceAgentScriptTiming{
216224
ScriptID: script.ID,
217225
})

coderd/database/dbmem/dbmem.go

+9
Original file line numberDiff line numberDiff line change
@@ -6018,6 +6018,15 @@ func (q *FakeQuerier) GetWorkspaceAgentScriptTimingsByBuildID(ctx context.Contex
60186018
WorkspaceAgentName: agent.Name,
60196019
})
60206020
}
6021+
6022+
// We want to only return the first script run for each Script ID.
6023+
slices.SortFunc(rows, func(a, b database.GetWorkspaceAgentScriptTimingsByBuildIDRow) int {
6024+
return a.StartedAt.Compare(b.StartedAt)
6025+
})
6026+
rows = slices.CompactFunc(rows, func(e1, e2 database.GetWorkspaceAgentScriptTimingsByBuildIDRow) bool {
6027+
return e1.ScriptID == e2.ScriptID
6028+
})
6029+
60216030
return rows, nil
60226031
}
60236032

coderd/database/queries.sql.go

+2-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/queries/workspaceagents.sql

+3-2
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ RETURNING workspace_agent_script_timings.*;
304304

305305
-- name: GetWorkspaceAgentScriptTimingsByBuildID :many
306306
SELECT
307-
workspace_agent_script_timings.*,
307+
DISTINCT ON (workspace_agent_script_timings.script_id) workspace_agent_script_timings.*,
308308
workspace_agent_scripts.display_name,
309309
workspace_agents.id as workspace_agent_id,
310310
workspace_agents.name as workspace_agent_name
@@ -313,4 +313,5 @@ INNER JOIN workspace_agent_scripts ON workspace_agent_scripts.id = workspace_age
313313
INNER JOIN workspace_agents ON workspace_agents.id = workspace_agent_scripts.workspace_agent_id
314314
INNER JOIN workspace_resources ON workspace_resources.id = workspace_agents.resource_id
315315
INNER JOIN workspace_builds ON workspace_builds.job_id = workspace_resources.job_id
316-
WHERE workspace_builds.id = $1;
316+
WHERE workspace_builds.id = $1
317+
ORDER BY workspace_agent_script_timings.script_id, workspace_agent_script_timings.started_at;

coderd/workspacebuilds_test.go

+50-2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"github.com/stretchr/testify/require"
1515
"go.opentelemetry.io/otel"
1616
"go.opentelemetry.io/otel/propagation"
17+
"golang.org/x/exp/slices"
1718
"golang.org/x/xerrors"
1819

1920
"cdr.dev/slog"
@@ -1421,6 +1422,47 @@ func TestWorkspaceBuildTimings(t *testing.T) {
14211422
}
14221423
})
14231424

1425+
t.Run("MultipleTimingsForSameAgentScript", func(t *testing.T) {
1426+
t.Parallel()
1427+
1428+
// Given: a build with multiple timings for the same script
1429+
build := makeBuild(t)
1430+
resource := dbgen.WorkspaceResource(t, db, database.WorkspaceResource{
1431+
JobID: build.JobID,
1432+
})
1433+
agent := dbgen.WorkspaceAgent(t, db, database.WorkspaceAgent{
1434+
ResourceID: resource.ID,
1435+
})
1436+
script := dbgen.WorkspaceAgentScript(t, db, database.WorkspaceAgentScript{
1437+
WorkspaceAgentID: agent.ID,
1438+
})
1439+
timings := make([]database.WorkspaceAgentScriptTiming, 3)
1440+
scriptStartedAt := dbtime.Now()
1441+
for i := range timings {
1442+
timings[i] = dbgen.WorkspaceAgentScriptTiming(t, db, database.WorkspaceAgentScriptTiming{
1443+
StartedAt: scriptStartedAt,
1444+
EndedAt: scriptStartedAt.Add(1 * time.Minute),
1445+
ScriptID: script.ID,
1446+
})
1447+
1448+
// Add an hour to the previous "started at" so we can
1449+
// reliably differentiate the scripts from each other.
1450+
scriptStartedAt = scriptStartedAt.Add(1 * time.Hour)
1451+
}
1452+
1453+
// When: fetching timings for the build
1454+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
1455+
t.Cleanup(cancel)
1456+
res, err := client.WorkspaceBuildTimings(ctx, build.ID)
1457+
require.NoError(t, err)
1458+
1459+
// Then: return a response with the first agent script timing
1460+
require.Len(t, res.AgentScriptTimings, 1)
1461+
1462+
require.Equal(t, timings[0].StartedAt.UnixMilli(), res.AgentScriptTimings[0].StartedAt.UnixMilli())
1463+
require.Equal(t, timings[0].EndedAt.UnixMilli(), res.AgentScriptTimings[0].EndedAt.UnixMilli())
1464+
})
1465+
14241466
t.Run("AgentScriptTimings", func(t *testing.T) {
14251467
t.Parallel()
14261468

@@ -1432,10 +1474,10 @@ func TestWorkspaceBuildTimings(t *testing.T) {
14321474
agent := dbgen.WorkspaceAgent(t, db, database.WorkspaceAgent{
14331475
ResourceID: resource.ID,
14341476
})
1435-
script := dbgen.WorkspaceAgentScript(t, db, database.WorkspaceAgentScript{
1477+
scripts := dbgen.WorkspaceAgentScripts(t, db, 5, database.WorkspaceAgentScript{
14361478
WorkspaceAgentID: agent.ID,
14371479
})
1438-
agentScriptTimings := dbgen.WorkspaceAgentScriptTimings(t, db, script, 5)
1480+
agentScriptTimings := dbgen.WorkspaceAgentScriptTimings(t, db, scripts)
14391481

14401482
// When: fetching timings for the build
14411483
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
@@ -1445,6 +1487,12 @@ func TestWorkspaceBuildTimings(t *testing.T) {
14451487

14461488
// Then: return a response with the expected timings
14471489
require.Len(t, res.AgentScriptTimings, 5)
1490+
slices.SortFunc(res.AgentScriptTimings, func(a, b codersdk.AgentScriptTiming) int {
1491+
return a.StartedAt.Compare(b.StartedAt)
1492+
})
1493+
slices.SortFunc(agentScriptTimings, func(a, b database.WorkspaceAgentScriptTiming) int {
1494+
return a.StartedAt.Compare(b.StartedAt)
1495+
})
14481496
for i := range res.AgentScriptTimings {
14491497
timingRes := res.AgentScriptTimings[i]
14501498
genTiming := agentScriptTimings[i]

coderd/workspaces_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -3757,10 +3757,10 @@ func TestWorkspaceTimings(t *testing.T) {
37573757
agent := dbgen.WorkspaceAgent(t, db, database.WorkspaceAgent{
37583758
ResourceID: resource.ID,
37593759
})
3760-
script := dbgen.WorkspaceAgentScript(t, db, database.WorkspaceAgentScript{
3760+
scripts := dbgen.WorkspaceAgentScripts(t, db, 3, database.WorkspaceAgentScript{
37613761
WorkspaceAgentID: agent.ID,
37623762
})
3763-
dbgen.WorkspaceAgentScriptTimings(t, db, script, 3)
3763+
dbgen.WorkspaceAgentScriptTimings(t, db, scripts)
37643764

37653765
// When: fetching the timings
37663766
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)

0 commit comments

Comments
 (0)