@@ -1322,6 +1322,101 @@ func TestQueuePosition(t *testing.T) {
1322
1322
}
1323
1323
}
1324
1324
1325
+ func TestAcquireProvisionerJob (t * testing.T ) {
1326
+ t .Parallel ()
1327
+
1328
+ t .Run ("HumanInitiatedJobsFirst" ,func (t * testing.T ) {
1329
+ t .Parallel ()
1330
+ var (
1331
+ db ,_ = dbtestutil .NewDB (t )
1332
+ ctx = testutil .Context (t ,testutil .WaitMedium )
1333
+ org = dbgen .Organization (t ,db , database.Organization {})
1334
+ _ = dbgen .ProvisionerDaemon (t ,db , database.ProvisionerDaemon {})// Required for queue position
1335
+ now = dbtime .Now ()
1336
+ numJobs = 10
1337
+ humanIDs = make ([]uuid.UUID ,0 ,numJobs / 2 )
1338
+ prebuildIDs = make ([]uuid.UUID ,0 ,numJobs / 2 )
1339
+ )
1340
+
1341
+ // Given: a number of jobs in the queue, with prebuilds and non-prebuilds interleaved
1342
+ for idx := range numJobs {
1343
+ var initiator uuid.UUID
1344
+ if idx % 2 == 0 {
1345
+ initiator = database .PrebuildsSystemUserID
1346
+ }else {
1347
+ initiator = uuid .MustParse ("c0dec0de-c0de-c0de-c0de-c0dec0dec0de" )
1348
+ }
1349
+ pj ,err := db .InsertProvisionerJob (ctx , database.InsertProvisionerJobParams {
1350
+ ID :uuid .MustParse (fmt .Sprintf ("00000000-0000-0000-0000-00000000000%x" ,idx + 1 )),
1351
+ CreatedAt :time .Now ().Add (- time .Second * time .Duration (idx )),
1352
+ UpdatedAt :time .Now ().Add (- time .Second * time .Duration (idx )),
1353
+ InitiatorID :initiator ,
1354
+ OrganizationID :org .ID ,
1355
+ Provisioner :database .ProvisionerTypeEcho ,
1356
+ Type :database .ProvisionerJobTypeWorkspaceBuild ,
1357
+ StorageMethod :database .ProvisionerStorageMethodFile ,
1358
+ FileID :uuid .New (),
1359
+ Input :json .RawMessage (`{}` ),
1360
+ Tags : database.StringMap {},
1361
+ TraceMetadata : pqtype.NullRawMessage {},
1362
+ })
1363
+ require .NoError (t ,err )
1364
+ // We expected prebuilds to be acquired after human-initiated jobs.
1365
+ if initiator == database .PrebuildsSystemUserID {
1366
+ prebuildIDs = append ([]uuid.UUID {pj .ID },prebuildIDs ... )
1367
+ }else {
1368
+ humanIDs = append ([]uuid.UUID {pj .ID },humanIDs ... )
1369
+ }
1370
+ t .Logf ("created job id=%q initiator=%q created_at=%q" ,pj .ID .String (),pj .InitiatorID .String (),pj .CreatedAt .String ())
1371
+ }
1372
+
1373
+ expectedIDs := append (humanIDs ,prebuildIDs ... )//nolint:gocritic // not the same slice
1374
+
1375
+ // When: we query the queue positions for the jobs
1376
+ qjs ,err := db .GetProvisionerJobsByIDsWithQueuePosition (ctx , database.GetProvisionerJobsByIDsWithQueuePositionParams {
1377
+ IDs :expectedIDs ,
1378
+ StaleIntervalMS :provisionerdserver .StaleInterval .Milliseconds (),
1379
+ })
1380
+ require .NoError (t ,err )
1381
+ require .Len (t ,qjs ,numJobs )
1382
+ // Ensure the jobs are sorted by queue position.
1383
+ sort .Slice (qjs ,func (i ,j int )bool {
1384
+ return qjs [i ].QueuePosition < qjs [j ].QueuePosition
1385
+ })
1386
+
1387
+ // Then: the queue positions for the jobs should indicate the order in which
1388
+ // they will be acquired, with human-initiated jobs first.
1389
+ for idx ,qj := range qjs {
1390
+ t .Logf ("queued job %d/%d id=%q initiator=%q created_at=%q queue_position=%d" ,idx + 1 ,numJobs ,qj .ProvisionerJob .ID .String (),qj .ProvisionerJob .InitiatorID .String (),qj .ProvisionerJob .CreatedAt .String (),qj .QueuePosition )
1391
+ require .Equal (t ,expectedIDs [idx ].String (),qj .ProvisionerJob .ID .String (),"job %d/%d should match expected id" ,idx + 1 ,numJobs )
1392
+ require .Equal (t ,int64 (idx + 1 ),qj .QueuePosition ,"job %d/%d should have queue position %d" ,idx + 1 ,numJobs ,idx + 1 )
1393
+ }
1394
+
1395
+ // When: the jobs are acquired
1396
+ // Then: human-initiated jobs are prioritized first.
1397
+ for idx := range numJobs {
1398
+ acquired ,err := db .AcquireProvisionerJob (ctx , database.AcquireProvisionerJobParams {
1399
+ OrganizationID :org .ID ,
1400
+ StartedAt : sql.NullTime {Time :time .Now (),Valid :true },
1401
+ WorkerID : uuid.NullUUID {UUID :uuid .New (),Valid :true },
1402
+ Types : []database.ProvisionerType {database .ProvisionerTypeEcho },
1403
+ ProvisionerTags :json .RawMessage (`{}` ),
1404
+ })
1405
+ require .NoError (t ,err )
1406
+ require .Equal (t ,expectedIDs [idx ].String (),acquired .ID .String (),"acquired job %d/%d with initiator %q" ,idx + 1 ,numJobs ,acquired .InitiatorID .String ())
1407
+ t .Logf ("acquired job id=%q initiator=%q created_at=%q" ,acquired .ID .String (),acquired .InitiatorID .String (),acquired .CreatedAt .String ())
1408
+ err = db .UpdateProvisionerJobWithCompleteByID (ctx , database.UpdateProvisionerJobWithCompleteByIDParams {
1409
+ ID :acquired .ID ,
1410
+ UpdatedAt :now ,
1411
+ CompletedAt : sql.NullTime {Time :now ,Valid :true },
1412
+ Error : sql.NullString {},
1413
+ ErrorCode : sql.NullString {},
1414
+ })
1415
+ require .NoError (t ,err ,"mark job %d/%d as complete" ,idx + 1 ,numJobs )
1416
+ }
1417
+ })
1418
+ }
1419
+
1325
1420
func TestUserLastSeenFilter (t * testing.T ) {
1326
1421
t .Parallel ()
1327
1422
if testing .Short () {