Skip to content

Commit 5c49221

Browse files
fix(coderd/database): consider tag sets when calculating queue position
1 parent 1e35c53 commit 5c49221

File tree

4 files changed

+347
-62
lines changed

4 files changed

+347
-62
lines changed

coderd/database/querier.go

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

coderd/database/querier_test.go

+227-2
Original file line numberDiff line numberDiff line change
@@ -2162,11 +2162,125 @@ func TestGetProvisionerJobsByIDsWithQueuePosition(t *testing.T) {
21622162
t.SkipNow()
21632163
}
21642164

2165+
now := dbtime.Now()
2166+
ctx := testutil.Context(t, testutil.WaitShort)
2167+
2168+
testCases := []struct {
2169+
name string
2170+
jobTags []database.StringMap
2171+
daemonTags []database.StringMap
2172+
queueSizes []int64
2173+
queuePositions []int64
2174+
}{
2175+
{
2176+
name: "test-case-1",
2177+
jobTags: []database.StringMap{
2178+
{"a": "1", "b": "2"},
2179+
{"a": "1"},
2180+
{"a": "1", "c": "3"},
2181+
},
2182+
daemonTags: []database.StringMap{
2183+
{"a": "1", "b": "2"},
2184+
{"a": "1"},
2185+
},
2186+
queueSizes: []int64{2, 2, 0},
2187+
queuePositions: []int64{1, 1, 0},
2188+
},
2189+
{
2190+
name: "test-case-2",
2191+
jobTags: []database.StringMap{
2192+
{"a": "1", "b": "2"},
2193+
{"a": "1"},
2194+
{"a": "1", "c": "3"},
2195+
},
2196+
daemonTags: []database.StringMap{
2197+
{"a": "1", "b": "2"},
2198+
{"a": "1"},
2199+
{"a": "1", "b": "2", "c": "3"},
2200+
},
2201+
queueSizes: []int64{3, 3, 3},
2202+
queuePositions: []int64{1, 1, 3},
2203+
},
2204+
}
2205+
2206+
for _, tc := range testCases {
2207+
db, _ := dbtestutil.NewDB(t)
2208+
2209+
// Create provisioner jobs based on provided tags:
2210+
allJobs := make([]database.ProvisionerJob, len(tc.jobTags))
2211+
for idx, tags := range tc.jobTags {
2212+
// Make sure jobs are stored in correct order, first job should have the earliest createdAt timestamp.
2213+
// Example for 3 jobs:
2214+
// job_1 createdAt: now - 3 minutes
2215+
// job_2 createdAt: now - 2 minutes
2216+
// job_3 createdAt: now - 1 minute
2217+
timeOffsetInMinutes := len(tc.jobTags) - idx
2218+
timeOffset := time.Duration(timeOffsetInMinutes) * time.Minute
2219+
createdAt := now.Add(-timeOffset)
2220+
2221+
allJobs[idx] = dbgen.ProvisionerJob(t, db, nil, database.ProvisionerJob{
2222+
CreatedAt: createdAt,
2223+
Tags: tags,
2224+
})
2225+
}
2226+
2227+
// Create provisioner daemons based on provided tags:
2228+
for idx, tags := range tc.daemonTags {
2229+
dbgen.ProvisionerDaemon(t, db, database.ProvisionerDaemon{
2230+
Name: fmt.Sprintf("prov_%v", idx),
2231+
Provisioners: []database.ProvisionerType{database.ProvisionerTypeEcho},
2232+
Tags: tags,
2233+
})
2234+
}
2235+
2236+
// Assert invariant: the jobs are in pending status
2237+
for idx, job := range allJobs {
2238+
require.Equal(t, database.ProvisionerJobStatusPending, job.JobStatus, "expected job %d to have status %s", idx, database.ProvisionerJobStatusPending)
2239+
}
2240+
2241+
var jobIDs []uuid.UUID
2242+
for _, job := range allJobs {
2243+
jobIDs = append(jobIDs, job.ID)
2244+
}
2245+
2246+
// When: we fetch the jobs by their IDs
2247+
actualJobs, err := db.GetProvisionerJobsByIDsWithQueuePosition(ctx, jobIDs)
2248+
require.NoError(t, err)
2249+
require.Len(t, actualJobs, len(allJobs), "should return all jobs")
2250+
2251+
// Then: the jobs should be returned in the correct order (by IDs in the input slice)
2252+
for idx, job := range actualJobs {
2253+
assert.EqualValues(t, allJobs[idx], job.ProvisionerJob)
2254+
}
2255+
2256+
// Then: the queue size should be set correctly
2257+
var queueSizes []int64
2258+
for _, job := range actualJobs {
2259+
queueSizes = append(queueSizes, job.QueueSize)
2260+
}
2261+
assert.EqualValues(t, tc.queueSizes, queueSizes, "expected queue positions to be set correctly")
2262+
2263+
// Then: the queue position should be set correctly:
2264+
var queuePositions []int64
2265+
for _, job := range actualJobs {
2266+
queuePositions = append(queuePositions, job.QueuePosition)
2267+
}
2268+
assert.EqualValues(t, tc.queuePositions, queuePositions, "expected queue positions to be set correctly")
2269+
}
2270+
}
2271+
2272+
func TestGetProvisionerJobsByIDsWithQueuePosition_MixedStatuses(t *testing.T) {
2273+
t.Parallel()
2274+
if !dbtestutil.WillUsePostgres() {
2275+
t.SkipNow()
2276+
}
2277+
21652278
db, _ := dbtestutil.NewDB(t)
21662279
now := dbtime.Now()
21672280
ctx := testutil.Context(t, testutil.WaitShort)
21682281

2169-
// Given the following provisioner jobs:
2282+
defaultTags := database.StringMap{"a": "1"}
2283+
// Create the following provisioner jobs:
21702284
allJobs := []database.ProvisionerJob{
21712285
// Pending. This will be the last in the queue because
21722286
// it was created most recently.
@@ -2176,6 +2290,7 @@ func TestGetProvisionerJobsByIDsWithQueuePosition(t *testing.T) {
21762290
CanceledAt: sql.NullTime{},
21772291
CompletedAt: sql.NullTime{},
21782292
Error: sql.NullString{},
2293+
Tags: defaultTags,
21792294
}),
21802295

21812296
// Another pending. This will come first in the queue
@@ -2186,6 +2301,7 @@ func TestGetProvisionerJobsByIDsWithQueuePosition(t *testing.T) {
21862301
CanceledAt: sql.NullTime{},
21872302
CompletedAt: sql.NullTime{},
21882303
Error: sql.NullString{},
2304+
Tags: defaultTags,
21892305
}),
21902306

