perf: reduce number of queries made by /api/v2/workspaceagents/{id} (#21522)

Relates to https://github.com/coder/internal/issues/1214

The `ExtractWorkspaceAgentParam` middleware ends up making 4 database
queries to follow the chain of `WorkspaceAgent` -> `WorkspaceResource`
-> `ProvisionerJob` -> `WorkspaceBuild` -- but then dropping all that
hard work on the floor. The `api.workspaceAgent` handler that references
this middleware then has to do all of that work again, plus one more
query to get the related `User` so we can get the username. This pattern
is also mirrored in `getDatabaseTerminal` but without the middleware.

This PR:
* Adds a new query `GetWorkspaceAgentAndWorkspaceByID` to fetch all
this information at once to avoid the multiple round-trips,
* Updates the existing usage of `GetWorkspaceAgentByID` to this new
query instead,
* Updates `ExtractWorkspaceAgentParam` to also store the workspace in
the request context

Dalibo: [0.63ms](https://explain.dalibo.com/plan/40bb597f3539gc6c)
This commit is contained in:
Cian Johnston
2026-01-19 12:36:33 +00:00
committed by GitHub
parent d176714f90
commit 08343a7a9f
17 changed files with 259 additions and 230 deletions
+1 -1
View File
@@ -148,7 +148,7 @@ func (p *DBTokenProvider) Issue(ctx context.Context, rw http.ResponseWriter, r *
aReq.dbReq = dbReq // Update audit request.
token.UserID = dbReq.User.ID
token.UserID = dbReq.UserID
token.WorkspaceID = dbReq.Workspace.ID
token.AgentID = dbReq.Agent.ID
if dbReq.AppURL != nil {
+10 -34
View File
@@ -188,10 +188,10 @@ func (r Request) Check() error {
type databaseRequest struct {
Request
// User is the user that owns the app.
User database.User
// UserID is the ID of the user that owns the app.
UserID uuid.UUID
// Workspace is the workspace that the app is in.
Workspace database.Workspace
Workspace database.WorkspaceTable
// Agent is the agent that the app is running on.
Agent database.WorkspaceAgent
// App is the app that the user is trying to access.
@@ -420,8 +420,8 @@ func (r Request) getDatabase(ctx context.Context, db database.Store) (*databaseR
return &databaseRequest{
Request: r,
User: user,
Workspace: workspace,
UserID: user.ID,
Workspace: workspace.WorkspaceTable(),
Agent: agent,
App: app,
AppURL: appURLParsed,
@@ -443,40 +443,16 @@ func (r Request) getDatabaseTerminal(ctx context.Context, db database.Store) (*d
}
var err error
agent, err := db.GetWorkspaceAgentByID(ctx, agentID)
aw, err := db.GetWorkspaceAgentAndWorkspaceByID(ctx, agentID)
if err != nil {
return nil, xerrors.Errorf("get workspace agent %q: %w", agentID, err)
}
// Get the corresponding resource.
res, err := db.GetWorkspaceResourceByID(ctx, agent.ResourceID)
if err != nil {
return nil, xerrors.Errorf("get workspace agent resource %q: %w", agent.ResourceID, err)
}
// Get the corresponding workspace build.
build, err := db.GetWorkspaceBuildByJobID(ctx, res.JobID)
if err != nil {
return nil, xerrors.Errorf("get workspace build by job ID %q: %w", res.JobID, err)
}
// Get the corresponding workspace.
workspace, err := db.GetWorkspaceByID(ctx, build.WorkspaceID)
if err != nil {
return nil, xerrors.Errorf("get workspace %q: %w", build.WorkspaceID, err)
}
// Get the workspace's owner.
user, err := db.GetUserByID(ctx, workspace.OwnerID)
if err != nil {
return nil, xerrors.Errorf("get user %q: %w", workspace.OwnerID, err)
return nil, xerrors.Errorf("get workspace agent %q with workspace: %w", agentID, err)
}
return &databaseRequest{
Request: r,
User: user,
Workspace: workspace,
Agent: agent,
UserID: aw.WorkspaceTable.OwnerID,
Workspace: aw.WorkspaceTable,
Agent: aw.WorkspaceAgent,
AppURL: nil,
AppSharingLevel: database.AppSharingLevelOwner,
}, nil