@@ -17,10 +17,18 @@ import (
17
17
18
18
// Executor automatically starts or stops workspaces.
19
19
type Executor struct {
20
- ctx context.Context
21
- db database.Store
22
- log slog.Logger
23
- tick <- chan time.Time
20
+ ctx context.Context
21
+ db database.Store
22
+ log slog.Logger
23
+ tick <- chan time.Time
24
+ statsCh chan <- Stats
25
+ }
26
+
27
+ // Stats contains information about one run of Executor.
28
+ type Stats struct {
29
+ Transitions map [uuid.UUID ]database.WorkspaceTransition
30
+ Elapsed time.Duration
31
+ Error error
24
32
}
25
33
26
34
// New returns a new autobuild executor.
@@ -34,22 +42,42 @@ func New(ctx context.Context, db database.Store, log slog.Logger, tick <-chan ti
34
42
return le
35
43
}
36
44
45
+ // WithStatsChannel will cause Executor to push a RunStats to ch after
46
+ // every tick.
47
+ func (e * Executor ) WithStatsChannel (ch chan <- Stats ) * Executor {
48
+ e .statsCh = ch
49
+ return e
50
+ }
51
+
37
52
// Run will cause executor to start or stop workspaces on every
38
53
// tick from its channel. It will stop when its context is Done, or when
39
54
// its channel is closed.
40
55
func (e * Executor ) Run () {
41
56
go func () {
42
57
for t := range e .tick {
43
- if err := e .runOnce (t ); err != nil {
44
- e .log .Error (e .ctx , "error running once" , slog .Error (err ))
58
+ stats := e .runOnce (t )
59
+ if stats .Error != nil {
60
+ e .log .Error (e .ctx , "error running once" , slog .Error (stats .Error ))
45
61
}
62
+ if e .statsCh != nil {
63
+ e .statsCh <- stats
64
+ }
65
+ e .log .Debug (e .ctx , "run stats" , slog .F ("elapsed" , stats .Elapsed ), slog .F ("transitions" , stats .Transitions ))
46
66
}
47
67
}()
48
68
}
49
69
50
- func (e * Executor ) runOnce (t time.Time ) error {
70
+ func (e * Executor ) runOnce (t time.Time ) Stats {
71
+ var err error
72
+ stats := Stats {
73
+ Transitions : make (map [uuid.UUID ]database.WorkspaceTransition ),
74
+ }
75
+ defer func () {
76
+ stats .Elapsed = time .Since (t )
77
+ stats .Error = err
78
+ }()
51
79
currentTick := t .Truncate (time .Minute )
52
- return e .db .InTx (func (db database.Store ) error {
80
+ err = e .db .InTx (func (db database.Store ) error {
53
81
// TTL is set at the workspace level, and deadline at the workspace build level.
54
82
// When a workspace build is created, its deadline initially starts at zero.
55
83
// When provisionerd successfully completes a provision job, the deadline is
@@ -146,6 +174,7 @@ func (e *Executor) runOnce(t time.Time) error {
146
174
slog .F ("transition" , validTransition ),
147
175
)
148
176
177
+ stats .Transitions [ws .ID ] = validTransition
149
178
if err := build (e .ctx , db , ws , validTransition , priorHistory , priorJob ); err != nil {
150
179
e .log .Error (e .ctx , "unable to transition workspace" ,
151
180
slog .F ("workspace_id" , ws .ID ),
@@ -156,6 +185,7 @@ func (e *Executor) runOnce(t time.Time) error {
156
185
}
157
186
return nil
158
187
})
188
+ return stats
159
189
}
160
190
161
191
// TODO(cian): this function duplicates most of api.postWorkspaceBuilds. Refactor.
0 commit comments