Skip to content

Commit 636b5b8

Browse files
committed
move more
1 parent 64a5aad commit 636b5b8

File tree

3 files changed

+79
-46
lines changed

3 files changed

+79
-46
lines changed

coderd/agentapi/stats.go

Lines changed: 8 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import (
1818
"github.com/coder/coder/v2/coderd/database/pubsub"
1919
"github.com/coder/coder/v2/coderd/prometheusmetrics"
2020
"github.com/coder/coder/v2/coderd/schedule"
21-
"github.com/coder/coder/v2/codersdk"
21+
"github.com/coder/coder/v2/coderd/workspaceapps"
2222
)
2323

2424
type StatsBatcher interface {
@@ -34,6 +34,7 @@ type StatsAPI struct {
3434
TemplateScheduleStore *atomic.Pointer[schedule.TemplateScheduleStore]
3535
AgentStatsRefreshInterval time.Duration
3636
UpdateAgentMetricsFn func(ctx context.Context, labels prometheusmetrics.AgentMetricLabels, metrics []*agentproto.Stats_Metric)
37+
StatsCollector workspaceapps.StatsCollector
3738

3839
TimeNowFn func() time.Time // defaults to dbtime.Now()
3940
}
@@ -70,28 +71,6 @@ func (a *StatsAPI) UpdateStats(ctx context.Context, req *agentproto.UpdateStatsR
7071
)
7172

7273
now := a.now()
73-
// if req.Stats.ConnectionCount > 0 {
74-
// var nextAutostart time.Time
75-
// if workspace.AutostartSchedule.String != "" {
76-
// templateSchedule, err := (*(a.TemplateScheduleStore.Load())).Get(ctx, a.Database, workspace.TemplateID)
77-
// // If the template schedule fails to load, just default to bumping
78-
// // without the next transition and log it.
79-
// if err != nil {
80-
// a.Log.Error(ctx, "failed to load template schedule bumping activity, defaulting to bumping by 60min",
81-
// slog.F("workspace_id", workspace.ID),
82-
// slog.F("template_id", workspace.TemplateID),
83-
// slog.Error(err),
84-
// )
85-
// } else {
86-
// next, allowed := schedule.NextAutostart(now, workspace.AutostartSchedule.String, templateSchedule)
87-
// if allowed {
88-
// nextAutostart = next
89-
// }
90-
// }
91-
// }
92-
// ActivityBumpWorkspace(ctx, a.Log.Named("activity_bump"), a.Database, workspace.ID, nextAutostart)
93-
// }
94-
9574
var errGroup errgroup.Group
9675
errGroup.Go(func() error {
9776
err := a.StatsBatcher.Add(now, workspaceAgent.ID, workspace.TemplateID, workspace.OwnerID, workspace.ID, req.Stats)
@@ -101,17 +80,6 @@ func (a *StatsAPI) UpdateStats(ctx context.Context, req *agentproto.UpdateStatsR
10180
}
10281
return nil
10382
})
104-
errGroup.Go(func() error {
105-
// nolint:gocritic // (#13146) Will be moved soon as part of refactor.
106-
err := a.Database.UpdateWorkspaceLastUsedAt(ctx, database.UpdateWorkspaceLastUsedAtParams{
107-
ID: workspace.ID,
108-
LastUsedAt: now,
109-
})
110-
if err != nil {
111-
return xerrors.Errorf("update workspace LastUsedAt: %w", err)
112-
}
113-
return nil
114-
})
11583
if a.UpdateAgentMetricsFn != nil {
11684
errGroup.Go(func() error {
11785
user, err := a.Database.GetUserByID(ctx, workspace.OwnerID)
@@ -133,16 +101,12 @@ func (a *StatsAPI) UpdateStats(ctx context.Context, req *agentproto.UpdateStatsR
133101
return nil, xerrors.Errorf("update stats in database: %w", err)
134102
}
135103

136-
// Tell the frontend about the new agent report, now that everything is updated
137-
a.publishWorkspaceAgentStats(ctx, workspace.ID)
104+
// Flushing the stats collector will update last_used_at,
105+
// dealine for the workspace, and will publish a workspace update event.
106+
a.StatsCollector.CollectAndFlush(ctx, workspaceapps.StatsReport{
107+
WorkspaceID: workspace.ID,
108+
// TODO: fill out
109+
})
138110

139111
return res, nil
140112
}
141-
142-
func (a *StatsAPI) publishWorkspaceAgentStats(ctx context.Context, workspaceID uuid.UUID) {
143-
err := a.Pubsub.Publish(codersdk.WorkspaceNotifyChannel(workspaceID), []byte{})
144-
if err != nil {
145-
a.Log.Warn(ctx, "failed to publish workspace agent stats",
146-
slog.F("workspace_id", workspaceID), slog.Error(err))
147-
}
148-
}

coderd/workspaceapps/activity_bump.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package workspaceapps
2+
3+
import (
4+
"context"
5+
"time"
6+
7+
"github.com/google/uuid"
8+
"golang.org/x/xerrors"
9+
10+
"cdr.dev/slog"
11+
"github.com/coder/coder/v2/coderd/database"
12+
)
13+
14+
// ActivityBumpWorkspace automatically bumps the workspace's auto-off timer
15+
// if it is set to expire soon. The deadline will be bumped by 1 hour*.
16+
// If the bump crosses over an autostart time, the workspace will be
17+
// bumped by the workspace ttl instead.
18+
//
19+
// If nextAutostart is the zero value or in the past, the workspace
20+
// will be bumped by 1 hour.
21+
// It handles the edge case in the example:
22+
// 1. Autostart is set to 9am.
23+
// 2. User works all day, and leaves a terminal open to the workspace overnight.
24+
// 3. The open terminal continually bumps the workspace deadline.
25+
// 4. 9am the next day, the activity bump pushes to 10am.
26+
// 5. If the user goes inactive for 1 hour during the day, the workspace will
27+
// now stop, because it has been extended by 1 hour durations. Despite the TTL
28+
// being set to 8hrs from the autostart time.
29+
//
30+
// So the issue is that when the workspace is bumped across an autostart
31+
// deadline, we should treat the workspace as being "started" again and
32+
// extend the deadline by the autostart time + workspace ttl instead.
33+
//
34+
// The issue still remains with build_max_deadline. We need to respect the original
35+
// maximum deadline, so that will need to be handled separately.
36+
// A way to avoid this is to configure the max deadline to something that will not
37+
// span more than 1 day. This will force the workspace to restart and reset the deadline
38+
// each morning when it autostarts.
39+
func ActivityBumpWorkspace(ctx context.Context, log slog.Logger, db database.Store, workspaceID uuid.UUID, nextAutostart time.Time) {
40+
// We set a short timeout so if the app is under load, these
41+
// low priority operations fail first.
42+
ctx, cancel := context.WithTimeout(ctx, time.Second*15)
43+
defer cancel()
44+
err := db.ActivityBumpWorkspace(ctx, database.ActivityBumpWorkspaceParams{
45+
NextAutostart: nextAutostart.UTC(),
46+
WorkspaceID: workspaceID,
47+
})
48+
if err != nil {
49+
if !xerrors.Is(err, context.Canceled) && !database.IsQueryCanceledError(err) {
50+
// Bump will fail if the context is canceled, but this is ok.
51+
log.Error(ctx, "activity bump failed", slog.Error(err),
52+
slog.F("workspace_id", workspaceID),
53+
)
54+
}
55+
return
56+
}
57+
58+
log.Debug(ctx, "bumped deadline from activity",
59+
slog.F("workspace_id", workspaceID),
60+
)
61+
}

coderd/workspaceapps/stats.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,13 @@ import (
1111

1212
"cdr.dev/slog"
1313

14-
"github.com/coder/coder/v2/coderd/agentapi"
1514
"github.com/coder/coder/v2/coderd/database"
1615
"github.com/coder/coder/v2/coderd/database/dbauthz"
1716
"github.com/coder/coder/v2/coderd/database/dbtime"
17+
"github.com/coder/coder/v2/coderd/database/pubsub"
1818
"github.com/coder/coder/v2/coderd/schedule"
1919
"github.com/coder/coder/v2/coderd/util/slice"
20+
"github.com/coder/coder/v2/codersdk"
2021
)
2122

2223
const (
@@ -63,6 +64,7 @@ var _ StatsReporter = (*StatsDBReporter)(nil)
6364
// StatsDBReporter writes workspace app StatsReports to the database.
6465
type StatsDBReporter struct {
6566
db database.Store
67+
pubsub pubsub.Pubsub
6668
logger slog.Logger
6769
templateScheduleStore *atomic.Pointer[schedule.TemplateScheduleStore]
6870
batchSize int
@@ -171,7 +173,13 @@ func (r *StatsDBReporter) Report(ctx context.Context, stats []StatsReport) error
171173
}
172174
}
173175
}
174-
agentapi.ActivityBumpWorkspace(ctx, r.logger.Named("activity_bump"), r.db, workspace.ID, nextAutostart)
176+
ActivityBumpWorkspace(ctx, r.logger.Named("activity_bump"), r.db, workspace.ID, nextAutostart)
177+
178+
err := r.pubsub.Publish(codersdk.WorkspaceNotifyChannel(workspace.ID), []byte{})
179+
if err != nil {
180+
r.logger.Warn(ctx, "failed to publish workspace agent stats",
181+
slog.F("workspace_id", workspace.ID), slog.Error(err))
182+
}
175183
}
176184

177185
return nil

0 commit comments

Comments
 (0)