perf: support fastpath in dbauthz GetLatestWorkspaceBuildByWorkspaceID (#21047)

This PR piggy backs on the agent API cached workspace added in earlier PRs to provide a fast path for avoiding `GetWorkspaceByID` calls in `GetLatestWorkspaceBuildByWorkspaceID` via injection of the workspaces RBAC object into the context. We can do this from the `agentConnectionMonitor` easily since we already cache the workspace.

---------

Signed-off-by: Callum Styan <callumstyan@gmail.com>
This commit is contained in:
Callum Styan
2025-12-09 15:53:52 -08:00
committed by GitHub
parent a59a84b2a7
commit 27c3ec072e
4 changed files with 118 additions and 4 deletions
+12
View File
@@ -2455,6 +2455,18 @@ func (q *querier) GetLatestWorkspaceAppStatusesByWorkspaceIDs(ctx context.Contex
}
func (q *querier) GetLatestWorkspaceBuildByWorkspaceID(ctx context.Context, workspaceID uuid.UUID) (database.WorkspaceBuild, error) {
// Fast path: Check if we have a workspace RBAC object in context.
if rbacObj, ok := WorkspaceRBACFromContext(ctx); ok {
// Errors here will result in falling back to GetWorkspaceByAgentID,
// in case the cached data is stale.
if err := q.authorizeContext(ctx, policy.ActionRead, rbacObj); err == nil {
return q.db.GetLatestWorkspaceBuildByWorkspaceID(ctx, workspaceID)
}
q.log.Debug(ctx, "fast path authorization failed for GetLatestWorkspaceBuildByWorkspaceID, using slow path",
slog.F("workspace_id", workspaceID))
}
if _, err := q.GetWorkspaceByID(ctx, workspaceID); err != nil {
return database.WorkspaceBuild{}, err
}
+74
View File
@@ -4731,3 +4731,77 @@ func (s *MethodTestSuite) TestTelemetry() {
check.Args(database.CalculateAIBridgeInterceptionsTelemetrySummaryParams{}).Asserts(rbac.ResourceAibridgeInterception, policy.ActionRead)
}))
}
func TestGetLatestWorkspaceBuildByWorkspaceID_FastPath(t *testing.T) {
t.Parallel()
ownerID := uuid.New()
wsID := uuid.New()
orgID := uuid.New()
workspace := database.Workspace{
ID: wsID,
OwnerID: ownerID,
OrganizationID: orgID,
}
build := database.WorkspaceBuild{
ID: uuid.New(),
WorkspaceID: wsID,
}
wsIdentity := database.WorkspaceIdentity{
ID: wsID,
OwnerID: ownerID,
OrganizationID: orgID,
}
actor := rbac.Subject{
ID: ownerID.String(),
Roles: rbac.RoleIdentifiers{rbac.RoleOwner()},
Groups: []string{orgID.String()},
Scope: rbac.ScopeAll,
}
authorizer := &coderdtest.RecordingAuthorizer{
Wrapped: (&coderdtest.FakeAuthorizer{}).AlwaysReturn(nil),
}
t.Run("WithWorkspaceRBAC", func(t *testing.T) {
t.Parallel()
ctx := dbauthz.As(context.Background(), actor)
ctrl := gomock.NewController(t)
dbm := dbmock.NewMockStore(ctrl)
rbacObj := wsIdentity.RBACObject()
ctx, err := dbauthz.WithWorkspaceRBAC(ctx, rbacObj)
require.NoError(t, err)
dbm.EXPECT().GetLatestWorkspaceBuildByWorkspaceID(gomock.Any(), workspace.ID).Return(build, nil).AnyTimes()
dbm.EXPECT().Wrappers().Return([]string{})
q := dbauthz.New(dbm, authorizer, slogtest.Make(t, nil), coderdtest.AccessControlStorePointer())
result, err := q.GetLatestWorkspaceBuildByWorkspaceID(ctx, workspace.ID)
require.NoError(t, err)
require.Equal(t, build, result)
})
t.Run("WithoutWorkspaceRBAC", func(t *testing.T) {
t.Parallel()
ctx := dbauthz.As(context.Background(), actor)
ctrl := gomock.NewController(t)
dbm := dbmock.NewMockStore(ctrl)
dbm.EXPECT().GetWorkspaceByID(gomock.Any(), wsID).Return(workspace, nil).AnyTimes()
dbm.EXPECT().GetLatestWorkspaceBuildByWorkspaceID(gomock.Any(), workspace.ID).Return(build, nil).AnyTimes()
dbm.EXPECT().Wrappers().Return([]string{})
q := dbauthz.New(dbm, authorizer, slogtest.Make(t, nil), coderdtest.AccessControlStorePointer())
result, err := q.GetLatestWorkspaceBuildByWorkspaceID(ctx, workspace.ID)
require.NoError(t, err)
require.Equal(t, build, result)
})
}