@@ -3,16 +3,19 @@ package workspaceapps
3
3
import (
4
4
"context"
5
5
"sync"
6
+ "sync/atomic"
6
7
"time"
7
8
8
9
"github.com/google/uuid"
9
10
"golang.org/x/xerrors"
10
11
11
12
"cdr.dev/slog"
12
13
14
+ "github.com/coder/coder/v2/coderd/agentapi"
13
15
"github.com/coder/coder/v2/coderd/database"
14
16
"github.com/coder/coder/v2/coderd/database/dbauthz"
15
17
"github.com/coder/coder/v2/coderd/database/dbtime"
18
+ "github.com/coder/coder/v2/coderd/schedule"
16
19
"github.com/coder/coder/v2/coderd/util/slice"
17
20
)
18
21
@@ -59,8 +62,10 @@ var _ StatsReporter = (*StatsDBReporter)(nil)
59
62
60
63
// StatsDBReporter writes workspace app StatsReports to the database.
61
64
type StatsDBReporter struct {
62
- db database.Store
63
- batchSize int
65
+ db database.Store
66
+ logger slog.Logger
67
+ templateScheduleStore * atomic.Pointer [schedule.TemplateScheduleStore ]
68
+ batchSize int
64
69
}
65
70
66
71
// NewStatsDBReporter returns a new StatsDBReporter.
@@ -139,6 +144,36 @@ func (r *StatsDBReporter) Report(ctx context.Context, stats []StatsReport) error
139
144
return err
140
145
}
141
146
147
+ workspaces , err := tx .GetWorkspaces (ctx , database.GetWorkspacesParams {
148
+ WorkspaceIds : uniqueIDs ,
149
+ })
150
+ if err != nil {
151
+ return xerrors .Errorf ("getting workspaces: %w" , err )
152
+ }
153
+
154
+ // TODO: This probably needs batching to handle larger deployments
155
+ for _ , workspace := range workspaces {
156
+ var nextAutostart time.Time
157
+ if workspace .AutostartSchedule .String != "" {
158
+ templateSchedule , err := (* (r .templateScheduleStore .Load ())).Get (ctx , r .db , workspace .TemplateID )
159
+ // If the template schedule fails to load, just default to bumping
160
+ // without the next transition and log it.
161
+ if err != nil {
162
+ r .logger .Error (ctx , "failed to load template schedule bumping activity, defaulting to bumping by 60min" ,
163
+ slog .F ("workspace_id" , workspace .ID ),
164
+ slog .F ("template_id" , workspace .TemplateID ),
165
+ slog .Error (err ),
166
+ )
167
+ } else {
168
+ next , allowed := schedule .NextAutostart (dbtime .Now (), workspace .AutostartSchedule .String , templateSchedule )
169
+ if allowed {
170
+ nextAutostart = next
171
+ }
172
+ }
173
+ }
174
+ agentapi .ActivityBumpWorkspace (ctx , r .logger .Named ("activity_bump" ), r .db , workspace .ID , nextAutostart )
175
+ }
176
+
142
177
return nil
143
178
}, nil )
144
179
if err != nil {
@@ -252,6 +287,16 @@ func (sc *StatsCollector) Collect(report StatsReport) {
252
287
sc .opts .Logger .Debug (sc .ctx , "collected workspace app stats" , slog .F ("report" , report ))
253
288
}
254
289
290
+ func (sc * StatsCollector ) CollectAndFlush (ctx context.Context , report StatsReport ) error {
291
+ sc .Collect (report )
292
+ err := sc .flush (ctx )
293
+ if err != nil {
294
+ return xerrors .Errorf ("flushing collector: %w" , err )
295
+ }
296
+
297
+ return nil
298
+ }
299
+
255
300
// rollup performs stats rollup for sessions that fall within the
256
301
// configured rollup window. For sessions longer than the window,
257
302
// we report them individually.
0 commit comments