@@ -72,7 +72,8 @@ func TestNoReconciliationActionsIfNoPresets(t *testing.T) {
7272require .Equal (t ,templateVersion ,gotTemplateVersion )
7373
7474// when we trigger the reconciliation loop for all templates
75- require .NoError (t ,controller .ReconcileAll (ctx ))
75+ _ ,err = controller .ReconcileAll (ctx )
76+ require .NoError (t ,err )
7677
7778// then no reconciliation actions are taken
7879// because without presets, there are no prebuilds
@@ -126,7 +127,8 @@ func TestNoReconciliationActionsIfNoPrebuilds(t *testing.T) {
126127require .NotEmpty (t ,presetParameters )
127128
128129// when we trigger the reconciliation loop for all templates
129- require .NoError (t ,controller .ReconcileAll (ctx ))
130+ _ ,err = controller .ReconcileAll (ctx )
131+ require .NoError (t ,err )
130132
131133// then no reconciliation actions are taken
132134// because without prebuilds, there is nothing to reconcile
@@ -428,7 +430,8 @@ func (tc testCase) run(t *testing.T) {
428430// Run the reconciliation multiple times to ensure idempotency
429431// 8 was arbitrary, but large enough to reasonably trust the result
430432for i := 1 ;i <= 8 ;i ++ {
431- require .NoErrorf (t ,controller .ReconcileAll (ctx ),"failed on iteration %d" ,i )
433+ _ ,err := controller .ReconcileAll (ctx )
434+ require .NoErrorf (t ,err ,"failed on iteration %d" ,i )
432435
433436if tc .shouldCreateNewPrebuild != nil {
434437newPrebuildCount := 0
@@ -542,7 +545,8 @@ func TestMultiplePresetsPerTemplateVersion(t *testing.T) {
542545// Run the reconciliation multiple times to ensure idempotency
543546// 8 was arbitrary, but large enough to reasonably trust the result
544547for i := 1 ;i <= 8 ;i ++ {
545- require .NoErrorf (t ,controller .ReconcileAll (ctx ),"failed on iteration %d" ,i )
548+ _ ,err := controller .ReconcileAll (ctx )
549+ require .NoErrorf (t ,err ,"failed on iteration %d" ,i )
546550
547551newPrebuildCount := 0
548552workspaces ,err := db .GetWorkspacesByTemplateID (ctx ,template .ID )
@@ -668,7 +672,7 @@ func TestPrebuildScheduling(t *testing.T) {
668672DesiredInstances :5 ,
669673})
670674
671- err := controller .ReconcileAll (ctx )
675+ _ , err := controller .ReconcileAll (ctx )
672676require .NoError (t ,err )
673677
674678// get workspace builds
@@ -751,7 +755,8 @@ func TestInvalidPreset(t *testing.T) {
751755// Run the reconciliation multiple times to ensure idempotency
752756// 8 was arbitrary, but large enough to reasonably trust the result
753757for i := 1 ;i <= 8 ;i ++ {
754- require .NoErrorf (t ,controller .ReconcileAll (ctx ),"failed on iteration %d" ,i )
758+ _ ,err := controller .ReconcileAll (ctx )
759+ require .NoErrorf (t ,err ,"failed on iteration %d" ,i )
755760
756761workspaces ,err := db .GetWorkspacesByTemplateID (ctx ,template .ID )
757762require .NoError (t ,err )
@@ -817,7 +822,8 @@ func TestDeletionOfPrebuiltWorkspaceWithInvalidPreset(t *testing.T) {
817822})
818823
819824// Old prebuilt workspace should be deleted.
820- require .NoError (t ,controller .ReconcileAll (ctx ))
825+ _ ,err = controller .ReconcileAll (ctx )
826+ require .NoError (t ,err )
821827
822828builds ,err := db .GetWorkspaceBuildsByWorkspaceID (ctx , database.GetWorkspaceBuildsByWorkspaceIDParams {
823829WorkspaceID :prebuiltWorkspace .ID ,
@@ -916,12 +922,15 @@ func TestSkippingHardLimitedPresets(t *testing.T) {
916922
917923// Trigger reconciliation to attempt creating a new prebuild.
918924// The outcome depends on whether the hard limit has been reached.
919- require .NoError (t ,controller .ReconcileAll (ctx ))
925+ _ ,err = controller .ReconcileAll (ctx )
926+ require .NoError (t ,err )
920927
921928// These two additional calls to ReconcileAll should not trigger any notifications.
922929// A notification is only sent once.
923- require .NoError (t ,controller .ReconcileAll (ctx ))
924- require .NoError (t ,controller .ReconcileAll (ctx ))
930+ _ ,err = controller .ReconcileAll (ctx )
931+ require .NoError (t ,err )
932+ _ ,err = controller .ReconcileAll (ctx )
933+ require .NoError (t ,err )
925934
926935// Verify the final state after reconciliation.
927936workspaces ,err = db .GetWorkspacesByTemplateID (ctx ,template .ID )
@@ -1093,12 +1102,15 @@ func TestHardLimitedPresetShouldNotBlockDeletion(t *testing.T) {
10931102
10941103// Trigger reconciliation to attempt creating a new prebuild.
10951104// The outcome depends on whether the hard limit has been reached.
1096- require .NoError (t ,controller .ReconcileAll (ctx ))
1105+ _ ,err = controller .ReconcileAll (ctx )
1106+ require .NoError (t ,err )
10971107
10981108// These two additional calls to ReconcileAll should not trigger any notifications.
10991109// A notification is only sent once.
1100- require .NoError (t ,controller .ReconcileAll (ctx ))
1101- require .NoError (t ,controller .ReconcileAll (ctx ))
1110+ _ ,err = controller .ReconcileAll (ctx )
1111+ require .NoError (t ,err )
1112+ _ ,err = controller .ReconcileAll (ctx )
1113+ require .NoError (t ,err )
11021114
11031115// Verify the final state after reconciliation.
11041116// When hard limit is reached, no new workspace should be created.
@@ -1141,7 +1153,8 @@ func TestHardLimitedPresetShouldNotBlockDeletion(t *testing.T) {
11411153}
11421154
11431155// Trigger reconciliation to make sure that successful, but outdated prebuilt workspace will be deleted.
1144- require .NoError (t ,controller .ReconcileAll (ctx ))
1156+ _ ,err = controller .ReconcileAll (ctx )
1157+ require .NoError (t ,err )
11451158
11461159workspaces ,err = db .GetWorkspacesByTemplateID (ctx ,template .ID )
11471160require .NoError (t ,err )
@@ -1740,7 +1753,8 @@ func TestExpiredPrebuildsMultipleActions(t *testing.T) {
17401753}
17411754
17421755// Trigger reconciliation to process expired prebuilds and enforce desired state.
1743- require .NoError (t ,controller .ReconcileAll (ctx ))
1756+ _ ,err = controller .ReconcileAll (ctx )
1757+ require .NoError (t ,err )
17441758
17451759// Sort non-expired workspaces by CreatedAt in ascending order (oldest first)
17461760sort .Slice (nonExpiredWorkspaces ,func (i ,j int )bool {
@@ -2145,7 +2159,8 @@ func TestCancelPendingPrebuilds(t *testing.T) {
21452159require .NoError (t ,err )
21462160
21472161// When: the reconciliation loop is triggered
2148- require .NoError (t ,reconciler .ReconcileAll (ctx ))
2162+ _ ,err = reconciler .ReconcileAll (ctx )
2163+ require .NoError (t ,err )
21492164
21502165if tt .shouldCancel {
21512166// Then: the pending prebuild job from non-active version should be canceled
@@ -2347,7 +2362,8 @@ func TestCancelPendingPrebuilds(t *testing.T) {
23472362templateBVersion3Pending := setupPrebuilds (t ,db ,owner .OrganizationID ,templateBID ,templateBVersion3ID ,templateBVersion3PresetID ,1 ,true )
23482363
23492364// When: the reconciliation loop is executed
2350- require .NoError (t ,reconciler .ReconcileAll (ctx ))
2365+ _ ,err := reconciler .ReconcileAll (ctx )
2366+ require .NoError (t ,err )
23512367
23522368// Then: template A version 1 running workspaces should not be canceled
23532369checkIfJobCanceledAndDeleted (t ,clock ,ctx ,db ,false ,templateAVersion1Running )
@@ -2369,6 +2385,51 @@ func TestCancelPendingPrebuilds(t *testing.T) {
23692385})
23702386}
23712387
2388+ func TestReconciliationStats (t * testing.T ) {
2389+ t .Parallel ()
2390+
2391+ // Setup
2392+ clock := quartz .NewReal ()
2393+ db ,ps := dbtestutil .NewDB (t )
2394+ client ,_ ,_ := coderdtest .NewWithAPI (t ,& coderdtest.Options {
2395+ Database :db ,
2396+ Pubsub :ps ,
2397+ Clock :clock ,
2398+ })
2399+ fakeEnqueuer := newFakeEnqueuer ()
2400+ registry := prometheus .NewRegistry ()
2401+ cache := files .New (registry ,& coderdtest.FakeAuthorizer {})
2402+ logger := slogtest .Make (t ,& slogtest.Options {IgnoreErrors :false }).Leveled (slog .LevelDebug )
2403+ reconciler := prebuilds .NewStoreReconciler (db ,ps ,cache , codersdk.PrebuildsConfig {},logger ,clock ,registry ,fakeEnqueuer ,newNoopUsageCheckerPtr ())
2404+ owner := coderdtest .CreateFirstUser (t ,client )
2405+
2406+ ctx ,cancel := context .WithTimeout (context .Background (),testutil .WaitShort )
2407+ defer cancel ()
2408+
2409+ // Create a template version with a preset
2410+ dbfake .TemplateVersion (t ,db ).Seed (database.TemplateVersion {
2411+ OrganizationID :owner .OrganizationID ,
2412+ CreatedBy :owner .UserID ,
2413+ }).Preset (database.TemplateVersionPreset {
2414+ DesiredInstances : sql.NullInt32 {
2415+ Int32 :1 ,
2416+ Valid :true ,
2417+ },
2418+ }).Do ()
2419+
2420+ // Verify that ReconcileAll tracks and returns elapsed time
2421+ start := time .Now ()
2422+ stats ,err := reconciler .ReconcileAll (ctx )
2423+ actualElapsed := time .Since (start )
2424+ require .NoError (t ,err )
2425+ require .Greater (t ,stats .Elapsed ,time .Duration (0 ))
2426+
2427+ // Verify stats.Elapsed matches actual execution time
2428+ require .InDelta (t ,actualElapsed .Milliseconds (),stats .Elapsed .Milliseconds (),100 )
2429+ // Verify reconciliation loop is not unexpectedly slow
2430+ require .Less (t ,stats .Elapsed ,5 * time .Second )
2431+ }
2432+
23722433func newNoopEnqueuer ()* notifications.NoopEnqueuer {
23732434return notifications .NewNoopEnqueuer ()
23742435}
@@ -2863,7 +2924,7 @@ func TestReconciliationRespectsPauseSetting(t *testing.T) {
28632924_ = setupTestDBPreset (t ,db ,templateVersionID ,2 ,"test" )
28642925
28652926// Initially, reconciliation should create prebuilds
2866- err := reconciler .ReconcileAll (ctx )
2927+ _ , err := reconciler .ReconcileAll (ctx )
28672928require .NoError (t ,err )
28682929
28692930// Verify that prebuilds were created
@@ -2890,7 +2951,7 @@ func TestReconciliationRespectsPauseSetting(t *testing.T) {
28902951require .Len (t ,workspaces ,0 ,"prebuilds should be deleted" )
28912952
28922953// Run reconciliation again - it should be paused and not recreate prebuilds
2893- err = reconciler .ReconcileAll (ctx )
2954+ _ , err = reconciler .ReconcileAll (ctx )
28942955require .NoError (t ,err )
28952956
28962957// Verify that no new prebuilds were created because reconciliation is paused
@@ -2903,7 +2964,7 @@ func TestReconciliationRespectsPauseSetting(t *testing.T) {
29032964require .NoError (t ,err )
29042965
29052966// Run reconciliation again - it should now recreate the prebuilds
2906- err = reconciler .ReconcileAll (ctx )
2967+ _ , err = reconciler .ReconcileAll (ctx )
29072968require .NoError (t ,err )
29082969
29092970// Verify that prebuilds were recreated