mirror of
https://github.com/coder/coder.git
synced 2026-10-28 02:42:54 +08:00
fix: introduce dedicated queries for workspaces and workspace agents metrics (#19786)
aid in differentiation between sources of calls to `GetWorkspaces` but introducing new queries for metrics specific use cases --------- Signed-off-by: Callum Styan <callumstyan@gmail.com>
This commit is contained in:
@@ -3552,6 +3552,13 @@ func (q *querier) GetWorkspaceAgentsCreatedAfter(ctx context.Context, createdAt
|
|||||||
return q.db.GetWorkspaceAgentsCreatedAfter(ctx, createdAt)
|
return q.db.GetWorkspaceAgentsCreatedAfter(ctx, createdAt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (q *querier) GetWorkspaceAgentsForMetrics(ctx context.Context) ([]database.GetWorkspaceAgentsForMetricsRow, error) {
|
||||||
|
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceWorkspace); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return q.db.GetWorkspaceAgentsForMetrics(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
func (q *querier) GetWorkspaceAgentsInLatestBuildByWorkspaceID(ctx context.Context, workspaceID uuid.UUID) ([]database.WorkspaceAgent, error) {
|
func (q *querier) GetWorkspaceAgentsInLatestBuildByWorkspaceID(ctx context.Context, workspaceID uuid.UUID) ([]database.WorkspaceAgent, error) {
|
||||||
workspace, err := q.GetWorkspaceByID(ctx, workspaceID)
|
workspace, err := q.GetWorkspaceByID(ctx, workspaceID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -3857,6 +3864,13 @@ func (q *querier) GetWorkspacesEligibleForTransition(ctx context.Context, now ti
|
|||||||
return q.db.GetWorkspacesEligibleForTransition(ctx, now)
|
return q.db.GetWorkspacesEligibleForTransition(ctx, now)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (q *querier) GetWorkspacesForWorkspaceMetrics(ctx context.Context) ([]database.GetWorkspacesForWorkspaceMetricsRow, error) {
|
||||||
|
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceWorkspace); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return q.db.GetWorkspacesForWorkspaceMetrics(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
func (q *querier) InsertAIBridgeInterception(ctx context.Context, arg database.InsertAIBridgeInterceptionParams) (database.AIBridgeInterception, error) {
|
func (q *querier) InsertAIBridgeInterception(ctx context.Context, arg database.InsertAIBridgeInterceptionParams) (database.AIBridgeInterception, error) {
|
||||||
return insert(q.log, q.auth, rbac.ResourceAibridgeInterception.WithOwner(arg.InitiatorID.String()), q.db.InsertAIBridgeInterception)(ctx, arg)
|
return insert(q.log, q.auth, rbac.ResourceAibridgeInterception.WithOwner(arg.InitiatorID.String()), q.db.InsertAIBridgeInterception)(ctx, arg)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1689,6 +1689,15 @@ func (s *MethodTestSuite) TestWorkspace() {
|
|||||||
// No asserts here because SQLFilter.
|
// No asserts here because SQLFilter.
|
||||||
check.Args(arg).Asserts()
|
check.Args(arg).Asserts()
|
||||||
}))
|
}))
|
||||||
|
s.Run("GetWorkspaceAgentsForMetrics", s.Mocked(func(dbm *dbmock.MockStore, faker *gofakeit.Faker, check *expects) {
|
||||||
|
row := testutil.Fake(s.T(), faker, database.GetWorkspaceAgentsForMetricsRow{})
|
||||||
|
dbm.EXPECT().GetWorkspaceAgentsForMetrics(gomock.Any()).Return([]database.GetWorkspaceAgentsForMetricsRow{row}, nil).AnyTimes()
|
||||||
|
check.Args().Asserts(rbac.ResourceWorkspace, policy.ActionRead).Returns([]database.GetWorkspaceAgentsForMetricsRow{row})
|
||||||
|
}))
|
||||||
|
s.Run("GetWorkspacesForWorkspaceMetrics", s.Mocked(func(dbm *dbmock.MockStore, _ *gofakeit.Faker, check *expects) {
|
||||||
|
dbm.EXPECT().GetWorkspacesForWorkspaceMetrics(gomock.Any()).Return([]database.GetWorkspacesForWorkspaceMetricsRow{}, nil).AnyTimes()
|
||||||
|
check.Args().Asserts(rbac.ResourceWorkspace, policy.ActionRead)
|
||||||
|
}))
|
||||||
s.Run("GetAuthorizedWorkspaces", s.Mocked(func(dbm *dbmock.MockStore, _ *gofakeit.Faker, check *expects) {
|
s.Run("GetAuthorizedWorkspaces", s.Mocked(func(dbm *dbmock.MockStore, _ *gofakeit.Faker, check *expects) {
|
||||||
arg := database.GetWorkspacesParams{}
|
arg := database.GetWorkspacesParams{}
|
||||||
dbm.EXPECT().GetAuthorizedWorkspaces(gomock.Any(), arg, gomock.Any()).Return([]database.GetWorkspacesRow{}, nil).AnyTimes()
|
dbm.EXPECT().GetAuthorizedWorkspaces(gomock.Any(), arg, gomock.Any()).Return([]database.GetWorkspacesRow{}, nil).AnyTimes()
|
||||||
|
|||||||
@@ -1972,6 +1972,13 @@ func (m queryMetricsStore) GetWorkspaceAgentsCreatedAfter(ctx context.Context, c
|
|||||||
return agents, err
|
return agents, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m queryMetricsStore) GetWorkspaceAgentsForMetrics(ctx context.Context) ([]database.GetWorkspaceAgentsForMetricsRow, error) {
|
||||||
|
start := time.Now()
|
||||||
|
r0, r1 := m.s.GetWorkspaceAgentsForMetrics(ctx)
|
||||||
|
m.queryLatencies.WithLabelValues("GetWorkspaceAgentsForMetrics").Observe(time.Since(start).Seconds())
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
func (m queryMetricsStore) GetWorkspaceAgentsInLatestBuildByWorkspaceID(ctx context.Context, workspaceID uuid.UUID) ([]database.WorkspaceAgent, error) {
|
func (m queryMetricsStore) GetWorkspaceAgentsInLatestBuildByWorkspaceID(ctx context.Context, workspaceID uuid.UUID) ([]database.WorkspaceAgent, error) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
agents, err := m.s.GetWorkspaceAgentsInLatestBuildByWorkspaceID(ctx, workspaceID)
|
agents, err := m.s.GetWorkspaceAgentsInLatestBuildByWorkspaceID(ctx, workspaceID)
|
||||||
@@ -2224,6 +2231,13 @@ func (m queryMetricsStore) GetWorkspacesEligibleForTransition(ctx context.Contex
|
|||||||
return workspaces, err
|
return workspaces, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m queryMetricsStore) GetWorkspacesForWorkspaceMetrics(ctx context.Context) ([]database.GetWorkspacesForWorkspaceMetricsRow, error) {
|
||||||
|
start := time.Now()
|
||||||
|
r0, r1 := m.s.GetWorkspacesForWorkspaceMetrics(ctx)
|
||||||
|
m.queryLatencies.WithLabelValues("GetWorkspacesForWorkspaceMetrics").Observe(time.Since(start).Seconds())
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
func (m queryMetricsStore) InsertAIBridgeInterception(ctx context.Context, arg database.InsertAIBridgeInterceptionParams) (database.AIBridgeInterception, error) {
|
func (m queryMetricsStore) InsertAIBridgeInterception(ctx context.Context, arg database.InsertAIBridgeInterceptionParams) (database.AIBridgeInterception, error) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
r0, r1 := m.s.InsertAIBridgeInterception(ctx, arg)
|
r0, r1 := m.s.InsertAIBridgeInterception(ctx, arg)
|
||||||
|
|||||||
@@ -4199,6 +4199,21 @@ func (mr *MockStoreMockRecorder) GetWorkspaceAgentsCreatedAfter(ctx, createdAt a
|
|||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWorkspaceAgentsCreatedAfter", reflect.TypeOf((*MockStore)(nil).GetWorkspaceAgentsCreatedAfter), ctx, createdAt)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWorkspaceAgentsCreatedAfter", reflect.TypeOf((*MockStore)(nil).GetWorkspaceAgentsCreatedAfter), ctx, createdAt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetWorkspaceAgentsForMetrics mocks base method.
|
||||||
|
func (m *MockStore) GetWorkspaceAgentsForMetrics(ctx context.Context) ([]database.GetWorkspaceAgentsForMetricsRow, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "GetWorkspaceAgentsForMetrics", ctx)
|
||||||
|
ret0, _ := ret[0].([]database.GetWorkspaceAgentsForMetricsRow)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetWorkspaceAgentsForMetrics indicates an expected call of GetWorkspaceAgentsForMetrics.
|
||||||
|
func (mr *MockStoreMockRecorder) GetWorkspaceAgentsForMetrics(ctx any) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWorkspaceAgentsForMetrics", reflect.TypeOf((*MockStore)(nil).GetWorkspaceAgentsForMetrics), ctx)
|
||||||
|
}
|
||||||
|
|
||||||
// GetWorkspaceAgentsInLatestBuildByWorkspaceID mocks base method.
|
// GetWorkspaceAgentsInLatestBuildByWorkspaceID mocks base method.
|
||||||
func (m *MockStore) GetWorkspaceAgentsInLatestBuildByWorkspaceID(ctx context.Context, workspaceID uuid.UUID) ([]database.WorkspaceAgent, error) {
|
func (m *MockStore) GetWorkspaceAgentsInLatestBuildByWorkspaceID(ctx context.Context, workspaceID uuid.UUID) ([]database.WorkspaceAgent, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
@@ -4739,6 +4754,21 @@ func (mr *MockStoreMockRecorder) GetWorkspacesEligibleForTransition(ctx, now any
|
|||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWorkspacesEligibleForTransition", reflect.TypeOf((*MockStore)(nil).GetWorkspacesEligibleForTransition), ctx, now)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWorkspacesEligibleForTransition", reflect.TypeOf((*MockStore)(nil).GetWorkspacesEligibleForTransition), ctx, now)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetWorkspacesForWorkspaceMetrics mocks base method.
|
||||||
|
func (m *MockStore) GetWorkspacesForWorkspaceMetrics(ctx context.Context) ([]database.GetWorkspacesForWorkspaceMetricsRow, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "GetWorkspacesForWorkspaceMetrics", ctx)
|
||||||
|
ret0, _ := ret[0].([]database.GetWorkspacesForWorkspaceMetricsRow)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetWorkspacesForWorkspaceMetrics indicates an expected call of GetWorkspacesForWorkspaceMetrics.
|
||||||
|
func (mr *MockStoreMockRecorder) GetWorkspacesForWorkspaceMetrics(ctx any) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWorkspacesForWorkspaceMetrics", reflect.TypeOf((*MockStore)(nil).GetWorkspacesForWorkspaceMetrics), ctx)
|
||||||
|
}
|
||||||
|
|
||||||
// InTx mocks base method.
|
// InTx mocks base method.
|
||||||
func (m *MockStore) InTx(arg0 func(database.Store) error, arg1 *database.TxOptions) error {
|
func (m *MockStore) InTx(arg0 func(database.Store) error, arg1 *database.TxOptions) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
|
|||||||
@@ -464,6 +464,7 @@ type sqlcQuerier interface {
|
|||||||
GetWorkspaceAgentsByResourceIDs(ctx context.Context, ids []uuid.UUID) ([]WorkspaceAgent, error)
|
GetWorkspaceAgentsByResourceIDs(ctx context.Context, ids []uuid.UUID) ([]WorkspaceAgent, error)
|
||||||
GetWorkspaceAgentsByWorkspaceAndBuildNumber(ctx context.Context, arg GetWorkspaceAgentsByWorkspaceAndBuildNumberParams) ([]WorkspaceAgent, error)
|
GetWorkspaceAgentsByWorkspaceAndBuildNumber(ctx context.Context, arg GetWorkspaceAgentsByWorkspaceAndBuildNumberParams) ([]WorkspaceAgent, error)
|
||||||
GetWorkspaceAgentsCreatedAfter(ctx context.Context, createdAt time.Time) ([]WorkspaceAgent, error)
|
GetWorkspaceAgentsCreatedAfter(ctx context.Context, createdAt time.Time) ([]WorkspaceAgent, error)
|
||||||
|
GetWorkspaceAgentsForMetrics(ctx context.Context) ([]GetWorkspaceAgentsForMetricsRow, error)
|
||||||
GetWorkspaceAgentsInLatestBuildByWorkspaceID(ctx context.Context, workspaceID uuid.UUID) ([]WorkspaceAgent, error)
|
GetWorkspaceAgentsInLatestBuildByWorkspaceID(ctx context.Context, workspaceID uuid.UUID) ([]WorkspaceAgent, error)
|
||||||
GetWorkspaceAppByAgentIDAndSlug(ctx context.Context, arg GetWorkspaceAppByAgentIDAndSlugParams) (WorkspaceApp, error)
|
GetWorkspaceAppByAgentIDAndSlug(ctx context.Context, arg GetWorkspaceAppByAgentIDAndSlugParams) (WorkspaceApp, error)
|
||||||
GetWorkspaceAppStatusesByAppIDs(ctx context.Context, ids []uuid.UUID) ([]WorkspaceAppStatus, error)
|
GetWorkspaceAppStatusesByAppIDs(ctx context.Context, ids []uuid.UUID) ([]WorkspaceAppStatus, error)
|
||||||
@@ -510,6 +511,7 @@ type sqlcQuerier interface {
|
|||||||
GetWorkspacesAndAgentsByOwnerID(ctx context.Context, ownerID uuid.UUID) ([]GetWorkspacesAndAgentsByOwnerIDRow, error)
|
GetWorkspacesAndAgentsByOwnerID(ctx context.Context, ownerID uuid.UUID) ([]GetWorkspacesAndAgentsByOwnerIDRow, error)
|
||||||
GetWorkspacesByTemplateID(ctx context.Context, templateID uuid.UUID) ([]WorkspaceTable, error)
|
GetWorkspacesByTemplateID(ctx context.Context, templateID uuid.UUID) ([]WorkspaceTable, error)
|
||||||
GetWorkspacesEligibleForTransition(ctx context.Context, now time.Time) ([]GetWorkspacesEligibleForTransitionRow, error)
|
GetWorkspacesEligibleForTransition(ctx context.Context, now time.Time) ([]GetWorkspacesEligibleForTransitionRow, error)
|
||||||
|
GetWorkspacesForWorkspaceMetrics(ctx context.Context) ([]GetWorkspacesForWorkspaceMetricsRow, error)
|
||||||
InsertAIBridgeInterception(ctx context.Context, arg InsertAIBridgeInterceptionParams) (AIBridgeInterception, error)
|
InsertAIBridgeInterception(ctx context.Context, arg InsertAIBridgeInterceptionParams) (AIBridgeInterception, error)
|
||||||
InsertAIBridgeTokenUsage(ctx context.Context, arg InsertAIBridgeTokenUsageParams) (AIBridgeTokenUsage, error)
|
InsertAIBridgeTokenUsage(ctx context.Context, arg InsertAIBridgeTokenUsageParams) (AIBridgeTokenUsage, error)
|
||||||
InsertAIBridgeToolUsage(ctx context.Context, arg InsertAIBridgeToolUsageParams) (AIBridgeToolUsage, error)
|
InsertAIBridgeToolUsage(ctx context.Context, arg InsertAIBridgeToolUsageParams) (AIBridgeToolUsage, error)
|
||||||
|
|||||||
@@ -17645,6 +17645,102 @@ func (q *sqlQuerier) GetWorkspaceAgentsCreatedAfter(ctx context.Context, created
|
|||||||
return items, nil
|
return items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getWorkspaceAgentsForMetrics = `-- name: GetWorkspaceAgentsForMetrics :many
|
||||||
|
SELECT
|
||||||
|
w.id as workspace_id,
|
||||||
|
w.name as workspace_name,
|
||||||
|
u.username as owner_username,
|
||||||
|
t.name as template_name,
|
||||||
|
tv.name as template_version_name,
|
||||||
|
workspace_agents.id, workspace_agents.created_at, workspace_agents.updated_at, workspace_agents.name, workspace_agents.first_connected_at, workspace_agents.last_connected_at, workspace_agents.disconnected_at, workspace_agents.resource_id, workspace_agents.auth_token, workspace_agents.auth_instance_id, workspace_agents.architecture, workspace_agents.environment_variables, workspace_agents.operating_system, workspace_agents.instance_metadata, workspace_agents.resource_metadata, workspace_agents.directory, workspace_agents.version, workspace_agents.last_connected_replica_id, workspace_agents.connection_timeout_seconds, workspace_agents.troubleshooting_url, workspace_agents.motd_file, workspace_agents.lifecycle_state, workspace_agents.expanded_directory, workspace_agents.logs_length, workspace_agents.logs_overflowed, workspace_agents.started_at, workspace_agents.ready_at, workspace_agents.subsystems, workspace_agents.display_apps, workspace_agents.api_version, workspace_agents.display_order, workspace_agents.parent_id, workspace_agents.api_key_scope, workspace_agents.deleted
|
||||||
|
FROM workspaces w
|
||||||
|
JOIN users u ON w.owner_id = u.id
|
||||||
|
JOIN templates t ON w.template_id = t.id
|
||||||
|
JOIN workspace_builds wb ON w.id = wb.workspace_id
|
||||||
|
LEFT JOIN template_versions tv ON wb.template_version_id = tv.id
|
||||||
|
JOIN workspace_resources wr ON wb.job_id = wr.job_id
|
||||||
|
JOIN workspace_agents ON wr.id = workspace_agents.resource_id
|
||||||
|
WHERE w.deleted = false
|
||||||
|
AND wb.build_number = (
|
||||||
|
SELECT MAX(wb2.build_number)
|
||||||
|
FROM workspace_builds wb2
|
||||||
|
WHERE wb2.workspace_id = w.id
|
||||||
|
)
|
||||||
|
AND workspace_agents.deleted = FALSE
|
||||||
|
`
|
||||||
|
|
||||||
|
type GetWorkspaceAgentsForMetricsRow struct {
|
||||||
|
WorkspaceID uuid.UUID `db:"workspace_id" json:"workspace_id"`
|
||||||
|
WorkspaceName string `db:"workspace_name" json:"workspace_name"`
|
||||||
|
OwnerUsername string `db:"owner_username" json:"owner_username"`
|
||||||
|
TemplateName string `db:"template_name" json:"template_name"`
|
||||||
|
TemplateVersionName sql.NullString `db:"template_version_name" json:"template_version_name"`
|
||||||
|
WorkspaceAgent WorkspaceAgent `db:"workspace_agent" json:"workspace_agent"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *sqlQuerier) GetWorkspaceAgentsForMetrics(ctx context.Context) ([]GetWorkspaceAgentsForMetricsRow, error) {
|
||||||
|
rows, err := q.db.QueryContext(ctx, getWorkspaceAgentsForMetrics)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []GetWorkspaceAgentsForMetricsRow
|
||||||
|
for rows.Next() {
|
||||||
|
var i GetWorkspaceAgentsForMetricsRow
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.WorkspaceID,
|
||||||
|
&i.WorkspaceName,
|
||||||
|
&i.OwnerUsername,
|
||||||
|
&i.TemplateName,
|
||||||
|
&i.TemplateVersionName,
|
||||||
|
&i.WorkspaceAgent.ID,
|
||||||
|
&i.WorkspaceAgent.CreatedAt,
|
||||||
|
&i.WorkspaceAgent.UpdatedAt,
|
||||||
|
&i.WorkspaceAgent.Name,
|
||||||
|
&i.WorkspaceAgent.FirstConnectedAt,
|
||||||
|
&i.WorkspaceAgent.LastConnectedAt,
|
||||||
|
&i.WorkspaceAgent.DisconnectedAt,
|
||||||
|
&i.WorkspaceAgent.ResourceID,
|
||||||
|
&i.WorkspaceAgent.AuthToken,
|
||||||
|
&i.WorkspaceAgent.AuthInstanceID,
|
||||||
|
&i.WorkspaceAgent.Architecture,
|
||||||
|
&i.WorkspaceAgent.EnvironmentVariables,
|
||||||
|
&i.WorkspaceAgent.OperatingSystem,
|
||||||
|
&i.WorkspaceAgent.InstanceMetadata,
|
||||||
|
&i.WorkspaceAgent.ResourceMetadata,
|
||||||
|
&i.WorkspaceAgent.Directory,
|
||||||
|
&i.WorkspaceAgent.Version,
|
||||||
|
&i.WorkspaceAgent.LastConnectedReplicaID,
|
||||||
|
&i.WorkspaceAgent.ConnectionTimeoutSeconds,
|
||||||
|
&i.WorkspaceAgent.TroubleshootingURL,
|
||||||
|
&i.WorkspaceAgent.MOTDFile,
|
||||||
|
&i.WorkspaceAgent.LifecycleState,
|
||||||
|
&i.WorkspaceAgent.ExpandedDirectory,
|
||||||
|
&i.WorkspaceAgent.LogsLength,
|
||||||
|
&i.WorkspaceAgent.LogsOverflowed,
|
||||||
|
&i.WorkspaceAgent.StartedAt,
|
||||||
|
&i.WorkspaceAgent.ReadyAt,
|
||||||
|
pq.Array(&i.WorkspaceAgent.Subsystems),
|
||||||
|
pq.Array(&i.WorkspaceAgent.DisplayApps),
|
||||||
|
&i.WorkspaceAgent.APIVersion,
|
||||||
|
&i.WorkspaceAgent.DisplayOrder,
|
||||||
|
&i.WorkspaceAgent.ParentID,
|
||||||
|
&i.WorkspaceAgent.APIKeyScope,
|
||||||
|
&i.WorkspaceAgent.Deleted,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Close(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
const getWorkspaceAgentsInLatestBuildByWorkspaceID = `-- name: GetWorkspaceAgentsInLatestBuildByWorkspaceID :many
|
const getWorkspaceAgentsInLatestBuildByWorkspaceID = `-- name: GetWorkspaceAgentsInLatestBuildByWorkspaceID :many
|
||||||
SELECT
|
SELECT
|
||||||
workspace_agents.id, workspace_agents.created_at, workspace_agents.updated_at, workspace_agents.name, workspace_agents.first_connected_at, workspace_agents.last_connected_at, workspace_agents.disconnected_at, workspace_agents.resource_id, workspace_agents.auth_token, workspace_agents.auth_instance_id, workspace_agents.architecture, workspace_agents.environment_variables, workspace_agents.operating_system, workspace_agents.instance_metadata, workspace_agents.resource_metadata, workspace_agents.directory, workspace_agents.version, workspace_agents.last_connected_replica_id, workspace_agents.connection_timeout_seconds, workspace_agents.troubleshooting_url, workspace_agents.motd_file, workspace_agents.lifecycle_state, workspace_agents.expanded_directory, workspace_agents.logs_length, workspace_agents.logs_overflowed, workspace_agents.started_at, workspace_agents.ready_at, workspace_agents.subsystems, workspace_agents.display_apps, workspace_agents.api_version, workspace_agents.display_order, workspace_agents.parent_id, workspace_agents.api_key_scope, workspace_agents.deleted
|
workspace_agents.id, workspace_agents.created_at, workspace_agents.updated_at, workspace_agents.name, workspace_agents.first_connected_at, workspace_agents.last_connected_at, workspace_agents.disconnected_at, workspace_agents.resource_id, workspace_agents.auth_token, workspace_agents.auth_instance_id, workspace_agents.architecture, workspace_agents.environment_variables, workspace_agents.operating_system, workspace_agents.instance_metadata, workspace_agents.resource_metadata, workspace_agents.directory, workspace_agents.version, workspace_agents.last_connected_replica_id, workspace_agents.connection_timeout_seconds, workspace_agents.troubleshooting_url, workspace_agents.motd_file, workspace_agents.lifecycle_state, workspace_agents.expanded_directory, workspace_agents.logs_length, workspace_agents.logs_overflowed, workspace_agents.started_at, workspace_agents.ready_at, workspace_agents.subsystems, workspace_agents.display_apps, workspace_agents.api_version, workspace_agents.display_order, workspace_agents.parent_id, workspace_agents.api_key_scope, workspace_agents.deleted
|
||||||
@@ -22354,6 +22450,64 @@ func (q *sqlQuerier) GetWorkspacesEligibleForTransition(ctx context.Context, now
|
|||||||
return items, nil
|
return items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getWorkspacesForWorkspaceMetrics = `-- name: GetWorkspacesForWorkspaceMetrics :many
|
||||||
|
SELECT
|
||||||
|
u.username as owner_username,
|
||||||
|
t.name as template_name,
|
||||||
|
tv.name as template_version_name,
|
||||||
|
pj.job_status as latest_build_status,
|
||||||
|
wb.transition as latest_build_transition
|
||||||
|
FROM workspaces w
|
||||||
|
JOIN users u ON w.owner_id = u.id
|
||||||
|
JOIN templates t ON w.template_id = t.id
|
||||||
|
JOIN workspace_builds wb ON w.id = wb.workspace_id
|
||||||
|
JOIN provisioner_jobs pj ON wb.job_id = pj.id
|
||||||
|
LEFT JOIN template_versions tv ON wb.template_version_id = tv.id
|
||||||
|
WHERE w.deleted = false
|
||||||
|
AND wb.build_number = (
|
||||||
|
SELECT MAX(wb2.build_number)
|
||||||
|
FROM workspace_builds wb2
|
||||||
|
WHERE wb2.workspace_id = w.id
|
||||||
|
)
|
||||||
|
`
|
||||||
|
|
||||||
|
type GetWorkspacesForWorkspaceMetricsRow struct {
|
||||||
|
OwnerUsername string `db:"owner_username" json:"owner_username"`
|
||||||
|
TemplateName string `db:"template_name" json:"template_name"`
|
||||||
|
TemplateVersionName sql.NullString `db:"template_version_name" json:"template_version_name"`
|
||||||
|
LatestBuildStatus ProvisionerJobStatus `db:"latest_build_status" json:"latest_build_status"`
|
||||||
|
LatestBuildTransition WorkspaceTransition `db:"latest_build_transition" json:"latest_build_transition"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *sqlQuerier) GetWorkspacesForWorkspaceMetrics(ctx context.Context) ([]GetWorkspacesForWorkspaceMetricsRow, error) {
|
||||||
|
rows, err := q.db.QueryContext(ctx, getWorkspacesForWorkspaceMetrics)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []GetWorkspacesForWorkspaceMetricsRow
|
||||||
|
for rows.Next() {
|
||||||
|
var i GetWorkspacesForWorkspaceMetricsRow
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.OwnerUsername,
|
||||||
|
&i.TemplateName,
|
||||||
|
&i.TemplateVersionName,
|
||||||
|
&i.LatestBuildStatus,
|
||||||
|
&i.LatestBuildTransition,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Close(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
const insertWorkspace = `-- name: InsertWorkspace :one
|
const insertWorkspace = `-- name: InsertWorkspace :one
|
||||||
INSERT INTO
|
INSERT INTO
|
||||||
workspaces (
|
workspaces (
|
||||||
|
|||||||
@@ -365,3 +365,26 @@ WHERE
|
|||||||
id = $1
|
id = $1
|
||||||
AND parent_id IS NOT NULL
|
AND parent_id IS NOT NULL
|
||||||
AND deleted = FALSE;
|
AND deleted = FALSE;
|
||||||
|
|
||||||
|
-- name: GetWorkspaceAgentsForMetrics :many
|
||||||
|
SELECT
|
||||||
|
w.id as workspace_id,
|
||||||
|
w.name as workspace_name,
|
||||||
|
u.username as owner_username,
|
||||||
|
t.name as template_name,
|
||||||
|
tv.name as template_version_name,
|
||||||
|
sqlc.embed(workspace_agents)
|
||||||
|
FROM workspaces w
|
||||||
|
JOIN users u ON w.owner_id = u.id
|
||||||
|
JOIN templates t ON w.template_id = t.id
|
||||||
|
JOIN workspace_builds wb ON w.id = wb.workspace_id
|
||||||
|
LEFT JOIN template_versions tv ON wb.template_version_id = tv.id
|
||||||
|
JOIN workspace_resources wr ON wb.job_id = wr.job_id
|
||||||
|
JOIN workspace_agents ON wr.id = workspace_agents.resource_id
|
||||||
|
WHERE w.deleted = false
|
||||||
|
AND wb.build_number = (
|
||||||
|
SELECT MAX(wb2.build_number)
|
||||||
|
FROM workspace_builds wb2
|
||||||
|
WHERE wb2.workspace_id = w.id
|
||||||
|
)
|
||||||
|
AND workspace_agents.deleted = FALSE;
|
||||||
|
|||||||
@@ -983,3 +983,23 @@ WHERE
|
|||||||
AND fsb.initiator_id != 'c42fdf75-3097-471c-8c33-fb52454d81c0'::uuid
|
AND fsb.initiator_id != 'c42fdf75-3097-471c-8c33-fb52454d81c0'::uuid
|
||||||
GROUP BY t.name, COALESCE(tvp.name, ''), o.name
|
GROUP BY t.name, COALESCE(tvp.name, ''), o.name
|
||||||
ORDER BY t.name, preset_name, o.name;
|
ORDER BY t.name, preset_name, o.name;
|
||||||
|
|
||||||
|
-- name: GetWorkspacesForWorkspaceMetrics :many
|
||||||
|
SELECT
|
||||||
|
u.username as owner_username,
|
||||||
|
t.name as template_name,
|
||||||
|
tv.name as template_version_name,
|
||||||
|
pj.job_status as latest_build_status,
|
||||||
|
wb.transition as latest_build_transition
|
||||||
|
FROM workspaces w
|
||||||
|
JOIN users u ON w.owner_id = u.id
|
||||||
|
JOIN templates t ON w.template_id = t.id
|
||||||
|
JOIN workspace_builds wb ON w.id = wb.workspace_id
|
||||||
|
JOIN provisioner_jobs pj ON wb.job_id = pj.id
|
||||||
|
LEFT JOIN template_versions tv ON wb.template_version_id = tv.id
|
||||||
|
WHERE w.deleted = false
|
||||||
|
AND wb.build_number = (
|
||||||
|
SELECT MAX(wb2.build_number)
|
||||||
|
FROM workspace_builds wb2
|
||||||
|
WHERE wb2.workspace_id = w.id
|
||||||
|
);
|
||||||
|
|||||||
@@ -181,10 +181,8 @@ func Workspaces(ctx context.Context, logger slog.Logger, registerer prometheus.R
|
|||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
|
|
||||||
updateWorkspaceMetrics := func() {
|
updateWorkspaceMetrics := func() {
|
||||||
ws, err := db.GetWorkspaces(ctx, database.GetWorkspacesParams{
|
// Don't count deleted workspaces as part of these metrics.
|
||||||
Deleted: false,
|
ws, err := db.GetWorkspacesForWorkspaceMetrics(ctx)
|
||||||
WithSummary: false,
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, sql.ErrNoRows) {
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
workspaceLatestBuildTotals.Reset()
|
workspaceLatestBuildTotals.Reset()
|
||||||
@@ -346,88 +344,67 @@ func Agents(ctx context.Context, logger slog.Logger, registerer prometheus.Regis
|
|||||||
timer := prometheus.NewTimer(metricsCollectorAgents)
|
timer := prometheus.NewTimer(metricsCollectorAgents)
|
||||||
derpMap := derpMapFn()
|
derpMap := derpMapFn()
|
||||||
|
|
||||||
workspaceRows, err := db.GetWorkspaces(ctx, database.GetWorkspacesParams{
|
workspaceAgents, err := db.GetWorkspaceAgentsForMetrics(ctx)
|
||||||
AgentInactiveDisconnectTimeoutSeconds: int64(agentInactiveDisconnectTimeout.Seconds()),
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(ctx, "can't get workspace rows", slog.Error(err))
|
logger.Error(ctx, "can't get workspace agents", slog.Error(err))
|
||||||
goto done
|
goto done
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, workspace := range workspaceRows {
|
for _, agent := range workspaceAgents {
|
||||||
templateName := workspace.TemplateName
|
// Collect information about agents
|
||||||
templateVersionName := workspace.TemplateVersionName.String
|
templateVersionName := agent.TemplateVersionName.String
|
||||||
if !workspace.TemplateVersionName.Valid {
|
if !agent.TemplateVersionName.Valid {
|
||||||
templateVersionName = "unknown"
|
templateVersionName = "unknown"
|
||||||
}
|
}
|
||||||
|
agentsGauge.WithLabelValues(VectorOperationAdd, 1, agent.OwnerUsername, agent.WorkspaceName, agent.TemplateName, templateVersionName)
|
||||||
|
|
||||||
// username :=
|
connectionStatus := agent.WorkspaceAgent.Status(agentInactiveDisconnectTimeout)
|
||||||
|
node := (*coordinator.Load()).Node(agent.WorkspaceAgent.ID)
|
||||||
|
|
||||||
agents, err := db.GetWorkspaceAgentsInLatestBuildByWorkspaceID(ctx, workspace.ID)
|
tailnetNode := "unknown"
|
||||||
if err != nil {
|
if node != nil {
|
||||||
logger.Error(ctx, "can't get workspace agents", slog.F("workspace_id", workspace.ID), slog.Error(err))
|
tailnetNode = node.ID.String()
|
||||||
agentsGauge.WithLabelValues(VectorOperationAdd, 0, workspace.OwnerUsername, workspace.Name, templateName, templateVersionName)
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(agents) == 0 {
|
agentsConnectionsGauge.WithLabelValues(VectorOperationSet, 1, agent.WorkspaceAgent.Name, agent.OwnerUsername, agent.WorkspaceName, string(connectionStatus.Status), string(agent.WorkspaceAgent.LifecycleState), tailnetNode)
|
||||||
logger.Debug(ctx, "workspace agents are unavailable", slog.F("workspace_id", workspace.ID))
|
|
||||||
agentsGauge.WithLabelValues(VectorOperationAdd, 0, workspace.OwnerUsername, workspace.Name, templateName, templateVersionName)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, agent := range agents {
|
if node == nil {
|
||||||
// Collect information about agents
|
logger.Debug(ctx, "can't read in-memory node for agent", slog.F("agent_id", agent.WorkspaceAgent.ID))
|
||||||
agentsGauge.WithLabelValues(VectorOperationAdd, 1, workspace.OwnerUsername, workspace.Name, templateName, templateVersionName)
|
} else {
|
||||||
|
// Collect information about connection latencies
|
||||||
connectionStatus := agent.Status(agentInactiveDisconnectTimeout)
|
for rawRegion, latency := range node.DERPLatency {
|
||||||
node := (*coordinator.Load()).Node(agent.ID)
|
regionParts := strings.SplitN(rawRegion, "-", 2)
|
||||||
|
regionID, err := strconv.Atoi(regionParts[0])
|
||||||
tailnetNode := "unknown"
|
if err != nil {
|
||||||
if node != nil {
|
logger.Error(ctx, "can't convert DERP region", slog.F("agent_id", agent.WorkspaceAgent.ID), slog.F("raw_region", rawRegion), slog.Error(err))
|
||||||
tailnetNode = node.ID.String()
|
continue
|
||||||
}
|
|
||||||
|
|
||||||
agentsConnectionsGauge.WithLabelValues(VectorOperationSet, 1, agent.Name, workspace.OwnerUsername, workspace.Name, string(connectionStatus.Status), string(agent.LifecycleState), tailnetNode)
|
|
||||||
|
|
||||||
if node == nil {
|
|
||||||
logger.Debug(ctx, "can't read in-memory node for agent", slog.F("agent_id", agent.ID))
|
|
||||||
} else {
|
|
||||||
// Collect information about connection latencies
|
|
||||||
for rawRegion, latency := range node.DERPLatency {
|
|
||||||
regionParts := strings.SplitN(rawRegion, "-", 2)
|
|
||||||
regionID, err := strconv.Atoi(regionParts[0])
|
|
||||||
if err != nil {
|
|
||||||
logger.Error(ctx, "can't convert DERP region", slog.F("agent_id", agent.ID), slog.F("raw_region", rawRegion), slog.Error(err))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
region, found := derpMap.Regions[regionID]
|
|
||||||
if !found {
|
|
||||||
// It's possible that a workspace agent is using an old DERPMap
|
|
||||||
// and reports regions that do not exist. If that's the case,
|
|
||||||
// report the region as unknown!
|
|
||||||
region = &tailcfg.DERPRegion{
|
|
||||||
RegionID: regionID,
|
|
||||||
RegionName: fmt.Sprintf("Unnamed %d", regionID),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
agentsConnectionLatenciesGauge.WithLabelValues(VectorOperationSet, latency, agent.Name, workspace.OwnerUsername, workspace.Name, region.RegionName, fmt.Sprintf("%v", node.PreferredDERP == regionID))
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Collect information about registered applications
|
region, found := derpMap.Regions[regionID]
|
||||||
apps, err := db.GetWorkspaceAppsByAgentID(ctx, agent.ID)
|
if !found {
|
||||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
// It's possible that a workspace agent is using an old DERPMap
|
||||||
logger.Error(ctx, "can't get workspace apps", slog.F("agent_id", agent.ID), slog.Error(err))
|
// and reports regions that do not exist. If that's the case,
|
||||||
continue
|
// report the region as unknown!
|
||||||
}
|
region = &tailcfg.DERPRegion{
|
||||||
|
RegionID: regionID,
|
||||||
|
RegionName: fmt.Sprintf("Unnamed %d", regionID),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for _, app := range apps {
|
agentsConnectionLatenciesGauge.WithLabelValues(VectorOperationSet, latency, agent.WorkspaceAgent.Name, agent.OwnerUsername, agent.WorkspaceName, region.RegionName, fmt.Sprintf("%v", node.PreferredDERP == regionID))
|
||||||
agentsAppsGauge.WithLabelValues(VectorOperationAdd, 1, agent.Name, workspace.OwnerUsername, workspace.Name, app.DisplayName, string(app.Health))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Collect information about registered applications
|
||||||
|
apps, err := db.GetWorkspaceAppsByAgentID(ctx, agent.WorkspaceAgent.ID)
|
||||||
|
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||||
|
logger.Error(ctx, "can't get workspace apps", slog.F("agent_id", agent.WorkspaceAgent.ID), slog.Error(err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, app := range apps {
|
||||||
|
agentsAppsGauge.WithLabelValues(VectorOperationAdd, 1, agent.WorkspaceAgent.Name, agent.OwnerUsername, agent.WorkspaceName, app.DisplayName, string(app.Health))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
agentsGauge.Commit()
|
agentsGauge.Commit()
|
||||||
|
|||||||
@@ -291,6 +291,7 @@ func TestWorkspaceLatestBuildTotals(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, metric := range m.Metric {
|
for _, metric := range m.Metric {
|
||||||
|
fmt.Printf("metric: %+v\n", metric)
|
||||||
count, ok := tc.Status[codersdk.ProvisionerJobStatus(metric.Label[0].GetValue())]
|
count, ok := tc.Status[codersdk.ProvisionerJobStatus(metric.Label[0].GetValue())]
|
||||||
if metric.Gauge.GetValue() == 0 {
|
if metric.Gauge.GetValue() == 0 {
|
||||||
continue
|
continue
|
||||||
|
|||||||
Reference in New Issue
Block a user