mirror of
https://github.com/coder/coder.git
synced 2026-10-29 17:44:33 +08:00
chore: convert agent stats to use a table (#6374)
* chore: convert workspace agent stats from json to table * chore: convert agent stats to use a table Backwards compatibility becomes hard when all agent stats are in a JSON blob. We also want to query this table for new agents that are failing health checks so we can display it in the UI. * Fix migration using default values
This commit is contained in:
+3
-3
@@ -767,12 +767,12 @@ func (a *agent) init(ctx context.Context) {
|
|||||||
|
|
||||||
func convertAgentStats(counts map[netlogtype.Connection]netlogtype.Counts) *agentsdk.Stats {
|
func convertAgentStats(counts map[netlogtype.Connection]netlogtype.Counts) *agentsdk.Stats {
|
||||||
stats := &agentsdk.Stats{
|
stats := &agentsdk.Stats{
|
||||||
ConnsByProto: map[string]int64{},
|
ConnectionsByProto: map[string]int64{},
|
||||||
NumConns: int64(len(counts)),
|
ConnectionCount: int64(len(counts)),
|
||||||
}
|
}
|
||||||
|
|
||||||
for conn, count := range counts {
|
for conn, count := range counts {
|
||||||
stats.ConnsByProto[conn.Proto.String()]++
|
stats.ConnectionsByProto[conn.Proto.String()]++
|
||||||
stats.RxPackets += int64(count.RxPackets)
|
stats.RxPackets += int64(count.RxPackets)
|
||||||
stats.RxBytes += int64(count.RxBytes)
|
stats.RxBytes += int64(count.RxBytes)
|
||||||
stats.TxPackets += int64(count.TxPackets)
|
stats.TxPackets += int64(count.TxPackets)
|
||||||
|
|||||||
+2
-2
@@ -73,7 +73,7 @@ func TestAgent_Stats_SSH(t *testing.T) {
|
|||||||
require.Eventuallyf(t, func() bool {
|
require.Eventuallyf(t, func() bool {
|
||||||
var ok bool
|
var ok bool
|
||||||
s, ok = <-stats
|
s, ok = <-stats
|
||||||
return ok && s.NumConns > 0 && s.RxBytes > 0 && s.TxBytes > 0
|
return ok && s.ConnectionCount > 0 && s.RxBytes > 0 && s.TxBytes > 0
|
||||||
}, testutil.WaitLong, testutil.IntervalFast,
|
}, testutil.WaitLong, testutil.IntervalFast,
|
||||||
"never saw stats: %+v", s,
|
"never saw stats: %+v", s,
|
||||||
)
|
)
|
||||||
@@ -102,7 +102,7 @@ func TestAgent_Stats_ReconnectingPTY(t *testing.T) {
|
|||||||
require.Eventuallyf(t, func() bool {
|
require.Eventuallyf(t, func() bool {
|
||||||
var ok bool
|
var ok bool
|
||||||
s, ok = <-stats
|
s, ok = <-stats
|
||||||
return ok && s.NumConns > 0 && s.RxBytes > 0 && s.TxBytes > 0
|
return ok && s.ConnectionCount > 0 && s.RxBytes > 0 && s.TxBytes > 0
|
||||||
}, testutil.WaitLong, testutil.IntervalFast,
|
}, testutil.WaitLong, testutil.IntervalFast,
|
||||||
"never saw stats: %+v", s,
|
"never saw stats: %+v", s,
|
||||||
)
|
)
|
||||||
|
|||||||
Generated
+2
-2
@@ -5202,14 +5202,14 @@ const docTemplate = `{
|
|||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"conns_by_proto": {
|
"conns_by_proto": {
|
||||||
"description": "ConnsByProto is a count of connections by protocol.",
|
"description": "ConnectionsByProto is a count of connections by protocol.",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": {
|
"additionalProperties": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"num_comms": {
|
"num_comms": {
|
||||||
"description": "NumConns is the number of connections received by an agent.",
|
"description": "ConnectionCount is the number of connections received by an agent.",
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
"rx_bytes": {
|
"rx_bytes": {
|
||||||
|
|||||||
Generated
+2
-2
@@ -4595,14 +4595,14 @@
|
|||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"conns_by_proto": {
|
"conns_by_proto": {
|
||||||
"description": "ConnsByProto is a count of connections by protocol.",
|
"description": "ConnectionsByProto is a count of connections by protocol.",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": {
|
"additionalProperties": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"num_comms": {
|
"num_comms": {
|
||||||
"description": "NumConns is the number of connections received by an agent.",
|
"description": "ConnectionCount is the number of connections received by an agent.",
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
"rx_bytes": {
|
"rx_bytes": {
|
||||||
|
|||||||
@@ -1413,18 +1413,18 @@ func (q *querier) UpdateWorkspaceAgentConnectionByID(ctx context.Context, arg da
|
|||||||
return update(q.log, q.auth, fetch, q.db.UpdateWorkspaceAgentConnectionByID)(ctx, arg)
|
return update(q.log, q.auth, fetch, q.db.UpdateWorkspaceAgentConnectionByID)(ctx, arg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *querier) InsertAgentStat(ctx context.Context, arg database.InsertAgentStatParams) (database.AgentStat, error) {
|
func (q *querier) InsertWorkspaceAgentStat(ctx context.Context, arg database.InsertWorkspaceAgentStatParams) (database.WorkspaceAgentStat, error) {
|
||||||
// TODO: This is a workspace agent operation. Should users be able to query this?
|
// TODO: This is a workspace agent operation. Should users be able to query this?
|
||||||
// Not really sure what this is for.
|
// Not really sure what this is for.
|
||||||
workspace, err := q.db.GetWorkspaceByID(ctx, arg.WorkspaceID)
|
workspace, err := q.db.GetWorkspaceByID(ctx, arg.WorkspaceID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return database.AgentStat{}, err
|
return database.WorkspaceAgentStat{}, err
|
||||||
}
|
}
|
||||||
err = q.authorizeContext(ctx, rbac.ActionUpdate, workspace)
|
err = q.authorizeContext(ctx, rbac.ActionUpdate, workspace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return database.AgentStat{}, err
|
return database.WorkspaceAgentStat{}, err
|
||||||
}
|
}
|
||||||
return q.db.InsertAgentStat(ctx, arg)
|
return q.db.InsertWorkspaceAgentStat(ctx, arg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *querier) UpdateWorkspaceAppHealthByID(ctx context.Context, arg database.UpdateWorkspaceAppHealthByIDParams) error {
|
func (q *querier) UpdateWorkspaceAppHealthByID(ctx context.Context, arg database.UpdateWorkspaceAppHealthByIDParams) error {
|
||||||
|
|||||||
@@ -1170,9 +1170,9 @@ func (s *MethodTestSuite) TestWorkspace() {
|
|||||||
ID: agt.ID,
|
ID: agt.ID,
|
||||||
}).Asserts(ws, rbac.ActionUpdate).Returns()
|
}).Asserts(ws, rbac.ActionUpdate).Returns()
|
||||||
}))
|
}))
|
||||||
s.Run("InsertAgentStat", s.Subtest(func(db database.Store, check *expects) {
|
s.Run("InsertWorkspaceAgentStat", s.Subtest(func(db database.Store, check *expects) {
|
||||||
ws := dbgen.Workspace(s.T(), db, database.Workspace{})
|
ws := dbgen.Workspace(s.T(), db, database.Workspace{})
|
||||||
check.Args(database.InsertAgentStatParams{
|
check.Args(database.InsertWorkspaceAgentStatParams{
|
||||||
WorkspaceID: ws.ID,
|
WorkspaceID: ws.ID,
|
||||||
}).Asserts(ws, rbac.ActionUpdate)
|
}).Asserts(ws, rbac.ActionUpdate)
|
||||||
}))
|
}))
|
||||||
|
|||||||
@@ -197,8 +197,8 @@ func (q *querier) GetWorkspaceResourceMetadataCreatedAfter(ctx context.Context,
|
|||||||
return q.db.GetWorkspaceResourceMetadataCreatedAfter(ctx, createdAt)
|
return q.db.GetWorkspaceResourceMetadataCreatedAfter(ctx, createdAt)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *querier) DeleteOldAgentStats(ctx context.Context) error {
|
func (q *querier) DeleteOldWorkspaceAgentStats(ctx context.Context) error {
|
||||||
return q.db.DeleteOldAgentStats(ctx)
|
return q.db.DeleteOldWorkspaceAgentStats(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *querier) GetParameterSchemasCreatedAfter(ctx context.Context, createdAt time.Time) ([]database.ParameterSchema, error) {
|
func (q *querier) GetParameterSchemasCreatedAfter(ctx context.Context, createdAt time.Time) ([]database.ParameterSchema, error) {
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ func (s *MethodTestSuite) TestSystemFunctions() {
|
|||||||
_ = dbgen.WorkspaceResourceMetadatums(s.T(), db, database.WorkspaceResourceMetadatum{})
|
_ = dbgen.WorkspaceResourceMetadatums(s.T(), db, database.WorkspaceResourceMetadatum{})
|
||||||
check.Args(time.Now()).Asserts()
|
check.Args(time.Now()).Asserts()
|
||||||
}))
|
}))
|
||||||
s.Run("DeleteOldAgentStats", s.Subtest(func(db database.Store, check *expects) {
|
s.Run("DeleteOldWorkspaceAgentStats", s.Subtest(func(db database.Store, check *expects) {
|
||||||
check.Args().Asserts()
|
check.Args().Asserts()
|
||||||
}))
|
}))
|
||||||
s.Run("GetParameterSchemasCreatedAfter", s.Subtest(func(db database.Store, check *expects) {
|
s.Run("GetParameterSchemasCreatedAfter", s.Subtest(func(db database.Store, check *expects) {
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ func New() database.Store {
|
|||||||
mutex: &sync.RWMutex{},
|
mutex: &sync.RWMutex{},
|
||||||
data: &data{
|
data: &data{
|
||||||
apiKeys: make([]database.APIKey, 0),
|
apiKeys: make([]database.APIKey, 0),
|
||||||
agentStats: make([]database.AgentStat, 0),
|
|
||||||
organizationMembers: make([]database.OrganizationMember, 0),
|
organizationMembers: make([]database.OrganizationMember, 0),
|
||||||
organizations: make([]database.Organization, 0),
|
organizations: make([]database.Organization, 0),
|
||||||
users: make([]database.User, 0),
|
users: make([]database.User, 0),
|
||||||
@@ -60,6 +59,7 @@ func New() database.Store {
|
|||||||
provisionerJobs: make([]database.ProvisionerJob, 0),
|
provisionerJobs: make([]database.ProvisionerJob, 0),
|
||||||
templateVersions: make([]database.TemplateVersion, 0),
|
templateVersions: make([]database.TemplateVersion, 0),
|
||||||
templates: make([]database.Template, 0),
|
templates: make([]database.Template, 0),
|
||||||
|
workspaceAgentStats: make([]database.WorkspaceAgentStat, 0),
|
||||||
workspaceBuilds: make([]database.WorkspaceBuild, 0),
|
workspaceBuilds: make([]database.WorkspaceBuild, 0),
|
||||||
workspaceApps: make([]database.WorkspaceApp, 0),
|
workspaceApps: make([]database.WorkspaceApp, 0),
|
||||||
workspaces: make([]database.Workspace, 0),
|
workspaces: make([]database.Workspace, 0),
|
||||||
@@ -98,7 +98,7 @@ type data struct {
|
|||||||
userLinks []database.UserLink
|
userLinks []database.UserLink
|
||||||
|
|
||||||
// New tables
|
// New tables
|
||||||
agentStats []database.AgentStat
|
workspaceAgentStats []database.WorkspaceAgentStat
|
||||||
auditLogs []database.AuditLog
|
auditLogs []database.AuditLog
|
||||||
files []database.File
|
files []database.File
|
||||||
gitAuthLinks []database.GitAuthLink
|
gitAuthLinks []database.GitAuthLink
|
||||||
@@ -258,29 +258,34 @@ func (q *fakeQuerier) AcquireProvisionerJob(_ context.Context, arg database.Acqu
|
|||||||
return database.ProvisionerJob{}, sql.ErrNoRows
|
return database.ProvisionerJob{}, sql.ErrNoRows
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*fakeQuerier) DeleteOldAgentStats(_ context.Context) error {
|
func (*fakeQuerier) DeleteOldWorkspaceAgentStats(_ context.Context) error {
|
||||||
// no-op
|
// no-op
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *fakeQuerier) InsertAgentStat(_ context.Context, p database.InsertAgentStatParams) (database.AgentStat, error) {
|
func (q *fakeQuerier) InsertWorkspaceAgentStat(_ context.Context, p database.InsertWorkspaceAgentStatParams) (database.WorkspaceAgentStat, error) {
|
||||||
if err := validateDatabaseType(p); err != nil {
|
if err := validateDatabaseType(p); err != nil {
|
||||||
return database.AgentStat{}, err
|
return database.WorkspaceAgentStat{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
q.mutex.Lock()
|
q.mutex.Lock()
|
||||||
defer q.mutex.Unlock()
|
defer q.mutex.Unlock()
|
||||||
|
|
||||||
stat := database.AgentStat{
|
stat := database.WorkspaceAgentStat{
|
||||||
ID: p.ID,
|
ID: p.ID,
|
||||||
CreatedAt: p.CreatedAt,
|
CreatedAt: p.CreatedAt,
|
||||||
WorkspaceID: p.WorkspaceID,
|
WorkspaceID: p.WorkspaceID,
|
||||||
AgentID: p.AgentID,
|
AgentID: p.AgentID,
|
||||||
UserID: p.UserID,
|
UserID: p.UserID,
|
||||||
Payload: p.Payload,
|
ConnectionsByProto: p.ConnectionsByProto,
|
||||||
TemplateID: p.TemplateID,
|
ConnectionCount: p.ConnectionCount,
|
||||||
|
RxPackets: p.RxPackets,
|
||||||
|
RxBytes: p.RxBytes,
|
||||||
|
TxPackets: p.TxPackets,
|
||||||
|
TxBytes: p.TxBytes,
|
||||||
|
TemplateID: p.TemplateID,
|
||||||
}
|
}
|
||||||
q.agentStats = append(q.agentStats, stat)
|
q.workspaceAgentStats = append(q.workspaceAgentStats, stat)
|
||||||
return stat, nil
|
return stat, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -290,7 +295,7 @@ func (q *fakeQuerier) GetTemplateDAUs(_ context.Context, templateID uuid.UUID) (
|
|||||||
|
|
||||||
seens := make(map[time.Time]map[uuid.UUID]struct{})
|
seens := make(map[time.Time]map[uuid.UUID]struct{})
|
||||||
|
|
||||||
for _, as := range q.agentStats {
|
for _, as := range q.workspaceAgentStats {
|
||||||
if as.TemplateID != templateID {
|
if as.TemplateID != templateID {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -330,7 +335,7 @@ func (q *fakeQuerier) GetDeploymentDAUs(_ context.Context) ([]database.GetDeploy
|
|||||||
|
|
||||||
seens := make(map[time.Time]map[uuid.UUID]struct{})
|
seens := make(map[time.Time]map[uuid.UUID]struct{})
|
||||||
|
|
||||||
for _, as := range q.agentStats {
|
for _, as := range q.workspaceAgentStats {
|
||||||
date := as.CreatedAt.Truncate(time.Hour * 24)
|
date := as.CreatedAt.Truncate(time.Hour * 24)
|
||||||
|
|
||||||
dateEntry := seens[date]
|
dateEntry := seens[date]
|
||||||
|
|||||||
Generated
+18
-13
@@ -123,16 +123,6 @@ CREATE TYPE workspace_transition AS ENUM (
|
|||||||
'delete'
|
'delete'
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE agent_stats (
|
|
||||||
id uuid NOT NULL,
|
|
||||||
created_at timestamp with time zone NOT NULL,
|
|
||||||
user_id uuid NOT NULL,
|
|
||||||
agent_id uuid NOT NULL,
|
|
||||||
workspace_id uuid NOT NULL,
|
|
||||||
template_id uuid NOT NULL,
|
|
||||||
payload jsonb NOT NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE api_keys (
|
CREATE TABLE api_keys (
|
||||||
id text NOT NULL,
|
id text NOT NULL,
|
||||||
hashed_secret bytea NOT NULL,
|
hashed_secret bytea NOT NULL,
|
||||||
@@ -472,6 +462,21 @@ CREATE TABLE users (
|
|||||||
last_seen_at timestamp without time zone DEFAULT '0001-01-01 00:00:00'::timestamp without time zone NOT NULL
|
last_seen_at timestamp without time zone DEFAULT '0001-01-01 00:00:00'::timestamp without time zone NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE TABLE workspace_agent_stats (
|
||||||
|
id uuid NOT NULL,
|
||||||
|
created_at timestamp with time zone NOT NULL,
|
||||||
|
user_id uuid NOT NULL,
|
||||||
|
agent_id uuid NOT NULL,
|
||||||
|
workspace_id uuid NOT NULL,
|
||||||
|
template_id uuid NOT NULL,
|
||||||
|
connections_by_proto jsonb DEFAULT '{}'::jsonb NOT NULL,
|
||||||
|
connection_count integer DEFAULT 0 NOT NULL,
|
||||||
|
rx_packets integer DEFAULT 0 NOT NULL,
|
||||||
|
rx_bytes integer DEFAULT 0 NOT NULL,
|
||||||
|
tx_packets integer DEFAULT 0 NOT NULL,
|
||||||
|
tx_bytes integer DEFAULT 0 NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
CREATE TABLE workspace_agents (
|
CREATE TABLE workspace_agents (
|
||||||
id uuid NOT NULL,
|
id uuid NOT NULL,
|
||||||
created_at timestamp with time zone NOT NULL,
|
created_at timestamp with time zone NOT NULL,
|
||||||
@@ -611,7 +616,7 @@ ALTER TABLE ONLY provisioner_job_logs ALTER COLUMN id SET DEFAULT nextval('provi
|
|||||||
|
|
||||||
ALTER TABLE ONLY workspace_resource_metadata ALTER COLUMN id SET DEFAULT nextval('workspace_resource_metadata_id_seq'::regclass);
|
ALTER TABLE ONLY workspace_resource_metadata ALTER COLUMN id SET DEFAULT nextval('workspace_resource_metadata_id_seq'::regclass);
|
||||||
|
|
||||||
ALTER TABLE ONLY agent_stats
|
ALTER TABLE ONLY workspace_agent_stats
|
||||||
ADD CONSTRAINT agent_stats_pkey PRIMARY KEY (id);
|
ADD CONSTRAINT agent_stats_pkey PRIMARY KEY (id);
|
||||||
|
|
||||||
ALTER TABLE ONLY api_keys
|
ALTER TABLE ONLY api_keys
|
||||||
@@ -734,9 +739,9 @@ ALTER TABLE ONLY workspace_resources
|
|||||||
ALTER TABLE ONLY workspaces
|
ALTER TABLE ONLY workspaces
|
||||||
ADD CONSTRAINT workspaces_pkey PRIMARY KEY (id);
|
ADD CONSTRAINT workspaces_pkey PRIMARY KEY (id);
|
||||||
|
|
||||||
CREATE INDEX idx_agent_stats_created_at ON agent_stats USING btree (created_at);
|
CREATE INDEX idx_agent_stats_created_at ON workspace_agent_stats USING btree (created_at);
|
||||||
|
|
||||||
CREATE INDEX idx_agent_stats_user_id ON agent_stats USING btree (user_id);
|
CREATE INDEX idx_agent_stats_user_id ON workspace_agent_stats USING btree (user_id);
|
||||||
|
|
||||||
CREATE INDEX idx_api_keys_user ON api_keys USING btree (user_id);
|
CREATE INDEX idx_api_keys_user ON api_keys USING btree (user_id);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
ALTER TABLE workspace_agent_stats RENAME TO agent_stats;
|
||||||
|
|
||||||
|
ALTER TABLE agent_stats ADD COLUMN payload jsonb NOT NULL DEFAULT '{}'::jsonb;
|
||||||
|
ALTER TABLE agent_stats DROP COLUMN connections_by_proto,
|
||||||
|
DROP COLUMN connection_count,
|
||||||
|
DROP COLUMN rx_packets,
|
||||||
|
DROP COLUMN rx_bytes,
|
||||||
|
DROP COLUMN tx_packets,
|
||||||
|
DROP COLUMN tx_bytes;
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
ALTER TABLE agent_stats RENAME TO workspace_agent_stats;
|
||||||
|
|
||||||
|
ALTER TABLE workspace_agent_stats ADD COLUMN connections_by_proto jsonb NOT NULL DEFAULT '{}'::jsonb;
|
||||||
|
ALTER TABLE workspace_agent_stats ADD COLUMN connection_count integer DEFAULT 0 NOT NULL;
|
||||||
|
ALTER TABLE workspace_agent_stats ADD COLUMN rx_packets integer DEFAULT 0 NOT NULL;
|
||||||
|
ALTER TABLE workspace_agent_stats ADD COLUMN rx_bytes integer DEFAULT 0 NOT NULL;
|
||||||
|
ALTER TABLE workspace_agent_stats ADD COLUMN tx_packets integer DEFAULT 0 NOT NULL;
|
||||||
|
ALTER TABLE workspace_agent_stats ADD COLUMN tx_bytes integer DEFAULT 0 NOT NULL;
|
||||||
|
|
||||||
|
UPDATE workspace_agent_stats SET
|
||||||
|
connections_by_proto = coalesce((payload ->> 'conns_by_proto')::jsonb, '{}'::jsonb),
|
||||||
|
connection_count = coalesce((payload ->> 'num_conns')::integer, 0),
|
||||||
|
rx_packets = coalesce((payload ->> 'rx_packets')::integer, 0),
|
||||||
|
rx_bytes = coalesce((payload ->> 'rx_bytes')::integer, 0),
|
||||||
|
tx_packets = coalesce((payload ->> 'tx_packets')::integer, 0),
|
||||||
|
tx_bytes = coalesce((payload ->> 'tx_bytes')::integer, 0);
|
||||||
|
|
||||||
|
ALTER TABLE workspace_agent_stats DROP COLUMN payload;
|
||||||
+15
-10
@@ -1218,16 +1218,6 @@ type APIKey struct {
|
|||||||
Scope APIKeyScope `db:"scope" json:"scope"`
|
Scope APIKeyScope `db:"scope" json:"scope"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AgentStat struct {
|
|
||||||
ID uuid.UUID `db:"id" json:"id"`
|
|
||||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
|
||||||
UserID uuid.UUID `db:"user_id" json:"user_id"`
|
|
||||||
AgentID uuid.UUID `db:"agent_id" json:"agent_id"`
|
|
||||||
WorkspaceID uuid.UUID `db:"workspace_id" json:"workspace_id"`
|
|
||||||
TemplateID uuid.UUID `db:"template_id" json:"template_id"`
|
|
||||||
Payload json.RawMessage `db:"payload" json:"payload"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type AuditLog struct {
|
type AuditLog struct {
|
||||||
ID uuid.UUID `db:"id" json:"id"`
|
ID uuid.UUID `db:"id" json:"id"`
|
||||||
Time time.Time `db:"time" json:"time"`
|
Time time.Time `db:"time" json:"time"`
|
||||||
@@ -1558,6 +1548,21 @@ type WorkspaceAgent struct {
|
|||||||
ExpandedDirectory string `db:"expanded_directory" json:"expanded_directory"`
|
ExpandedDirectory string `db:"expanded_directory" json:"expanded_directory"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type WorkspaceAgentStat struct {
|
||||||
|
ID uuid.UUID `db:"id" json:"id"`
|
||||||
|
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||||
|
UserID uuid.UUID `db:"user_id" json:"user_id"`
|
||||||
|
AgentID uuid.UUID `db:"agent_id" json:"agent_id"`
|
||||||
|
WorkspaceID uuid.UUID `db:"workspace_id" json:"workspace_id"`
|
||||||
|
TemplateID uuid.UUID `db:"template_id" json:"template_id"`
|
||||||
|
ConnectionsByProto json.RawMessage `db:"connections_by_proto" json:"connections_by_proto"`
|
||||||
|
ConnectionCount int32 `db:"connection_count" json:"connection_count"`
|
||||||
|
RxPackets int32 `db:"rx_packets" json:"rx_packets"`
|
||||||
|
RxBytes int32 `db:"rx_bytes" json:"rx_bytes"`
|
||||||
|
TxPackets int32 `db:"tx_packets" json:"tx_packets"`
|
||||||
|
TxBytes int32 `db:"tx_bytes" json:"tx_bytes"`
|
||||||
|
}
|
||||||
|
|
||||||
type WorkspaceApp struct {
|
type WorkspaceApp struct {
|
||||||
ID uuid.UUID `db:"id" json:"id"`
|
ID uuid.UUID `db:"id" json:"id"`
|
||||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ type sqlcQuerier interface {
|
|||||||
DeleteGroupMemberFromGroup(ctx context.Context, arg DeleteGroupMemberFromGroupParams) error
|
DeleteGroupMemberFromGroup(ctx context.Context, arg DeleteGroupMemberFromGroupParams) error
|
||||||
DeleteGroupMembersByOrgAndUser(ctx context.Context, arg DeleteGroupMembersByOrgAndUserParams) error
|
DeleteGroupMembersByOrgAndUser(ctx context.Context, arg DeleteGroupMembersByOrgAndUserParams) error
|
||||||
DeleteLicense(ctx context.Context, id int32) (int32, error)
|
DeleteLicense(ctx context.Context, id int32) (int32, error)
|
||||||
DeleteOldAgentStats(ctx context.Context) error
|
DeleteOldWorkspaceAgentStats(ctx context.Context) error
|
||||||
DeleteParameterValueByID(ctx context.Context, id uuid.UUID) error
|
DeleteParameterValueByID(ctx context.Context, id uuid.UUID) error
|
||||||
DeleteReplicasUpdatedBefore(ctx context.Context, updatedAt time.Time) error
|
DeleteReplicasUpdatedBefore(ctx context.Context, updatedAt time.Time) error
|
||||||
GetAPIKeyByID(ctx context.Context, id string) (APIKey, error)
|
GetAPIKeyByID(ctx context.Context, id string) (APIKey, error)
|
||||||
@@ -133,7 +133,6 @@ type sqlcQuerier interface {
|
|||||||
GetWorkspaceResourcesCreatedAfter(ctx context.Context, createdAt time.Time) ([]WorkspaceResource, error)
|
GetWorkspaceResourcesCreatedAfter(ctx context.Context, createdAt time.Time) ([]WorkspaceResource, error)
|
||||||
GetWorkspaces(ctx context.Context, arg GetWorkspacesParams) ([]GetWorkspacesRow, error)
|
GetWorkspaces(ctx context.Context, arg GetWorkspacesParams) ([]GetWorkspacesRow, error)
|
||||||
InsertAPIKey(ctx context.Context, arg InsertAPIKeyParams) (APIKey, error)
|
InsertAPIKey(ctx context.Context, arg InsertAPIKeyParams) (APIKey, error)
|
||||||
InsertAgentStat(ctx context.Context, arg InsertAgentStatParams) (AgentStat, error)
|
|
||||||
// We use the organization_id as the id
|
// We use the organization_id as the id
|
||||||
// for simplicity since all users is
|
// for simplicity since all users is
|
||||||
// every member of the org.
|
// every member of the org.
|
||||||
@@ -168,6 +167,7 @@ type sqlcQuerier interface {
|
|||||||
InsertUserLink(ctx context.Context, arg InsertUserLinkParams) (UserLink, error)
|
InsertUserLink(ctx context.Context, arg InsertUserLinkParams) (UserLink, error)
|
||||||
InsertWorkspace(ctx context.Context, arg InsertWorkspaceParams) (Workspace, error)
|
InsertWorkspace(ctx context.Context, arg InsertWorkspaceParams) (Workspace, error)
|
||||||
InsertWorkspaceAgent(ctx context.Context, arg InsertWorkspaceAgentParams) (WorkspaceAgent, error)
|
InsertWorkspaceAgent(ctx context.Context, arg InsertWorkspaceAgentParams) (WorkspaceAgent, error)
|
||||||
|
InsertWorkspaceAgentStat(ctx context.Context, arg InsertWorkspaceAgentStatParams) (WorkspaceAgentStat, error)
|
||||||
InsertWorkspaceApp(ctx context.Context, arg InsertWorkspaceAppParams) (WorkspaceApp, error)
|
InsertWorkspaceApp(ctx context.Context, arg InsertWorkspaceAppParams) (WorkspaceApp, error)
|
||||||
InsertWorkspaceBuild(ctx context.Context, arg InsertWorkspaceBuildParams) (WorkspaceBuild, error)
|
InsertWorkspaceBuild(ctx context.Context, arg InsertWorkspaceBuildParams) (WorkspaceBuild, error)
|
||||||
InsertWorkspaceBuildParameters(ctx context.Context, arg InsertWorkspaceBuildParametersParams) error
|
InsertWorkspaceBuildParameters(ctx context.Context, arg InsertWorkspaceBuildParametersParams) error
|
||||||
|
|||||||
+159
-139
@@ -16,145 +16,6 @@ import (
|
|||||||
"github.com/tabbed/pqtype"
|
"github.com/tabbed/pqtype"
|
||||||
)
|
)
|
||||||
|
|
||||||
const deleteOldAgentStats = `-- name: DeleteOldAgentStats :exec
|
|
||||||
DELETE FROM agent_stats WHERE created_at < NOW() - INTERVAL '30 days'
|
|
||||||
`
|
|
||||||
|
|
||||||
func (q *sqlQuerier) DeleteOldAgentStats(ctx context.Context) error {
|
|
||||||
_, err := q.db.ExecContext(ctx, deleteOldAgentStats)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
const getDeploymentDAUs = `-- name: GetDeploymentDAUs :many
|
|
||||||
SELECT
|
|
||||||
(created_at at TIME ZONE 'UTC')::date as date,
|
|
||||||
user_id
|
|
||||||
FROM
|
|
||||||
agent_stats
|
|
||||||
GROUP BY
|
|
||||||
date, user_id
|
|
||||||
ORDER BY
|
|
||||||
date ASC
|
|
||||||
`
|
|
||||||
|
|
||||||
type GetDeploymentDAUsRow struct {
|
|
||||||
Date time.Time `db:"date" json:"date"`
|
|
||||||
UserID uuid.UUID `db:"user_id" json:"user_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *sqlQuerier) GetDeploymentDAUs(ctx context.Context) ([]GetDeploymentDAUsRow, error) {
|
|
||||||
rows, err := q.db.QueryContext(ctx, getDeploymentDAUs)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
var items []GetDeploymentDAUsRow
|
|
||||||
for rows.Next() {
|
|
||||||
var i GetDeploymentDAUsRow
|
|
||||||
if err := rows.Scan(&i.Date, &i.UserID); 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 getTemplateDAUs = `-- name: GetTemplateDAUs :many
|
|
||||||
SELECT
|
|
||||||
(created_at at TIME ZONE 'UTC')::date as date,
|
|
||||||
user_id
|
|
||||||
FROM
|
|
||||||
agent_stats
|
|
||||||
WHERE
|
|
||||||
template_id = $1
|
|
||||||
GROUP BY
|
|
||||||
date, user_id
|
|
||||||
ORDER BY
|
|
||||||
date ASC
|
|
||||||
`
|
|
||||||
|
|
||||||
type GetTemplateDAUsRow struct {
|
|
||||||
Date time.Time `db:"date" json:"date"`
|
|
||||||
UserID uuid.UUID `db:"user_id" json:"user_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *sqlQuerier) GetTemplateDAUs(ctx context.Context, templateID uuid.UUID) ([]GetTemplateDAUsRow, error) {
|
|
||||||
rows, err := q.db.QueryContext(ctx, getTemplateDAUs, templateID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
var items []GetTemplateDAUsRow
|
|
||||||
for rows.Next() {
|
|
||||||
var i GetTemplateDAUsRow
|
|
||||||
if err := rows.Scan(&i.Date, &i.UserID); 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 insertAgentStat = `-- name: InsertAgentStat :one
|
|
||||||
INSERT INTO
|
|
||||||
agent_stats (
|
|
||||||
id,
|
|
||||||
created_at,
|
|
||||||
user_id,
|
|
||||||
workspace_id,
|
|
||||||
template_id,
|
|
||||||
agent_id,
|
|
||||||
payload
|
|
||||||
)
|
|
||||||
VALUES
|
|
||||||
($1, $2, $3, $4, $5, $6, $7) RETURNING id, created_at, user_id, agent_id, workspace_id, template_id, payload
|
|
||||||
`
|
|
||||||
|
|
||||||
type InsertAgentStatParams struct {
|
|
||||||
ID uuid.UUID `db:"id" json:"id"`
|
|
||||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
|
||||||
UserID uuid.UUID `db:"user_id" json:"user_id"`
|
|
||||||
WorkspaceID uuid.UUID `db:"workspace_id" json:"workspace_id"`
|
|
||||||
TemplateID uuid.UUID `db:"template_id" json:"template_id"`
|
|
||||||
AgentID uuid.UUID `db:"agent_id" json:"agent_id"`
|
|
||||||
Payload json.RawMessage `db:"payload" json:"payload"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *sqlQuerier) InsertAgentStat(ctx context.Context, arg InsertAgentStatParams) (AgentStat, error) {
|
|
||||||
row := q.db.QueryRowContext(ctx, insertAgentStat,
|
|
||||||
arg.ID,
|
|
||||||
arg.CreatedAt,
|
|
||||||
arg.UserID,
|
|
||||||
arg.WorkspaceID,
|
|
||||||
arg.TemplateID,
|
|
||||||
arg.AgentID,
|
|
||||||
arg.Payload,
|
|
||||||
)
|
|
||||||
var i AgentStat
|
|
||||||
err := row.Scan(
|
|
||||||
&i.ID,
|
|
||||||
&i.CreatedAt,
|
|
||||||
&i.UserID,
|
|
||||||
&i.AgentID,
|
|
||||||
&i.WorkspaceID,
|
|
||||||
&i.TemplateID,
|
|
||||||
&i.Payload,
|
|
||||||
)
|
|
||||||
return i, err
|
|
||||||
}
|
|
||||||
|
|
||||||
const deleteAPIKeyByID = `-- name: DeleteAPIKeyByID :exec
|
const deleteAPIKeyByID = `-- name: DeleteAPIKeyByID :exec
|
||||||
DELETE
|
DELETE
|
||||||
FROM
|
FROM
|
||||||
@@ -5450,6 +5311,165 @@ func (q *sqlQuerier) UpdateWorkspaceAgentStartupByID(ctx context.Context, arg Up
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const deleteOldWorkspaceAgentStats = `-- name: DeleteOldWorkspaceAgentStats :exec
|
||||||
|
DELETE FROM workspace_agent_stats WHERE created_at < NOW() - INTERVAL '30 days'
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *sqlQuerier) DeleteOldWorkspaceAgentStats(ctx context.Context) error {
|
||||||
|
_, err := q.db.ExecContext(ctx, deleteOldWorkspaceAgentStats)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const getDeploymentDAUs = `-- name: GetDeploymentDAUs :many
|
||||||
|
SELECT
|
||||||
|
(created_at at TIME ZONE 'UTC')::date as date,
|
||||||
|
user_id
|
||||||
|
FROM
|
||||||
|
workspace_agent_stats
|
||||||
|
GROUP BY
|
||||||
|
date, user_id
|
||||||
|
ORDER BY
|
||||||
|
date ASC
|
||||||
|
`
|
||||||
|
|
||||||
|
type GetDeploymentDAUsRow struct {
|
||||||
|
Date time.Time `db:"date" json:"date"`
|
||||||
|
UserID uuid.UUID `db:"user_id" json:"user_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *sqlQuerier) GetDeploymentDAUs(ctx context.Context) ([]GetDeploymentDAUsRow, error) {
|
||||||
|
rows, err := q.db.QueryContext(ctx, getDeploymentDAUs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []GetDeploymentDAUsRow
|
||||||
|
for rows.Next() {
|
||||||
|
var i GetDeploymentDAUsRow
|
||||||
|
if err := rows.Scan(&i.Date, &i.UserID); 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 getTemplateDAUs = `-- name: GetTemplateDAUs :many
|
||||||
|
SELECT
|
||||||
|
(created_at at TIME ZONE 'UTC')::date as date,
|
||||||
|
user_id
|
||||||
|
FROM
|
||||||
|
workspace_agent_stats
|
||||||
|
WHERE
|
||||||
|
template_id = $1
|
||||||
|
GROUP BY
|
||||||
|
date, user_id
|
||||||
|
ORDER BY
|
||||||
|
date ASC
|
||||||
|
`
|
||||||
|
|
||||||
|
type GetTemplateDAUsRow struct {
|
||||||
|
Date time.Time `db:"date" json:"date"`
|
||||||
|
UserID uuid.UUID `db:"user_id" json:"user_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *sqlQuerier) GetTemplateDAUs(ctx context.Context, templateID uuid.UUID) ([]GetTemplateDAUsRow, error) {
|
||||||
|
rows, err := q.db.QueryContext(ctx, getTemplateDAUs, templateID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []GetTemplateDAUsRow
|
||||||
|
for rows.Next() {
|
||||||
|
var i GetTemplateDAUsRow
|
||||||
|
if err := rows.Scan(&i.Date, &i.UserID); 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 insertWorkspaceAgentStat = `-- name: InsertWorkspaceAgentStat :one
|
||||||
|
INSERT INTO
|
||||||
|
workspace_agent_stats (
|
||||||
|
id,
|
||||||
|
created_at,
|
||||||
|
user_id,
|
||||||
|
workspace_id,
|
||||||
|
template_id,
|
||||||
|
agent_id,
|
||||||
|
connections_by_proto,
|
||||||
|
connection_count,
|
||||||
|
rx_packets,
|
||||||
|
rx_bytes,
|
||||||
|
tx_packets,
|
||||||
|
tx_bytes
|
||||||
|
)
|
||||||
|
VALUES
|
||||||
|
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12) RETURNING id, created_at, user_id, agent_id, workspace_id, template_id, connections_by_proto, connection_count, rx_packets, rx_bytes, tx_packets, tx_bytes
|
||||||
|
`
|
||||||
|
|
||||||
|
type InsertWorkspaceAgentStatParams struct {
|
||||||
|
ID uuid.UUID `db:"id" json:"id"`
|
||||||
|
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||||
|
UserID uuid.UUID `db:"user_id" json:"user_id"`
|
||||||
|
WorkspaceID uuid.UUID `db:"workspace_id" json:"workspace_id"`
|
||||||
|
TemplateID uuid.UUID `db:"template_id" json:"template_id"`
|
||||||
|
AgentID uuid.UUID `db:"agent_id" json:"agent_id"`
|
||||||
|
ConnectionsByProto json.RawMessage `db:"connections_by_proto" json:"connections_by_proto"`
|
||||||
|
ConnectionCount int32 `db:"connection_count" json:"connection_count"`
|
||||||
|
RxPackets int32 `db:"rx_packets" json:"rx_packets"`
|
||||||
|
RxBytes int32 `db:"rx_bytes" json:"rx_bytes"`
|
||||||
|
TxPackets int32 `db:"tx_packets" json:"tx_packets"`
|
||||||
|
TxBytes int32 `db:"tx_bytes" json:"tx_bytes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *sqlQuerier) InsertWorkspaceAgentStat(ctx context.Context, arg InsertWorkspaceAgentStatParams) (WorkspaceAgentStat, error) {
|
||||||
|
row := q.db.QueryRowContext(ctx, insertWorkspaceAgentStat,
|
||||||
|
arg.ID,
|
||||||
|
arg.CreatedAt,
|
||||||
|
arg.UserID,
|
||||||
|
arg.WorkspaceID,
|
||||||
|
arg.TemplateID,
|
||||||
|
arg.AgentID,
|
||||||
|
arg.ConnectionsByProto,
|
||||||
|
arg.ConnectionCount,
|
||||||
|
arg.RxPackets,
|
||||||
|
arg.RxBytes,
|
||||||
|
arg.TxPackets,
|
||||||
|
arg.TxBytes,
|
||||||
|
)
|
||||||
|
var i WorkspaceAgentStat
|
||||||
|
err := row.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.CreatedAt,
|
||||||
|
&i.UserID,
|
||||||
|
&i.AgentID,
|
||||||
|
&i.WorkspaceID,
|
||||||
|
&i.TemplateID,
|
||||||
|
&i.ConnectionsByProto,
|
||||||
|
&i.ConnectionCount,
|
||||||
|
&i.RxPackets,
|
||||||
|
&i.RxBytes,
|
||||||
|
&i.TxPackets,
|
||||||
|
&i.TxBytes,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
const getWorkspaceAppByAgentIDAndSlug = `-- name: GetWorkspaceAppByAgentIDAndSlug :one
|
const getWorkspaceAppByAgentIDAndSlug = `-- name: GetWorkspaceAppByAgentIDAndSlug :one
|
||||||
SELECT id, created_at, agent_id, display_name, icon, command, url, healthcheck_url, healthcheck_interval, healthcheck_threshold, health, subdomain, sharing_level, slug, external FROM workspace_apps WHERE agent_id = $1 AND slug = $2
|
SELECT id, created_at, agent_id, display_name, icon, command, url, healthcheck_url, healthcheck_interval, healthcheck_threshold, health, subdomain, sharing_level, slug, external FROM workspace_apps WHERE agent_id = $1 AND slug = $2
|
||||||
`
|
`
|
||||||
|
|||||||
+13
-8
@@ -1,23 +1,28 @@
|
|||||||
-- name: InsertAgentStat :one
|
-- name: InsertWorkspaceAgentStat :one
|
||||||
INSERT INTO
|
INSERT INTO
|
||||||
agent_stats (
|
workspace_agent_stats (
|
||||||
id,
|
id,
|
||||||
created_at,
|
created_at,
|
||||||
user_id,
|
user_id,
|
||||||
workspace_id,
|
workspace_id,
|
||||||
template_id,
|
template_id,
|
||||||
agent_id,
|
agent_id,
|
||||||
payload
|
connections_by_proto,
|
||||||
|
connection_count,
|
||||||
|
rx_packets,
|
||||||
|
rx_bytes,
|
||||||
|
tx_packets,
|
||||||
|
tx_bytes
|
||||||
)
|
)
|
||||||
VALUES
|
VALUES
|
||||||
($1, $2, $3, $4, $5, $6, $7) RETURNING *;
|
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12) RETURNING *;
|
||||||
|
|
||||||
-- name: GetTemplateDAUs :many
|
-- name: GetTemplateDAUs :many
|
||||||
SELECT
|
SELECT
|
||||||
(created_at at TIME ZONE 'UTC')::date as date,
|
(created_at at TIME ZONE 'UTC')::date as date,
|
||||||
user_id
|
user_id
|
||||||
FROM
|
FROM
|
||||||
agent_stats
|
workspace_agent_stats
|
||||||
WHERE
|
WHERE
|
||||||
template_id = $1
|
template_id = $1
|
||||||
GROUP BY
|
GROUP BY
|
||||||
@@ -30,11 +35,11 @@ SELECT
|
|||||||
(created_at at TIME ZONE 'UTC')::date as date,
|
(created_at at TIME ZONE 'UTC')::date as date,
|
||||||
user_id
|
user_id
|
||||||
FROM
|
FROM
|
||||||
agent_stats
|
workspace_agent_stats
|
||||||
GROUP BY
|
GROUP BY
|
||||||
date, user_id
|
date, user_id
|
||||||
ORDER BY
|
ORDER BY
|
||||||
date ASC;
|
date ASC;
|
||||||
|
|
||||||
-- name: DeleteOldAgentStats :exec
|
-- name: DeleteOldWorkspaceAgentStats :exec
|
||||||
DELETE FROM agent_stats WHERE created_at < NOW() - INTERVAL '30 days';
|
DELETE FROM workspace_agent_stats WHERE created_at < NOW() - INTERVAL '30 days';
|
||||||
@@ -145,7 +145,7 @@ func countUniqueUsers(rows []database.GetTemplateDAUsRow) int {
|
|||||||
func (c *Cache) refresh(ctx context.Context) error {
|
func (c *Cache) refresh(ctx context.Context) error {
|
||||||
//nolint:gocritic // This is a system service.
|
//nolint:gocritic // This is a system service.
|
||||||
ctx = dbauthz.AsSystemRestricted(ctx)
|
ctx = dbauthz.AsSystemRestricted(ctx)
|
||||||
err := c.database.DeleteOldAgentStats(ctx)
|
err := c.database.DeleteOldWorkspaceAgentStats(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Errorf("delete old stats: %w", err)
|
return xerrors.Errorf("delete old stats: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ func TestCache_TemplateUsers(t *testing.T) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
type args struct {
|
type args struct {
|
||||||
rows []database.InsertAgentStatParams
|
rows []database.InsertWorkspaceAgentStatParams
|
||||||
}
|
}
|
||||||
type want struct {
|
type want struct {
|
||||||
entries []codersdk.DAUEntry
|
entries []codersdk.DAUEntry
|
||||||
@@ -45,7 +45,7 @@ func TestCache_TemplateUsers(t *testing.T) {
|
|||||||
{"empty", args{}, want{nil, 0}},
|
{"empty", args{}, want{nil, 0}},
|
||||||
{
|
{
|
||||||
"one hole", args{
|
"one hole", args{
|
||||||
rows: []database.InsertAgentStatParams{
|
rows: []database.InsertWorkspaceAgentStatParams{
|
||||||
{
|
{
|
||||||
CreatedAt: date(2022, 8, 27),
|
CreatedAt: date(2022, 8, 27),
|
||||||
UserID: zebra,
|
UserID: zebra,
|
||||||
@@ -75,7 +75,7 @@ func TestCache_TemplateUsers(t *testing.T) {
|
|||||||
}, 1},
|
}, 1},
|
||||||
},
|
},
|
||||||
{"no holes", args{
|
{"no holes", args{
|
||||||
rows: []database.InsertAgentStatParams{
|
rows: []database.InsertWorkspaceAgentStatParams{
|
||||||
{
|
{
|
||||||
CreatedAt: date(2022, 8, 27),
|
CreatedAt: date(2022, 8, 27),
|
||||||
UserID: zebra,
|
UserID: zebra,
|
||||||
@@ -104,7 +104,7 @@ func TestCache_TemplateUsers(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}, 1}},
|
}, 1}},
|
||||||
{"holes", args{
|
{"holes", args{
|
||||||
rows: []database.InsertAgentStatParams{
|
rows: []database.InsertWorkspaceAgentStatParams{
|
||||||
{
|
{
|
||||||
CreatedAt: date(2022, 1, 1),
|
CreatedAt: date(2022, 1, 1),
|
||||||
UserID: zebra,
|
UserID: zebra,
|
||||||
@@ -179,7 +179,7 @@ func TestCache_TemplateUsers(t *testing.T) {
|
|||||||
|
|
||||||
for _, row := range tt.args.rows {
|
for _, row := range tt.args.rows {
|
||||||
row.TemplateID = template.ID
|
row.TemplateID = template.ID
|
||||||
db.InsertAgentStat(context.Background(), row)
|
db.InsertWorkspaceAgentStat(context.Background(), row)
|
||||||
}
|
}
|
||||||
|
|
||||||
require.Eventuallyf(t, func() bool {
|
require.Eventuallyf(t, func() bool {
|
||||||
|
|||||||
+15
-10
@@ -933,21 +933,26 @@ func (api *API) workspaceAgentReportStats(rw http.ResponseWriter, r *http.Reques
|
|||||||
|
|
||||||
activityBumpWorkspace(ctx, api.Logger.Named("activity_bump"), api.Database, workspace.ID)
|
activityBumpWorkspace(ctx, api.Logger.Named("activity_bump"), api.Database, workspace.ID)
|
||||||
|
|
||||||
payload, err := json.Marshal(req)
|
payload, err := json.Marshal(req.ConnectionsByProto)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.Logger.Error(ctx, "marshal agent stats report", slog.Error(err))
|
api.Logger.Error(ctx, "marshal agent connections by proto", slog.F("workspace_agent", workspaceAgent.ID), slog.Error(err))
|
||||||
payload = json.RawMessage("{}")
|
payload = json.RawMessage("{}")
|
||||||
}
|
}
|
||||||
|
|
||||||
now := database.Now()
|
now := database.Now()
|
||||||
_, err = api.Database.InsertAgentStat(ctx, database.InsertAgentStatParams{
|
_, err = api.Database.InsertWorkspaceAgentStat(ctx, database.InsertWorkspaceAgentStatParams{
|
||||||
ID: uuid.New(),
|
ID: uuid.New(),
|
||||||
CreatedAt: now,
|
CreatedAt: now,
|
||||||
AgentID: workspaceAgent.ID,
|
AgentID: workspaceAgent.ID,
|
||||||
WorkspaceID: workspace.ID,
|
WorkspaceID: workspace.ID,
|
||||||
UserID: workspace.OwnerID,
|
UserID: workspace.OwnerID,
|
||||||
TemplateID: workspace.TemplateID,
|
TemplateID: workspace.TemplateID,
|
||||||
Payload: payload,
|
ConnectionsByProto: payload,
|
||||||
|
ConnectionCount: int32(req.ConnectionCount),
|
||||||
|
RxPackets: int32(req.RxPackets),
|
||||||
|
RxBytes: int32(req.RxBytes),
|
||||||
|
TxPackets: int32(req.TxPackets),
|
||||||
|
TxBytes: int32(req.TxBytes),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpapi.InternalServerError(rw, err)
|
httpapi.InternalServerError(rw, err)
|
||||||
|
|||||||
@@ -1178,12 +1178,12 @@ func TestWorkspaceAgentReportStats(t *testing.T) {
|
|||||||
agentClient.SetSessionToken(authToken)
|
agentClient.SetSessionToken(authToken)
|
||||||
|
|
||||||
_, err := agentClient.PostStats(context.Background(), &agentsdk.Stats{
|
_, err := agentClient.PostStats(context.Background(), &agentsdk.Stats{
|
||||||
ConnsByProto: map[string]int64{"TCP": 1},
|
ConnectionsByProto: map[string]int64{"TCP": 1},
|
||||||
NumConns: 1,
|
ConnectionCount: 1,
|
||||||
RxPackets: 1,
|
RxPackets: 1,
|
||||||
RxBytes: 1,
|
RxBytes: 1,
|
||||||
TxPackets: 1,
|
TxPackets: 1,
|
||||||
TxBytes: 1,
|
TxBytes: 1,
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
|||||||
@@ -397,7 +397,7 @@ func (c *Client) ReportStats(ctx context.Context, log slog.Logger, statsChan <-c
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Send an empty stat to get the interval.
|
// Send an empty stat to get the interval.
|
||||||
postStat(&Stats{ConnsByProto: map[string]int64{}})
|
postStat(&Stats{ConnectionsByProto: map[string]int64{}})
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
defer close(exited)
|
defer close(exited)
|
||||||
@@ -426,10 +426,10 @@ func (c *Client) ReportStats(ctx context.Context, log slog.Logger, statsChan <-c
|
|||||||
// Stats records the Agent's network connection statistics for use in
|
// Stats records the Agent's network connection statistics for use in
|
||||||
// user-facing metrics and debugging.
|
// user-facing metrics and debugging.
|
||||||
type Stats struct {
|
type Stats struct {
|
||||||
// ConnsByProto is a count of connections by protocol.
|
// ConnectionsByProto is a count of connections by protocol.
|
||||||
ConnsByProto map[string]int64 `json:"conns_by_proto"`
|
ConnectionsByProto map[string]int64 `json:"conns_by_proto"`
|
||||||
// NumConns is the number of connections received by an agent.
|
// ConnectionCount is the number of connections received by an agent.
|
||||||
NumConns int64 `json:"num_comms"`
|
ConnectionCount int64 `json:"num_comms"`
|
||||||
// RxPackets is the number of received packets.
|
// RxPackets is the number of received packets.
|
||||||
RxPackets int64 `json:"rx_packets"`
|
RxPackets int64 `json:"rx_packets"`
|
||||||
// RxBytes is the number of received bytes.
|
// RxBytes is the number of received bytes.
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ func TestAgentReportStats(t *testing.T) {
|
|||||||
chanLen := 3
|
chanLen := 3
|
||||||
statCh := make(chan *agentsdk.Stats, chanLen)
|
statCh := make(chan *agentsdk.Stats, chanLen)
|
||||||
for i := 0; i < chanLen; i++ {
|
for i := 0; i < chanLen; i++ {
|
||||||
statCh <- &agentsdk.Stats{ConnsByProto: map[string]int64{}}
|
statCh <- &agentsdk.Stats{ConnectionsByProto: map[string]int64{}}
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|||||||
Reference in New Issue
Block a user