Skip to content

Commit efb87ed

Browse files
committed
add test to prevent deadlock regression
1 parent 177affb commit efb87ed

File tree

1 file changed

+81
-0
lines changed

1 file changed

+81
-0
lines changed

enterprise/coderd/workspaces_test.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,14 @@ import (
1616
"github.com/coder/coder/v2/coderd/autobuild"
1717
"github.com/coder/coder/v2/coderd/coderdtest"
1818
"github.com/coder/coder/v2/coderd/database"
19+
"github.com/coder/coder/v2/coderd/database/dbtestutil"
1920
"github.com/coder/coder/v2/coderd/rbac"
2021
agplschedule "github.com/coder/coder/v2/coderd/schedule"
2122
"github.com/coder/coder/v2/coderd/schedule/cron"
2223
"github.com/coder/coder/v2/coderd/util/ptr"
2324
"github.com/coder/coder/v2/codersdk"
25+
entaudit "github.com/coder/coder/v2/enterprise/audit"
26+
"github.com/coder/coder/v2/enterprise/audit/backends"
2427
"github.com/coder/coder/v2/enterprise/coderd/coderdenttest"
2528
"github.com/coder/coder/v2/enterprise/coderd/license"
2629
"github.com/coder/coder/v2/enterprise/coderd/schedule"
@@ -309,6 +312,84 @@ func TestWorkspaceAutobuild(t *testing.T) {
309312
require.True(t, ws.LastUsedAt.After(lastUsedAt))
310313
})
311314

315+
// This test serves as a regression prevention for generating
316+
// audit logs in the same transaction the transition workspaces to
317+
// the dormant state. The auditor that is passed to autobuild does
318+
// not use the transaction when inserting an audit log which can
319+
// cause a deadlock.
320+
t.Run("NoDeadlock", func(t *testing.T) {
321+
t.Parallel()
322+
323+
if !dbtestutil.WillUsePostgres() {
324+
t.Skipf("Skipping non-postgres run")
325+
}
326+
327+
var (
328+
ticker = make(chan time.Time)
329+
statCh = make(chan autobuild.Stats)
330+
inactiveTTL = time.Minute
331+
)
332+
333+
const (
334+
maxConns = 3
335+
numWorkspaces = maxConns * 5
336+
)
337+
// This is a bit bizarre but necessary so that we can
338+
// initialize our coderd with a real auditor and limit DB connections
339+
// to simulate deadlock conditions.
340+
db, pubsub, sdb := dbtestutil.NewDBWithSQLDB(t)
341+
// Set MaxOpenConns so we can ensure we aren't inadvertently acquiring
342+
// another connection from within a transaction.
343+
sdb.SetMaxOpenConns(maxConns)
344+
auditor := entaudit.NewAuditor(db, entaudit.DefaultFilter, backends.NewPostgres(db, true))
345+
346+
client, user := coderdenttest.New(t, &coderdenttest.Options{
347+
Options: &coderdtest.Options{
348+
AutobuildTicker: ticker,
349+
AutobuildStats: statCh,
350+
TemplateScheduleStore: schedule.NewEnterpriseTemplateScheduleStore(agplUserQuietHoursScheduleStore()),
351+
Database: db,
352+
Pubsub: pubsub,
353+
Auditor: auditor,
354+
IncludeProvisionerDaemon: true,
355+
},
356+
LicenseOptions: &coderdenttest.LicenseOptions{
357+
Features: license.Features{codersdk.FeatureAdvancedTemplateScheduling: 1},
358+
},
359+
})
360+
361+
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
362+
Parse: echo.ParseComplete,
363+
ProvisionPlan: echo.PlanComplete,
364+
ProvisionApply: echo.ApplyComplete,
365+
})
366+
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
367+
ctr.TimeTilDormantMillis = ptr.Ref[int64](inactiveTTL.Milliseconds())
368+
})
369+
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
370+
371+
workspaces := make([]codersdk.Workspace, 0, numWorkspaces)
372+
for i := 0; i < numWorkspaces; i++ {
373+
ws := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
374+
build := coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, ws.LatestBuild.ID)
375+
require.Equal(t, codersdk.WorkspaceStatusRunning, build.Status)
376+
workspaces = append(workspaces, ws)
377+
}
378+
379+
// Simulate being inactive.
380+
ticker <- time.Now().Add(time.Hour)
381+
stats := <-statCh
382+
383+
// Expect workspace to transition to stopped state for breaching
384+
// failure TTL.
385+
require.Len(t, stats.Transitions, numWorkspaces)
386+
for _, ws := range workspaces {
387+
// The workspace should be dormant.
388+
ws = coderdtest.MustWorkspace(t, client, ws.ID)
389+
require.NotNil(t, ws.DormantAt)
390+
}
391+
})
392+
312393
t.Run("InactiveTTLTooEarly", func(t *testing.T) {
313394
t.Parallel()
314395

0 commit comments

Comments
 (0)