@@ -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
@@ -425,7 +427,8 @@ func (tc testCase) run(t *testing.T) {
425427// Run the reconciliation multiple times to ensure idempotency
426428// 8 was arbitrary, but large enough to reasonably trust the result
427429for i := 1 ;i <= 8 ;i ++ {
428- require .NoErrorf (t ,controller .ReconcileAll (ctx ),"failed on iteration %d" ,i )
430+ _ ,err := controller .ReconcileAll (ctx )
431+ require .NoErrorf (t ,err ,"failed on iteration %d" ,i )
429432
430433if tc .shouldCreateNewPrebuild != nil {
431434newPrebuildCount := 0
@@ -539,7 +542,8 @@ func TestMultiplePresetsPerTemplateVersion(t *testing.T) {
539542// Run the reconciliation multiple times to ensure idempotency
540543// 8 was arbitrary, but large enough to reasonably trust the result
541544for i := 1 ;i <= 8 ;i ++ {
542- require .NoErrorf (t ,controller .ReconcileAll (ctx ),"failed on iteration %d" ,i )
545+ _ ,err := controller .ReconcileAll (ctx )
546+ require .NoErrorf (t ,err ,"failed on iteration %d" ,i )
543547
544548newPrebuildCount := 0
545549workspaces ,err := db .GetWorkspacesByTemplateID (ctx ,template .ID )
@@ -665,7 +669,7 @@ func TestPrebuildScheduling(t *testing.T) {
665669DesiredInstances :5 ,
666670})
667671
668- err := controller .ReconcileAll (ctx )
672+ _ , err := controller .ReconcileAll (ctx )
669673require .NoError (t ,err )
670674
671675// get workspace builds
@@ -748,7 +752,8 @@ func TestInvalidPreset(t *testing.T) {
748752// Run the reconciliation multiple times to ensure idempotency
749753// 8 was arbitrary, but large enough to reasonably trust the result
750754for i := 1 ;i <= 8 ;i ++ {
751- require .NoErrorf (t ,controller .ReconcileAll (ctx ),"failed on iteration %d" ,i )
755+ _ ,err := controller .ReconcileAll (ctx )
756+ require .NoErrorf (t ,err ,"failed on iteration %d" ,i )
752757
753758workspaces ,err := db .GetWorkspacesByTemplateID (ctx ,template .ID )
754759require .NoError (t ,err )
@@ -814,7 +819,8 @@ func TestDeletionOfPrebuiltWorkspaceWithInvalidPreset(t *testing.T) {
814819})
815820
816821// Old prebuilt workspace should be deleted.
817- require .NoError (t ,controller .ReconcileAll (ctx ))
822+ _ ,err = controller .ReconcileAll (ctx )
823+ require .NoError (t ,err )
818824
819825builds ,err := db .GetWorkspaceBuildsByWorkspaceID (ctx , database.GetWorkspaceBuildsByWorkspaceIDParams {
820826WorkspaceID :prebuiltWorkspace .ID ,
@@ -913,12 +919,15 @@ func TestSkippingHardLimitedPresets(t *testing.T) {
913919
914920// Trigger reconciliation to attempt creating a new prebuild.
915921// The outcome depends on whether the hard limit has been reached.
916- require .NoError (t ,controller .ReconcileAll (ctx ))
922+ _ ,err = controller .ReconcileAll (ctx )
923+ require .NoError (t ,err )
917924
918925// These two additional calls to ReconcileAll should not trigger any notifications.
919926// A notification is only sent once.
920- require .NoError (t ,controller .ReconcileAll (ctx ))
921- require .NoError (t ,controller .ReconcileAll (ctx ))
927+ _ ,err = controller .ReconcileAll (ctx )
928+ require .NoError (t ,err )
929+ _ ,err = controller .ReconcileAll (ctx )
930+ require .NoError (t ,err )
922931
923932// Verify the final state after reconciliation.
924933workspaces ,err = db .GetWorkspacesByTemplateID (ctx ,template .ID )
@@ -1090,12 +1099,15 @@ func TestHardLimitedPresetShouldNotBlockDeletion(t *testing.T) {
10901099
10911100// Trigger reconciliation to attempt creating a new prebuild.
10921101// The outcome depends on whether the hard limit has been reached.
1093- require .NoError (t ,controller .ReconcileAll (ctx ))
1102+ _ ,err = controller .ReconcileAll (ctx )
1103+ require .NoError (t ,err )
10941104
10951105// These two additional calls to ReconcileAll should not trigger any notifications.
10961106// A notification is only sent once.
1097- require .NoError (t ,controller .ReconcileAll (ctx ))
1098- require .NoError (t ,controller .ReconcileAll (ctx ))
1107+ _ ,err = controller .ReconcileAll (ctx )
1108+ require .NoError (t ,err )
1109+ _ ,err = controller .ReconcileAll (ctx )
1110+ require .NoError (t ,err )
10991111
11001112// Verify the final state after reconciliation.
11011113// When hard limit is reached, no new workspace should be created.
@@ -1138,7 +1150,8 @@ func TestHardLimitedPresetShouldNotBlockDeletion(t *testing.T) {
11381150}
11391151
11401152// Trigger reconciliation to make sure that successful, but outdated prebuilt workspace will be deleted.
1141- require .NoError (t ,controller .ReconcileAll (ctx ))
1153+ _ ,err = controller .ReconcileAll (ctx )
1154+ require .NoError (t ,err )
11421155
11431156workspaces ,err = db .GetWorkspacesByTemplateID (ctx ,template .ID )
11441157require .NoError (t ,err )
@@ -1737,7 +1750,8 @@ func TestExpiredPrebuildsMultipleActions(t *testing.T) {
17371750}
17381751
17391752// Trigger reconciliation to process expired prebuilds and enforce desired state.
1740- require .NoError (t ,controller .ReconcileAll (ctx ))
1753+ _ ,err = controller .ReconcileAll (ctx )
1754+ require .NoError (t ,err )
17411755
17421756// Sort non-expired workspaces by CreatedAt in ascending order (oldest first)
17431757sort .Slice (nonExpiredWorkspaces ,func (i ,j int )bool {
@@ -2142,7 +2156,8 @@ func TestCancelPendingPrebuilds(t *testing.T) {
21422156require .NoError (t ,err )
21432157
21442158// When: the reconciliation loop is triggered
2145- require .NoError (t ,reconciler .ReconcileAll (ctx ))
2159+ _ ,err = reconciler .ReconcileAll (ctx )
2160+ require .NoError (t ,err )
21462161
21472162if tt .shouldCancel {
21482163// Then: the prebuild related jobs from non-active version should be canceled
@@ -2306,7 +2321,8 @@ func TestCancelPendingPrebuilds(t *testing.T) {
23062321templateBVersion3Pending := setupPrebuilds (t ,db ,owner .OrganizationID ,templateBID ,templateBVersion3ID ,templateBVersion3PresetID ,1 ,true )
23072322
23082323// When: the reconciliation loop is executed
2309- require .NoError (t ,reconciler .ReconcileAll (ctx ))
2324+ _ ,err := reconciler .ReconcileAll (ctx )
2325+ require .NoError (t ,err )
23102326
23112327// Then: template A version 1 running workspaces should not be canceled
23122328checkIfJobCanceled (t ,clock ,ctx ,db ,false ,templateAVersion1Running )
@@ -2328,6 +2344,44 @@ func TestCancelPendingPrebuilds(t *testing.T) {
23282344})
23292345}
23302346
2347+ func TestReconciliationStats (t * testing.T ) {
2348+ t .Parallel ()
2349+
2350+ // Setup
2351+ clock := quartz .NewReal ()
2352+ db ,ps := dbtestutil .NewDB (t )
2353+ client ,_ ,_ := coderdtest .NewWithAPI (t ,& coderdtest.Options {
2354+ Database :db ,
2355+ Pubsub :ps ,
2356+ Clock :clock ,
2357+ })
2358+ fakeEnqueuer := newFakeEnqueuer ()
2359+ registry := prometheus .NewRegistry ()
2360+ cache := files .New (registry ,& coderdtest.FakeAuthorizer {})
2361+ logger := slogtest .Make (t ,& slogtest.Options {IgnoreErrors :false }).Leveled (slog .LevelDebug )
2362+ reconciler := prebuilds .NewStoreReconciler (db ,ps ,cache , codersdk.PrebuildsConfig {},logger ,clock ,registry ,fakeEnqueuer ,newNoopUsageCheckerPtr ())
2363+ owner := coderdtest .CreateFirstUser (t ,client )
2364+
2365+ ctx ,cancel := context .WithTimeout (context .Background (),testutil .WaitLong )
2366+ defer cancel ()
2367+
2368+ // Create a template version with a preset
2369+ dbfake .TemplateVersion (t ,db ).Seed (database.TemplateVersion {
2370+ OrganizationID :owner .OrganizationID ,
2371+ CreatedBy :owner .UserID ,
2372+ }).Preset (database.TemplateVersionPreset {
2373+ DesiredInstances : sql.NullInt32 {
2374+ Int32 :1 ,
2375+ Valid :true ,
2376+ },
2377+ }).Do ()
2378+
2379+ // Verify that ReconcileAll tracks and returns elapsed time
2380+ stats ,err := reconciler .ReconcileAll (ctx )
2381+ require .NoError (t ,err )
2382+ require .Greater (t ,stats .Elapsed ,time .Duration (0 ))
2383+ }
2384+
23312385func newNoopEnqueuer ()* notifications.NoopEnqueuer {
23322386return notifications .NewNoopEnqueuer ()
23332387}
@@ -2822,7 +2876,7 @@ func TestReconciliationRespectsPauseSetting(t *testing.T) {
28222876_ = setupTestDBPreset (t ,db ,templateVersionID ,2 ,"test" )
28232877
28242878// Initially, reconciliation should create prebuilds
2825- err := reconciler .ReconcileAll (ctx )
2879+ _ , err := reconciler .ReconcileAll (ctx )
28262880require .NoError (t ,err )
28272881
28282882// Verify that prebuilds were created
@@ -2849,7 +2903,7 @@ func TestReconciliationRespectsPauseSetting(t *testing.T) {
28492903require .Len (t ,workspaces ,0 ,"prebuilds should be deleted" )
28502904
28512905// Run reconciliation again - it should be paused and not recreate prebuilds
2852- err = reconciler .ReconcileAll (ctx )
2906+ _ , err = reconciler .ReconcileAll (ctx )
28532907require .NoError (t ,err )
28542908
28552909// Verify that no new prebuilds were created because reconciliation is paused
@@ -2862,7 +2916,7 @@ func TestReconciliationRespectsPauseSetting(t *testing.T) {
28622916require .NoError (t ,err )
28632917
28642918// Run reconciliation again - it should now recreate the prebuilds
2865- err = reconciler .ReconcileAll (ctx )
2919+ _ , err = reconciler .ReconcileAll (ctx )
28662920require .NoError (t ,err )
28672921
28682922// Verify that prebuilds were recreated