mirror of
https://github.com/coder/coder.git
synced 2026-09-22 05:05:20 +08:00
feat: add last used to Workspaces page (#3816)
This commit is contained in:
@@ -2208,6 +2208,22 @@ func (q *fakeQuerier) UpdateWorkspaceTTL(_ context.Context, arg database.UpdateW
|
||||
return sql.ErrNoRows
|
||||
}
|
||||
|
||||
func (q *fakeQuerier) UpdateWorkspaceLastUsedAt(_ context.Context, arg database.UpdateWorkspaceLastUsedAtParams) error {
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
for index, workspace := range q.workspaces {
|
||||
if workspace.ID != arg.ID {
|
||||
continue
|
||||
}
|
||||
workspace.LastUsedAt = arg.LastUsedAt
|
||||
q.workspaces[index] = workspace
|
||||
return nil
|
||||
}
|
||||
|
||||
return sql.ErrNoRows
|
||||
}
|
||||
|
||||
func (q *fakeQuerier) UpdateWorkspaceBuildByID(_ context.Context, arg database.UpdateWorkspaceBuildByIDParams) error {
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
Generated
+2
-1
@@ -377,7 +377,8 @@ CREATE TABLE workspaces (
|
||||
deleted boolean DEFAULT false NOT NULL,
|
||||
name character varying(64) NOT NULL,
|
||||
autostart_schedule text,
|
||||
ttl bigint
|
||||
ttl bigint,
|
||||
last_used_at timestamp without time zone DEFAULT '0001-01-01 00:00:00'::timestamp without time zone NOT NULL
|
||||
);
|
||||
|
||||
ALTER TABLE ONLY licenses ALTER COLUMN id SET DEFAULT nextval('public.licenses_id_seq'::regclass);
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE workspaces
|
||||
DROP COLUMN last_used_at;
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE workspaces
|
||||
ADD COLUMN last_used_at timestamp NOT NULL DEFAULT '0001-01-01 00:00:00+00:00';
|
||||
@@ -525,6 +525,7 @@ type Workspace struct {
|
||||
Name string `db:"name" json:"name"`
|
||||
AutostartSchedule sql.NullString `db:"autostart_schedule" json:"autostart_schedule"`
|
||||
Ttl sql.NullInt64 `db:"ttl" json:"ttl"`
|
||||
LastUsedAt time.Time `db:"last_used_at" json:"last_used_at"`
|
||||
}
|
||||
|
||||
type WorkspaceAgent struct {
|
||||
|
||||
@@ -150,6 +150,7 @@ type querier interface {
|
||||
UpdateWorkspaceAutostart(ctx context.Context, arg UpdateWorkspaceAutostartParams) error
|
||||
UpdateWorkspaceBuildByID(ctx context.Context, arg UpdateWorkspaceBuildByIDParams) error
|
||||
UpdateWorkspaceDeletedByID(ctx context.Context, arg UpdateWorkspaceDeletedByIDParams) error
|
||||
UpdateWorkspaceLastUsedAt(ctx context.Context, arg UpdateWorkspaceLastUsedAtParams) error
|
||||
UpdateWorkspaceTTL(ctx context.Context, arg UpdateWorkspaceTTLParams) error
|
||||
}
|
||||
|
||||
|
||||
@@ -4562,7 +4562,7 @@ func (q *sqlQuerier) InsertWorkspaceResourceMetadata(ctx context.Context, arg In
|
||||
|
||||
const getWorkspaceByID = `-- name: GetWorkspaceByID :one
|
||||
SELECT
|
||||
id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl
|
||||
id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl, last_used_at
|
||||
FROM
|
||||
workspaces
|
||||
WHERE
|
||||
@@ -4585,13 +4585,14 @@ func (q *sqlQuerier) GetWorkspaceByID(ctx context.Context, id uuid.UUID) (Worksp
|
||||
&i.Name,
|
||||
&i.AutostartSchedule,
|
||||
&i.Ttl,
|
||||
&i.LastUsedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getWorkspaceByOwnerIDAndName = `-- name: GetWorkspaceByOwnerIDAndName :one
|
||||
SELECT
|
||||
id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl
|
||||
id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl, last_used_at
|
||||
FROM
|
||||
workspaces
|
||||
WHERE
|
||||
@@ -4621,6 +4622,7 @@ func (q *sqlQuerier) GetWorkspaceByOwnerIDAndName(ctx context.Context, arg GetWo
|
||||
&i.Name,
|
||||
&i.AutostartSchedule,
|
||||
&i.Ttl,
|
||||
&i.LastUsedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
@@ -4669,7 +4671,7 @@ func (q *sqlQuerier) GetWorkspaceOwnerCountsByTemplateIDs(ctx context.Context, i
|
||||
|
||||
const getWorkspaces = `-- name: GetWorkspaces :many
|
||||
SELECT
|
||||
id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl
|
||||
id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl, last_used_at
|
||||
FROM
|
||||
workspaces
|
||||
WHERE
|
||||
@@ -4745,6 +4747,7 @@ func (q *sqlQuerier) GetWorkspaces(ctx context.Context, arg GetWorkspacesParams)
|
||||
&i.Name,
|
||||
&i.AutostartSchedule,
|
||||
&i.Ttl,
|
||||
&i.LastUsedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -4761,7 +4764,7 @@ func (q *sqlQuerier) GetWorkspaces(ctx context.Context, arg GetWorkspacesParams)
|
||||
|
||||
const getWorkspacesAutostart = `-- name: GetWorkspacesAutostart :many
|
||||
SELECT
|
||||
id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl
|
||||
id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl, last_used_at
|
||||
FROM
|
||||
workspaces
|
||||
WHERE
|
||||
@@ -4794,6 +4797,7 @@ func (q *sqlQuerier) GetWorkspacesAutostart(ctx context.Context) ([]Workspace, e
|
||||
&i.Name,
|
||||
&i.AutostartSchedule,
|
||||
&i.Ttl,
|
||||
&i.LastUsedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -4822,7 +4826,7 @@ INSERT INTO
|
||||
ttl
|
||||
)
|
||||
VALUES
|
||||
($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl
|
||||
($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl, last_used_at
|
||||
`
|
||||
|
||||
type InsertWorkspaceParams struct {
|
||||
@@ -4861,6 +4865,7 @@ func (q *sqlQuerier) InsertWorkspace(ctx context.Context, arg InsertWorkspacePar
|
||||
&i.Name,
|
||||
&i.AutostartSchedule,
|
||||
&i.Ttl,
|
||||
&i.LastUsedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
@@ -4873,7 +4878,7 @@ SET
|
||||
WHERE
|
||||
id = $1
|
||||
AND deleted = false
|
||||
RETURNING id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl
|
||||
RETURNING id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl, last_used_at
|
||||
`
|
||||
|
||||
type UpdateWorkspaceParams struct {
|
||||
@@ -4895,6 +4900,7 @@ func (q *sqlQuerier) UpdateWorkspace(ctx context.Context, arg UpdateWorkspacePar
|
||||
&i.Name,
|
||||
&i.AutostartSchedule,
|
||||
&i.Ttl,
|
||||
&i.LastUsedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
@@ -4937,6 +4943,25 @@ func (q *sqlQuerier) UpdateWorkspaceDeletedByID(ctx context.Context, arg UpdateW
|
||||
return err
|
||||
}
|
||||
|
||||
const updateWorkspaceLastUsedAt = `-- name: UpdateWorkspaceLastUsedAt :exec
|
||||
UPDATE
|
||||
workspaces
|
||||
SET
|
||||
last_used_at = $2
|
||||
WHERE
|
||||
id = $1
|
||||
`
|
||||
|
||||
type UpdateWorkspaceLastUsedAtParams struct {
|
||||
ID uuid.UUID `db:"id" json:"id"`
|
||||
LastUsedAt time.Time `db:"last_used_at" json:"last_used_at"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) UpdateWorkspaceLastUsedAt(ctx context.Context, arg UpdateWorkspaceLastUsedAtParams) error {
|
||||
_, err := q.db.ExecContext(ctx, updateWorkspaceLastUsedAt, arg.ID, arg.LastUsedAt)
|
||||
return err
|
||||
}
|
||||
|
||||
const updateWorkspaceTTL = `-- name: UpdateWorkspaceTTL :exec
|
||||
UPDATE
|
||||
workspaces
|
||||
|
||||
@@ -137,3 +137,11 @@ SET
|
||||
ttl = $2
|
||||
WHERE
|
||||
id = $1;
|
||||
|
||||
-- name: UpdateWorkspaceLastUsedAt :exec
|
||||
UPDATE
|
||||
workspaces
|
||||
SET
|
||||
last_used_at = $2
|
||||
WHERE
|
||||
id = $1;
|
||||
|
||||
@@ -608,6 +608,10 @@ func TestTemplateDAUs(t *testing.T) {
|
||||
Entries: []codersdk.DAUEntry{},
|
||||
}, daus, "no DAUs when stats are empty")
|
||||
|
||||
workspaces, err := client.Workspaces(ctx, codersdk.WorkspaceFilter{})
|
||||
require.NoError(t, err)
|
||||
assert.Zero(t, workspaces[0].LastUsedAt)
|
||||
|
||||
conn, err := client.DialWorkspaceAgent(ctx, resources[0].Agents[0].ID, opts)
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
@@ -641,4 +645,10 @@ func TestTemplateDAUs(t *testing.T) {
|
||||
testutil.WaitShort, testutil.IntervalFast,
|
||||
"got %+v != %+v", daus, want,
|
||||
)
|
||||
|
||||
workspaces, err = client.Workspaces(ctx, codersdk.WorkspaceFilter{})
|
||||
require.NoError(t, err)
|
||||
assert.WithinDuration(t,
|
||||
time.Now(), workspaces[0].LastUsedAt, time.Minute,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -830,18 +830,20 @@ func (api *API) workspaceAgentReportStats(rw http.ResponseWriter, r *http.Reques
|
||||
// We will see duplicate reports when on idle connections
|
||||
// (e.g. web terminal left open) or when there are no connections at
|
||||
// all.
|
||||
var insert = !reflect.DeepEqual(lastReport, rep)
|
||||
// We also don't want to update the workspace last used at on duplicate
|
||||
// reports.
|
||||
var updateDB = !reflect.DeepEqual(lastReport, rep)
|
||||
|
||||
api.Logger.Debug(ctx, "read stats report",
|
||||
slog.F("interval", api.AgentStatsRefreshInterval),
|
||||
slog.F("agent", workspaceAgent.ID),
|
||||
slog.F("resource", resource.ID),
|
||||
slog.F("workspace", workspace.ID),
|
||||
slog.F("insert", insert),
|
||||
slog.F("update_db", updateDB),
|
||||
slog.F("payload", rep),
|
||||
)
|
||||
|
||||
if insert {
|
||||
if updateDB {
|
||||
lastReport = rep
|
||||
|
||||
_, err = api.Database.InsertAgentStat(ctx, database.InsertAgentStatParams{
|
||||
@@ -860,6 +862,18 @@ func (api *API) workspaceAgentReportStats(rw http.ResponseWriter, r *http.Reques
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
err = api.Database.UpdateWorkspaceLastUsedAt(ctx, database.UpdateWorkspaceLastUsedAtParams{
|
||||
ID: build.WorkspaceID,
|
||||
LastUsedAt: time.Now(),
|
||||
})
|
||||
if err != nil {
|
||||
httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{
|
||||
Message: "Failed to update workspace last used at.",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
|
||||
@@ -941,6 +941,7 @@ func convertWorkspace(
|
||||
Name: workspace.Name,
|
||||
AutostartSchedule: autostartSchedule,
|
||||
TTLMillis: ttlMillis,
|
||||
LastUsedAt: workspace.LastUsedAt,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user