21912307
// Running
@@ -2195,6 +2311,7 @@ func TestGetProvisionerJobsByIDsWithQueuePosition(t *testing.T) {
21952311
CanceledAt: sql.NullTime{},
21962312
CompletedAt: sql.NullTime{},
21972313
Error: sql.NullString{},
2314+
Tags: defaultTags,
21982315
}),
21992316

22002317
// Succeeded
@@ -2204,6 +2321,7 @@ func TestGetProvisionerJobsByIDsWithQueuePosition(t *testing.T) {
22042321
CanceledAt: sql.NullTime{},
22052322
CompletedAt: sql.NullTime{Valid: true, Time: now},
22062323
Error: sql.NullString{},
2324+
Tags: defaultTags,
22072325
}),
22082326

22092327
// Canceling
@@ -2213,6 +2331,7 @@ func TestGetProvisionerJobsByIDsWithQueuePosition(t *testing.T) {
22132331
CanceledAt: sql.NullTime{Valid: true, Time: now},
22142332
CompletedAt: sql.NullTime{},
22152333
Error: sql.NullString{},
2334+
Tags: defaultTags,
22162335
}),
22172336

22182337
// Canceled
@@ -2222,6 +2341,7 @@ func TestGetProvisionerJobsByIDsWithQueuePosition(t *testing.T) {
22222341
CanceledAt: sql.NullTime{Valid: true, Time: now},
22232342
CompletedAt: sql.NullTime{Valid: true, Time: now},
22242343
Error: sql.NullString{},
2344+
Tags: defaultTags,
22252345
}),
22262346

22272347
// Failed
@@ -2231,9 +2351,17 @@ func TestGetProvisionerJobsByIDsWithQueuePosition(t *testing.T) {
22312351
CanceledAt: sql.NullTime{},
22322352
CompletedAt: sql.NullTime{},
22332353
Error: sql.NullString{String: "failed", Valid: true},
2354+
Tags: defaultTags,
22342355
}),
22352356
}
22362357

