From eb527feed3d3e95ab68dada0a4a74daa48bcaf00 Mon Sep 17 00:00:00 2001 From: Callum Styan Date: Mon, 18 Aug 2025 16:19:02 +0000 Subject: [PATCH 1/6] cache the seen user IDs in each iteration of the Agents metric loop to reduce calls to GetUserByID Signed-off-by: Callum Styan --- coderd/prometheusmetrics/prometheusmetrics.go | 32 ++++++++++++++++--- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/coderd/prometheusmetrics/prometheusmetrics.go b/coderd/prometheusmetrics/prometheusmetrics.go index cda274145e159..769d87d76ee9c 100644 --- a/coderd/prometheusmetrics/prometheusmetrics.go +++ b/coderd/prometheusmetrics/prometheusmetrics.go @@ -7,6 +7,7 @@ import ( "fmt" "strconv" "strings" + "sync" "sync/atomic" "time" @@ -296,6 +297,12 @@ func Agents(ctx context.Context, logger slog.Logger, registerer prometheus.Regis ctx = dbauthz.AsSystemRestricted(ctx) done := make(chan struct{}) + userCachePool := sync.Pool{ + New: func() interface{} { + return make(map[uuid.UUID]database.User) + }, + } + // Use time.Nanosecond to force an initial tick. It will be reset to the // correct duration after executing once. ticker := time.NewTicker(time.Nanosecond) @@ -313,6 +320,17 @@ func Agents(ctx context.Context, logger slog.Logger, registerer prometheus.Regis timer := prometheus.NewTimer(metricsCollectorAgents) derpMap := derpMapFn() + userCache, ok := userCachePool.Get().(map[uuid.UUID]database.User) + if !ok { + userCache = make(map[uuid.UUID]database.User) + } + defer func() { + for k := range userCache { + delete(userCache, k) + } + userCachePool.Put(userCache) + }() + workspaceRows, err := db.GetWorkspaces(ctx, database.GetWorkspacesParams{ AgentInactiveDisconnectTimeoutSeconds: int64(agentInactiveDisconnectTimeout.Seconds()), }) @@ -328,11 +346,15 @@ func Agents(ctx context.Context, logger slog.Logger, registerer prometheus.Regis templateVersionName = "unknown" } - user, err := db.GetUserByID(ctx, workspace.OwnerID) - if err != nil { - logger.Error(ctx, "can't get user from the database", slog.F("user_id", workspace.OwnerID), slog.Error(err)) - agentsGauge.WithLabelValues(VectorOperationAdd, 0, user.Username, workspace.Name, templateName, templateVersionName) - continue + user, ok := userCache[workspace.OwnerID] + if !ok { + user, err = db.GetUserByID(ctx, workspace.OwnerID) + if err != nil { + logger.Error(ctx, "can't get user from the database", slog.F("user_id", workspace.OwnerID), slog.Error(err)) + agentsGauge.WithLabelValues(VectorOperationAdd, 0, user.Username, workspace.Name, templateName, templateVersionName) + continue + } + userCache[workspace.OwnerID] = user } agents, err := db.GetWorkspaceAgentsInLatestBuildByWorkspaceID(ctx, workspace.ID) From a65de4a63fd3f69ee5221180c74994decd72df3e Mon Sep 17 00:00:00 2001 From: Callum Styan Date: Mon, 18 Aug 2025 17:33:12 +0000 Subject: [PATCH 2/6] Only cache the username instead of the entire user object Signed-off-by: Callum Styan --- coderd/prometheusmetrics/prometheusmetrics.go | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/coderd/prometheusmetrics/prometheusmetrics.go b/coderd/prometheusmetrics/prometheusmetrics.go index 769d87d76ee9c..d24fd0e4a883d 100644 --- a/coderd/prometheusmetrics/prometheusmetrics.go +++ b/coderd/prometheusmetrics/prometheusmetrics.go @@ -320,9 +320,9 @@ func Agents(ctx context.Context, logger slog.Logger, registerer prometheus.Regis timer := prometheus.NewTimer(metricsCollectorAgents) derpMap := derpMapFn() - userCache, ok := userCachePool.Get().(map[uuid.UUID]database.User) + userCache, ok := userCachePool.Get().(map[uuid.UUID]string) if !ok { - userCache = make(map[uuid.UUID]database.User) + userCache = make(map[uuid.UUID]string) } defer func() { for k := range userCache { @@ -346,33 +346,34 @@ func Agents(ctx context.Context, logger slog.Logger, registerer prometheus.Regis templateVersionName = "unknown" } - user, ok := userCache[workspace.OwnerID] + username, ok := userCache[workspace.OwnerID] if !ok { - user, err = db.GetUserByID(ctx, workspace.OwnerID) + user, err := db.GetUserByID(ctx, workspace.OwnerID) if err != nil { logger.Error(ctx, "can't get user from the database", slog.F("user_id", workspace.OwnerID), slog.Error(err)) agentsGauge.WithLabelValues(VectorOperationAdd, 0, user.Username, workspace.Name, templateName, templateVersionName) continue } - userCache[workspace.OwnerID] = user + username = user.Username + userCache[workspace.OwnerID] = username } agents, err := db.GetWorkspaceAgentsInLatestBuildByWorkspaceID(ctx, workspace.ID) if err != nil { logger.Error(ctx, "can't get workspace agents", slog.F("workspace_id", workspace.ID), slog.Error(err)) - agentsGauge.WithLabelValues(VectorOperationAdd, 0, user.Username, workspace.Name, templateName, templateVersionName) + agentsGauge.WithLabelValues(VectorOperationAdd, 0, username, workspace.Name, templateName, templateVersionName) continue } if len(agents) == 0 { logger.Debug(ctx, "workspace agents are unavailable", slog.F("workspace_id", workspace.ID)) - agentsGauge.WithLabelValues(VectorOperationAdd, 0, user.Username, workspace.Name, templateName, templateVersionName) + agentsGauge.WithLabelValues(VectorOperationAdd, 0, username, workspace.Name, templateName, templateVersionName) continue } for _, agent := range agents { // Collect information about agents - agentsGauge.WithLabelValues(VectorOperationAdd, 1, user.Username, workspace.Name, templateName, templateVersionName) + agentsGauge.WithLabelValues(VectorOperationAdd, 1, username, workspace.Name, templateName, templateVersionName) connectionStatus := agent.Status(agentInactiveDisconnectTimeout) node := (*coordinator.Load()).Node(agent.ID) @@ -382,7 +383,7 @@ func Agents(ctx context.Context, logger slog.Logger, registerer prometheus.Regis tailnetNode = node.ID.String() } - agentsConnectionsGauge.WithLabelValues(VectorOperationSet, 1, agent.Name, user.Username, workspace.Name, string(connectionStatus.Status), string(agent.LifecycleState), tailnetNode) + agentsConnectionsGauge.WithLabelValues(VectorOperationSet, 1, agent.Name, username, 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)) @@ -407,7 +408,7 @@ func Agents(ctx context.Context, logger slog.Logger, registerer prometheus.Regis } } - agentsConnectionLatenciesGauge.WithLabelValues(VectorOperationSet, latency, agent.Name, user.Username, workspace.Name, region.RegionName, fmt.Sprintf("%v", node.PreferredDERP == regionID)) + agentsConnectionLatenciesGauge.WithLabelValues(VectorOperationSet, latency, agent.Name, username, workspace.Name, region.RegionName, fmt.Sprintf("%v", node.PreferredDERP == regionID)) } } @@ -419,7 +420,7 @@ func Agents(ctx context.Context, logger slog.Logger, registerer prometheus.Regis } for _, app := range apps { - agentsAppsGauge.WithLabelValues(VectorOperationAdd, 1, agent.Name, user.Username, workspace.Name, app.DisplayName, string(app.Health)) + agentsAppsGauge.WithLabelValues(VectorOperationAdd, 1, agent.Name, username, workspace.Name, app.DisplayName, string(app.Health)) } } } From 6376d48efc53b8de74880fc46b2b192af2978590 Mon Sep 17 00:00:00 2001 From: Callum Styan Date: Mon, 18 Aug 2025 18:48:08 +0000 Subject: [PATCH 3/6] better optimization for metrics related GetUserByID calls Signed-off-by: Callum Styan --- coderd/prometheusmetrics/prometheusmetrics.go | 32 +++++++------------ coderd/workspacestats/reporter.go | 21 +++++++++--- 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/coderd/prometheusmetrics/prometheusmetrics.go b/coderd/prometheusmetrics/prometheusmetrics.go index d24fd0e4a883d..3eef3c57c9295 100644 --- a/coderd/prometheusmetrics/prometheusmetrics.go +++ b/coderd/prometheusmetrics/prometheusmetrics.go @@ -7,7 +7,6 @@ import ( "fmt" "strconv" "strings" - "sync" "sync/atomic" "time" @@ -297,12 +296,6 @@ func Agents(ctx context.Context, logger slog.Logger, registerer prometheus.Regis ctx = dbauthz.AsSystemRestricted(ctx) done := make(chan struct{}) - userCachePool := sync.Pool{ - New: func() interface{} { - return make(map[uuid.UUID]database.User) - }, - } - // Use time.Nanosecond to force an initial tick. It will be reset to the // correct duration after executing once. ticker := time.NewTicker(time.Nanosecond) @@ -320,17 +313,6 @@ func Agents(ctx context.Context, logger slog.Logger, registerer prometheus.Regis timer := prometheus.NewTimer(metricsCollectorAgents) derpMap := derpMapFn() - userCache, ok := userCachePool.Get().(map[uuid.UUID]string) - if !ok { - userCache = make(map[uuid.UUID]string) - } - defer func() { - for k := range userCache { - delete(userCache, k) - } - userCachePool.Put(userCache) - }() - workspaceRows, err := db.GetWorkspaces(ctx, database.GetWorkspacesParams{ AgentInactiveDisconnectTimeoutSeconds: int64(agentInactiveDisconnectTimeout.Seconds()), }) @@ -346,8 +328,17 @@ func Agents(ctx context.Context, logger slog.Logger, registerer prometheus.Regis templateVersionName = "unknown" } - username, ok := userCache[workspace.OwnerID] - if !ok { + username := workspace.OwnerUsername + // This should never be possible, but we'll guard against it just in case. + // 1. The owner_id field on the workspaces table is a reference to IDs in the users table and has a `NUT NULL` constraint + // 2. At user creation time our httpmw package enforces non-empty usernames + // 3. The workspaces_expanded view has an inner join on workspaces.owner_id = visible_users.id, and if the owner + // is valid in the users table (which visible_users is a view of) then they will have a username set + if username == "" { + logger.Warn(ctx, "in prometheusmetrics.Agents the username on workspace was empty string, this should not be possible", + slog.F("workspace_id", workspace.ID), + slog.F("template_id", workspace.TemplateID)) + // Fallback to GetUserByID if OwnerUsername is empty (e.g., in tests) user, err := db.GetUserByID(ctx, workspace.OwnerID) if err != nil { logger.Error(ctx, "can't get user from the database", slog.F("user_id", workspace.OwnerID), slog.Error(err)) @@ -355,7 +346,6 @@ func Agents(ctx context.Context, logger slog.Logger, registerer prometheus.Regis continue } username = user.Username - userCache[workspace.OwnerID] = username } agents, err := db.GetWorkspaceAgentsInLatestBuildByWorkspaceID(ctx, workspace.ID) diff --git a/coderd/workspacestats/reporter.go b/coderd/workspacestats/reporter.go index 58d177f1c2071..fb076512c1ba1 100644 --- a/coderd/workspacestats/reporter.go +++ b/coderd/workspacestats/reporter.go @@ -126,13 +126,26 @@ func (r *Reporter) ReportAgentStats(ctx context.Context, now time.Time, workspac // update prometheus metrics if r.opts.UpdateAgentMetricsFn != nil { - user, err := r.opts.Database.GetUserByID(ctx, workspace.OwnerID) - if err != nil { - return xerrors.Errorf("get user: %w", err) + username := workspace.OwnerUsername + // This should never be possible, but we'll guard against it just in case. + // 1. The owner_id field on the workspaces table is a reference to IDs in the users table and has a `NUT NULL` constraint + // 2. At user creation time our httpmw package enforces non-empty usernames + // 3. The workspaces_expanded view has an inner join on workspaces.owner_id = visible_users.id, and if the owner + // is valid in the users table (which visible_users is a view of) then they will have a username set + if username == "" { + r.opts.Logger.Warn(ctx, "in ReportAgentStats the username on workspace was empty string, this should not be possible", + slog.F("workspace_id", workspace.ID), + slog.F("template_id", workspace.TemplateID)) + // Fallback to GetUserByID if OwnerUsername is empty (e.g., in tests) + user, err := r.opts.Database.GetUserByID(ctx, workspace.OwnerID) + if err != nil { + return xerrors.Errorf("get user: %w", err) + } + username = user.Username } r.opts.UpdateAgentMetricsFn(ctx, prometheusmetrics.AgentMetricLabels{ - Username: user.Username, + Username: username, WorkspaceName: workspace.Name, AgentName: workspaceAgent.Name, TemplateName: templateName, From 332acb5a5fe6167be08b6f10d0ac881cf5fdac4a Mon Sep 17 00:00:00 2001 From: Callum Styan Date: Mon, 18 Aug 2025 18:52:46 +0000 Subject: [PATCH 4/6] TYPO not Signed-off-by: Callum Styan --- coderd/prometheusmetrics/prometheusmetrics.go | 2 +- coderd/workspacestats/reporter.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/coderd/prometheusmetrics/prometheusmetrics.go b/coderd/prometheusmetrics/prometheusmetrics.go index 3eef3c57c9295..37e1eb265eabc 100644 --- a/coderd/prometheusmetrics/prometheusmetrics.go +++ b/coderd/prometheusmetrics/prometheusmetrics.go @@ -330,7 +330,7 @@ func Agents(ctx context.Context, logger slog.Logger, registerer prometheus.Regis username := workspace.OwnerUsername // This should never be possible, but we'll guard against it just in case. - // 1. The owner_id field on the workspaces table is a reference to IDs in the users table and has a `NUT NULL` constraint + // 1. The owner_id field on the workspaces table is a reference to IDs in the users table and has a `NOT NULL` constraint // 2. At user creation time our httpmw package enforces non-empty usernames // 3. The workspaces_expanded view has an inner join on workspaces.owner_id = visible_users.id, and if the owner // is valid in the users table (which visible_users is a view of) then they will have a username set diff --git a/coderd/workspacestats/reporter.go b/coderd/workspacestats/reporter.go index fb076512c1ba1..e33f70bfae65d 100644 --- a/coderd/workspacestats/reporter.go +++ b/coderd/workspacestats/reporter.go @@ -128,7 +128,7 @@ func (r *Reporter) ReportAgentStats(ctx context.Context, now time.Time, workspac if r.opts.UpdateAgentMetricsFn != nil { username := workspace.OwnerUsername // This should never be possible, but we'll guard against it just in case. - // 1. The owner_id field on the workspaces table is a reference to IDs in the users table and has a `NUT NULL` constraint + // 1. The owner_id field on the workspaces table is a reference to IDs in the users table and has a `NOT NULL` constraint // 2. At user creation time our httpmw package enforces non-empty usernames // 3. The workspaces_expanded view has an inner join on workspaces.owner_id = visible_users.id, and if the owner // is valid in the users table (which visible_users is a view of) then they will have a username set From 43b122879a2584e6d9280f582e03ec20c32194af Mon Sep 17 00:00:00 2001 From: Callum Styan Date: Thu, 21 Aug 2025 16:33:51 +0000 Subject: [PATCH 5/6] don't need fallback DB query anymore since we added username min length constraint to users table Signed-off-by: Callum Styan --- coderd/prometheusmetrics/prometheusmetrics.go | 32 ++++--------------- coderd/workspacestats/reporter.go | 20 +----------- 2 files changed, 8 insertions(+), 44 deletions(-) diff --git a/coderd/prometheusmetrics/prometheusmetrics.go b/coderd/prometheusmetrics/prometheusmetrics.go index 37e1eb265eabc..6ea8615f3779a 100644 --- a/coderd/prometheusmetrics/prometheusmetrics.go +++ b/coderd/prometheusmetrics/prometheusmetrics.go @@ -328,42 +328,24 @@ func Agents(ctx context.Context, logger slog.Logger, registerer prometheus.Regis templateVersionName = "unknown" } - username := workspace.OwnerUsername - // This should never be possible, but we'll guard against it just in case. - // 1. The owner_id field on the workspaces table is a reference to IDs in the users table and has a `NOT NULL` constraint - // 2. At user creation time our httpmw package enforces non-empty usernames - // 3. The workspaces_expanded view has an inner join on workspaces.owner_id = visible_users.id, and if the owner - // is valid in the users table (which visible_users is a view of) then they will have a username set - if username == "" { - logger.Warn(ctx, "in prometheusmetrics.Agents the username on workspace was empty string, this should not be possible", - slog.F("workspace_id", workspace.ID), - slog.F("template_id", workspace.TemplateID)) - // Fallback to GetUserByID if OwnerUsername is empty (e.g., in tests) - user, err := db.GetUserByID(ctx, workspace.OwnerID) - if err != nil { - logger.Error(ctx, "can't get user from the database", slog.F("user_id", workspace.OwnerID), slog.Error(err)) - agentsGauge.WithLabelValues(VectorOperationAdd, 0, user.Username, workspace.Name, templateName, templateVersionName) - continue - } - username = user.Username - } + // username := agents, err := db.GetWorkspaceAgentsInLatestBuildByWorkspaceID(ctx, workspace.ID) if err != nil { logger.Error(ctx, "can't get workspace agents", slog.F("workspace_id", workspace.ID), slog.Error(err)) - agentsGauge.WithLabelValues(VectorOperationAdd, 0, username, workspace.Name, templateName, templateVersionName) + agentsGauge.WithLabelValues(VectorOperationAdd, 0, workspace.OwnerUsername, workspace.Name, templateName, templateVersionName) continue } if len(agents) == 0 { logger.Debug(ctx, "workspace agents are unavailable", slog.F("workspace_id", workspace.ID)) - agentsGauge.WithLabelValues(VectorOperationAdd, 0, username, workspace.Name, templateName, templateVersionName) + agentsGauge.WithLabelValues(VectorOperationAdd, 0, workspace.OwnerUsername, workspace.Name, templateName, templateVersionName) continue } for _, agent := range agents { // Collect information about agents - agentsGauge.WithLabelValues(VectorOperationAdd, 1, username, workspace.Name, templateName, templateVersionName) + agentsGauge.WithLabelValues(VectorOperationAdd, 1, workspace.OwnerUsername, workspace.Name, templateName, templateVersionName) connectionStatus := agent.Status(agentInactiveDisconnectTimeout) node := (*coordinator.Load()).Node(agent.ID) @@ -373,7 +355,7 @@ func Agents(ctx context.Context, logger slog.Logger, registerer prometheus.Regis tailnetNode = node.ID.String() } - agentsConnectionsGauge.WithLabelValues(VectorOperationSet, 1, agent.Name, username, workspace.Name, string(connectionStatus.Status), string(agent.LifecycleState), tailnetNode) + 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)) @@ -398,7 +380,7 @@ func Agents(ctx context.Context, logger slog.Logger, registerer prometheus.Regis } } - agentsConnectionLatenciesGauge.WithLabelValues(VectorOperationSet, latency, agent.Name, username, workspace.Name, region.RegionName, fmt.Sprintf("%v", node.PreferredDERP == regionID)) + agentsConnectionLatenciesGauge.WithLabelValues(VectorOperationSet, latency, agent.Name, workspace.OwnerUsername, workspace.Name, region.RegionName, fmt.Sprintf("%v", node.PreferredDERP == regionID)) } } @@ -410,7 +392,7 @@ func Agents(ctx context.Context, logger slog.Logger, registerer prometheus.Regis } for _, app := range apps { - agentsAppsGauge.WithLabelValues(VectorOperationAdd, 1, agent.Name, username, workspace.Name, app.DisplayName, string(app.Health)) + agentsAppsGauge.WithLabelValues(VectorOperationAdd, 1, agent.Name, workspace.OwnerUsername, workspace.Name, app.DisplayName, string(app.Health)) } } } diff --git a/coderd/workspacestats/reporter.go b/coderd/workspacestats/reporter.go index e33f70bfae65d..d8b01bf502904 100644 --- a/coderd/workspacestats/reporter.go +++ b/coderd/workspacestats/reporter.go @@ -126,26 +126,8 @@ func (r *Reporter) ReportAgentStats(ctx context.Context, now time.Time, workspac // update prometheus metrics if r.opts.UpdateAgentMetricsFn != nil { - username := workspace.OwnerUsername - // This should never be possible, but we'll guard against it just in case. - // 1. The owner_id field on the workspaces table is a reference to IDs in the users table and has a `NOT NULL` constraint - // 2. At user creation time our httpmw package enforces non-empty usernames - // 3. The workspaces_expanded view has an inner join on workspaces.owner_id = visible_users.id, and if the owner - // is valid in the users table (which visible_users is a view of) then they will have a username set - if username == "" { - r.opts.Logger.Warn(ctx, "in ReportAgentStats the username on workspace was empty string, this should not be possible", - slog.F("workspace_id", workspace.ID), - slog.F("template_id", workspace.TemplateID)) - // Fallback to GetUserByID if OwnerUsername is empty (e.g., in tests) - user, err := r.opts.Database.GetUserByID(ctx, workspace.OwnerID) - if err != nil { - return xerrors.Errorf("get user: %w", err) - } - username = user.Username - } - r.opts.UpdateAgentMetricsFn(ctx, prometheusmetrics.AgentMetricLabels{ - Username: username, + Username: workspace.OwnerUsername, WorkspaceName: workspace.Name, AgentName: workspaceAgent.Name, TemplateName: templateName, From ff0680e264b27308df01e7bf9fb98e724ac63277 Mon Sep 17 00:00:00 2001 From: Callum Styan Date: Thu, 21 Aug 2025 16:57:15 +0000 Subject: [PATCH 6/6] fix stats_tests.go based on username/not calling GetUserByID Signed-off-by: Callum Styan --- coderd/agentapi/stats_test.go | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/coderd/agentapi/stats_test.go b/coderd/agentapi/stats_test.go index 3ebf99aa6bc4b..aec2d68b71c12 100644 --- a/coderd/agentapi/stats_test.go +++ b/coderd/agentapi/stats_test.go @@ -41,11 +41,12 @@ func TestUpdateStates(t *testing.T) { Name: "tpl", } workspace = database.Workspace{ - ID: uuid.New(), - OwnerID: user.ID, - TemplateID: template.ID, - Name: "xyz", - TemplateName: template.Name, + ID: uuid.New(), + OwnerID: user.ID, + OwnerUsername: user.Username, + TemplateID: template.ID, + Name: "xyz", + TemplateName: template.Name, } agent = database.WorkspaceAgent{ ID: uuid.New(), @@ -138,9 +139,6 @@ func TestUpdateStates(t *testing.T) { // Workspace gets fetched. dbM.EXPECT().GetWorkspaceByAgentID(gomock.Any(), agent.ID).Return(workspace, nil) - // User gets fetched to hit the UpdateAgentMetricsFn. - dbM.EXPECT().GetUserByID(gomock.Any(), user.ID).Return(user, nil) - // We expect an activity bump because ConnectionCount > 0. dbM.EXPECT().ActivityBumpWorkspace(gomock.Any(), database.ActivityBumpWorkspaceParams{ WorkspaceID: workspace.ID, @@ -380,9 +378,6 @@ func TestUpdateStates(t *testing.T) { LastUsedAt: now.UTC(), }).Return(nil) - // User gets fetched to hit the UpdateAgentMetricsFn. - dbM.EXPECT().GetUserByID(gomock.Any(), user.ID).Return(user, nil) - resp, err := api.UpdateStats(context.Background(), req) require.NoError(t, err) require.Equal(t, &agentproto.UpdateStatsResponse{ @@ -498,9 +493,6 @@ func TestUpdateStates(t *testing.T) { LastUsedAt: now, }).Return(nil) - // User gets fetched to hit the UpdateAgentMetricsFn. - dbM.EXPECT().GetUserByID(gomock.Any(), user.ID).Return(user, nil) - // Ensure that pubsub notifications are sent. notifyDescription := make(chan struct{}) ps.SubscribeWithErr(wspubsub.WorkspaceEventChannel(workspace.OwnerID), pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy