feat: Turn on rbac check caching (#6202)

* chore: Turn on rbac check caching.

Should not affect much unless authz_querier experiment is
enabled
This commit is contained in:
Steven Masley
2023-02-15 08:56:07 -06:00
committed by GitHub
parent fac7c02eeb
commit 4cbbd1376d
9 changed files with 61 additions and 9 deletions
+7
View File
@@ -158,6 +158,13 @@ var (
partialQuery rego.PreparedPartialQuery
)
// NewCachingAuthorizer returns a new RegoAuthorizer that supports context based
// caching. To utilize the caching, the context passed to Authorize() must be
// created with 'WithCacheCtx(ctx)'.
func NewCachingAuthorizer(registry prometheus.Registerer) Authorizer {
return Cacher(NewAuthorizer(registry))
}
func NewAuthorizer(registry prometheus.Registerer) *RegoAuthorizer {
queryOnce.Do(func() {
var err error
+3 -3
View File
@@ -109,7 +109,7 @@ func BenchmarkRBACAuthorize(b *testing.B) {
uuid.MustParse("0632b012-49e0-4d70-a5b3-f4398f1dcd52"),
uuid.MustParse("70dbaa7a-ea9c-4f68-a781-97b08af8461d"),
)
authorizer := rbac.NewAuthorizer(prometheus.NewRegistry())
authorizer := rbac.NewCachingAuthorizer(prometheus.NewRegistry())
// This benchmarks all the simple cases using just user permissions. Groups
// are added as noise, but do not do anything.
for _, c := range benchCases {
@@ -136,7 +136,7 @@ func BenchmarkRBACAuthorizeGroups(b *testing.B) {
uuid.MustParse("0632b012-49e0-4d70-a5b3-f4398f1dcd52"),
uuid.MustParse("70dbaa7a-ea9c-4f68-a781-97b08af8461d"),
)
authorizer := rbac.NewAuthorizer(prometheus.NewRegistry())
authorizer := rbac.NewCachingAuthorizer(prometheus.NewRegistry())
// Same benchmark cases, but this time groups will be used to match.
// Some '*' permissions will still match, but using a fake action reduces
@@ -188,7 +188,7 @@ func BenchmarkRBACFilter(b *testing.B) {
uuid.MustParse("70dbaa7a-ea9c-4f68-a781-97b08af8461d"),
)
authorizer := rbac.NewAuthorizer(prometheus.NewRegistry())
authorizer := rbac.NewCachingAuthorizer(prometheus.NewRegistry())
for _, c := range benchCases {
b.Run("PrepareOnly-"+c.Name, func(b *testing.B) {
+1 -1
View File
@@ -23,7 +23,7 @@ type authSubject struct {
func TestRolePermissions(t *testing.T) {
t.Parallel()
auth := rbac.NewAuthorizer(prometheus.NewRegistry())
auth := rbac.NewCachingAuthorizer(prometheus.NewRegistry())
// currentUser is anything that references "me", "mine", or "my".
currentUser := uuid.New()
+2
View File
@@ -20,6 +20,8 @@ type cachedCalls struct {
// multiple calls are made to the Authorizer for the same subject, action, and
// object. The cache is on each `ctx` and is not shared between requests.
// If no cache is found on the context, the Authorizer is called as normal.
//
// Cacher is safe for multiple actors.
func Cacher(authz Authorizer) Authorizer {
return &cachedCalls{authz: authz}
}
+28
View File
@@ -2,6 +2,7 @@ package rbac_test
import (
"context"
"fmt"
"testing"
"github.com/stretchr/testify/require"
@@ -10,6 +11,33 @@ import (
"github.com/coder/coder/coderd/rbac"
)
// BenchmarkCacher benchmarks the performance of the cacher with a given
// cache size. The expected cache size in prod will usually be 1-2. In Filter
// cases it can get as high as 10.
func BenchmarkCacher(b *testing.B) {
b.ResetTimer()
// Size of the cache.
sizes := []int{1, 10, 100, 1000}
for _, size := range sizes {
b.Run(fmt.Sprintf("Size%d", size), func(b *testing.B) {
ctx := rbac.WithCacheCtx(context.Background())
authz := rbac.Cacher(&coderdtest.FakeAuthorizer{AlwaysReturn: nil})
for i := 0; i < size; i++ {
// Preload the cache of a given size
subj, obj, action := coderdtest.RandomRBACSubject(), coderdtest.RandomRBACObject(), coderdtest.RandomRBACAction()
_ = authz.Authorize(ctx, subj, action, obj)
}
// Cache is loaded as a slice, so this cache hit is always the last element.
subj, obj, action := coderdtest.RandomRBACSubject(), coderdtest.RandomRBACObject(), coderdtest.RandomRBACAction()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = authz.Authorize(ctx, subj, action, obj)
}
})
}
}
func TestCacher(t *testing.T) {
t.Parallel()