2358+
// Create default provisioner daemon:
2359+
dbgen.ProvisionerDaemon(t, db, database.ProvisionerDaemon{
2360+
Name: "default_provisioner",
2361+
Provisioners: []database.ProvisionerType{database.ProvisionerTypeEcho},
2362+
Tags: defaultTags,
2363+
})
2364+
22372365
// Assert invariant: the jobs are in the expected order
22382366
require.Len(t, allJobs, 7, "expected 7 jobs")
22392367
for idx, status := range []database.ProvisionerJobStatus{
@@ -2264,9 +2392,11 @@ func TestGetProvisionerJobsByIDsWithQueuePosition(t *testing.T) {
22642392
}
22652393

22662394
// Then: the queue size should be set correctly
2395+
var queueSizes []int64
22672396
for _, job := range actualJobs {
2268-
assert.EqualValues(t, job.QueueSize, 2, "should have queue size 2")
2397+
queueSizes = append(queueSizes, job.QueueSize)
22692398
}
2399+
assert.EqualValues(t, []int64{2, 2, 0, 0, 0, 0, 0}, queueSizes, "expected queue positions to be set correctly")
22702400

22712401
// Then: the queue position should be set correctly:
22722402
var queuePositions []int64
@@ -2276,6 +2406,101 @@ func TestGetProvisionerJobsByIDsWithQueuePosition(t *testing.T) {
22762406
assert.EqualValues(t, []int64{2, 1, 0, 0, 0, 0, 0}, queuePositions, "expected queue positions to be set correctly")
22772407
}
22782408

2409+
func TestGetProvisionerJobsByIDsWithQueuePosition_OrderValidation(t *testing.T) {
2410+
t.Parallel()
2411+
if !dbtestutil.WillUsePostgres() {
2412+
t.SkipNow()
2413+
}
2414+
2415+
db, _ := dbtestutil.NewDB(t)
2416+
now := dbtime.Now()
2417+
ctx := testutil.Context(t, testutil.WaitShort)
2418+
2419+
defaultTags := database.StringMap{"a": "1"}
2420+
// Create the following provisioner jobs:
2421+
allJobs := []database.ProvisionerJob{
2422+
dbgen.ProvisionerJob(t, db, nil, database.ProvisionerJob{
2423+
CreatedAt: now.Add(-4 * time.Minute),
2424+
Tags: defaultTags,
2425+
}),
2426+
2427+
dbgen.ProvisionerJob(t, db, nil, database.ProvisionerJob{
2428+
CreatedAt: now.Add(-5 * time.Minute),
2429+
Tags: defaultTags,
2430+
}),
2431+
2432+
dbgen.ProvisionerJob(t, db, nil, database.ProvisionerJob{
2433+
CreatedAt: now.Add(-6 * time.Minute),
2434+
Tags: defaultTags,
2435+
}),
2436+
2437+
dbgen.ProvisionerJob(t, db, nil, database.ProvisionerJob{
2438+
CreatedAt: now.Add(-3 * time.Minute),
2439+
Tags: defaultTags,
2440+
}),
2441+
2442+
dbgen.ProvisionerJob(t, db, nil, database.ProvisionerJob{
2443+
CreatedAt: now.Add(-2 * time.Minute),
2444+
Tags: defaultTags,
2445+
}),
2446+
2447+
dbgen.ProvisionerJob(t, db, nil, database.ProvisionerJob{
2448+
CreatedAt: now.Add(-1 * time.Minute),
2449+
Tags: defaultTags,
2450+
}),
2451+
}
2452+
2453+
// Create default provisioner daemon:
2454+
dbgen.ProvisionerDaemon(t, db, database.ProvisionerDaemon{
2455+
Name: "default_provisioner",
2456+
Provisioners: []database.ProvisionerType{database.ProvisionerTypeEcho},
2457+
Tags: defaultTags,
2458+
})
2459+
2460+
// Assert invariant: the jobs are in the expected order
2461+
require.Len(t, allJobs, 6, "expected 7 jobs")
2462+
for idx, status := range []database.ProvisionerJobStatus{
2463+
database.ProvisionerJobStatusPending,
2464+
database.ProvisionerJobStatusPending,
2465+
database.ProvisionerJobStatusPending,
2466+
database.ProvisionerJobStatusPending,
2467+
database.ProvisionerJobStatusPending,
2468+
database.ProvisionerJobStatusPending,
2469+
} {
2470+
require.Equal(t, status, allJobs[idx].JobStatus, "expected job %d to have status %s", idx, status)
2471+
}
2472+
2473+
var jobIDs []uuid.UUID
2474+
for _, job := range allJobs {
2475+
jobIDs = append(jobIDs, job.ID)
2476+
}
2477+
2478+
// When: we fetch the jobs by their IDs
2479+
actualJobs, err := db.GetProvisionerJobsByIDsWithQueuePosition(ctx, jobIDs)
2480+
require.NoError(t, err)
2481+
require.Len(t, actualJobs, len(allJobs), "should return all jobs")
2482+
2483+
// Then: the jobs should be returned in the correct order (by IDs in the input slice)
2484+
for idx, job := range actualJobs {
2485+
assert.EqualValues(t, allJobs[idx], job.ProvisionerJob)
2486+
assert.EqualValues(t, allJobs[idx].CreatedAt, job.ProvisionerJob.CreatedAt)
2487+
}
2488+
2489+
// Then: the queue size should be set correctly
2490+
var queueSizes []int64
2491+
for _, job := range actualJobs {
2492+
queueSizes = append(queueSizes, job.QueueSize)
2493+
}
2494+
assert.EqualValues(t, []int64{6, 6, 6, 6, 6, 6}, queueSizes, "expected queue positions to be set correctly")
2495+
2496+
// Then: the queue position should be set correctly:
2497+
var queuePositions []int64
2498+
for _, job := range actualJobs {
2499+
queuePositions = append(queuePositions, job.QueuePosition)
2500+
}
2501+
assert.EqualValues(t, []int64{3, 2, 1, 4, 5, 6}, queuePositions, "expected queue positions to be set correctly")
2502+
}
2503+
22792504
func TestGroupRemovalTrigger(t *testing.T) {
22802505
t.Parallel()
22812506

0 commit comments

Comments
 (0)