@@ -17,10 +17,18 @@ import (
1717
1818// Executor automatically starts or stops workspaces.
1919type 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
2432}
2533
2634// New returns a new autobuild executor.
@@ -34,22 +42,42 @@ func New(ctx context.Context, db database.Store, log slog.Logger, tick <-chan ti
3442 return le
3543}
3644
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+
3752// Run will cause executor to start or stop workspaces on every
3853// tick from its channel. It will stop when its context is Done, or when
3954// its channel is closed.
4055func (e * Executor ) Run () {
4156 go func () {
4257 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 ))
4561 }
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 ))
4666 }
4767 }()
4868}
4969
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+ }()
5179 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 {
5381 // TTL is set at the workspace level, and deadline at the workspace build level.
5482 // When a workspace build is created, its deadline initially starts at zero.
5583 // When provisionerd successfully completes a provision job, the deadline is
@@ -146,6 +174,7 @@ func (e *Executor) runOnce(t time.Time) error {
146174 slog .F ("transition" , validTransition ),
147175 )
148176
177+ stats .Transitions [ws .ID ] = validTransition
149178 if err := build (e .ctx , db , ws , validTransition , priorHistory , priorJob ); err != nil {
150179 e .log .Error (e .ctx , "unable to transition workspace" ,
151180 slog .F ("workspace_id" , ws .ID ),
@@ -156,6 +185,7 @@ func (e *Executor) runOnce(t time.Time) error {
156185 }
157186 return nil
158187 })
188+ return stats
159189}
160190
161191// TODO(cian): this function duplicates most of api.postWorkspaceBuilds. Refactor.
0 commit comments