The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/pkg/services/accesscontrol/acimpl/service_bench_test.go

235 lines
9.0 KiB

package acimpl
import (
"context"
"fmt"
"testing"
"time"
"github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/accesscontrol/actest"
"github.com/grafana/grafana/pkg/services/accesscontrol/database"
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting"
"github.com/stretchr/testify/require"
)
// setupBenchEnv will create userCount users, userCount managed roles with resourceCount managed permission each
// Example: setupBenchEnv(b, 2, 3):
// - will create 2 users and assign them 2 managed roles
// - each managed role will have 3 permissions {"resources:action2", "resources:id:x"} where x belongs to [1, 3]
func setupBenchEnv(b *testing.B, usersCount, resourceCount int) (accesscontrol.Service, *user.SignedInUser) {
now := time.Now()
sqlStore := db.InitTestDB(b)
store := database.ProvideService(sqlStore)
acService := &Service{
cfg: setting.NewCfg(),
log: log.New("accesscontrol-test"),
registrations: accesscontrol.RegistrationList{},
store: store,
roles: accesscontrol.BuildBasicRoleDefinitions(),
}
// Prepare default permissions
action1 := "resources:action1"
err := acService.DeclareFixedRoles(accesscontrol.RoleRegistration{
Role: accesscontrol.RoleDTO{Name: "fixed:test:role", Permissions: []accesscontrol.Permission{{Action: action1}}},
Grants: []string{string(org.RoleViewer)},
})
require.NoError(b, err)
err = acService.RegisterFixedRoles(context.Background())
require.NoError(b, err)
// Populate users, roles and assignments
if errInsert := actest.ConcurrentBatch(actest.Concurrency, usersCount, actest.BatchSize, func(start, end int) error {
n := end - start
users := make([]user.User, 0, n)
orgUsers := make([]org.OrgUser, 0, n)
roles := make([]accesscontrol.Role, 0, n)
userRoles := make([]accesscontrol.UserRole, 0, n)
for u := start + 1; u < end+1; u++ {
users = append(users, user.User{
ID: int64(u),
Name: fmt.Sprintf("user%v", u),
Login: fmt.Sprintf("user%v", u),
Email: fmt.Sprintf("user%v@example.org", u),
Created: now,
Updated: now,
})
orgUsers = append(orgUsers, org.OrgUser{
ID: int64(u),
UserID: int64(u),
OrgID: 1,
Role: org.RoleViewer,
Created: now,
Updated: now,
})
roles = append(roles, accesscontrol.Role{
ID: int64(u),
UID: fmt.Sprintf("managed_users_%v_permissions", u),
Name: fmt.Sprintf("managed:users:%v:permissions", u),
OrgID: 1,
Version: 1,
Created: now,
Updated: now,
})
userRoles = append(userRoles, accesscontrol.UserRole{
ID: int64(u),
OrgID: 1,
RoleID: int64(u),
UserID: int64(u),
Created: now,
})
}
err := sqlStore.WithDbSession(context.Background(), func(sess *db.Session) error {
if _, err := sess.Insert(users); err != nil {
return err
}
if _, err := sess.Insert(orgUsers); err != nil {
return err
}
if _, err := sess.Insert(roles); err != nil {
return err
}
_, err := sess.Insert(userRoles)
return err
})
return err
}); errInsert != nil {
require.NoError(b, errInsert, "could not insert users and roles")
return nil, nil
}
// Populate permissions
action2 := "resources:action2"
if errInsert := actest.ConcurrentBatch(actest.Concurrency, resourceCount*usersCount, actest.BatchSize, func(start, end int) error {
permissions := make([]accesscontrol.Permission, 0, end-start)
for i := start; i < end; i++ {
permissions = append(permissions, accesscontrol.Permission{
RoleID: int64(i/resourceCount + 1),
Action: action2,
Scope: fmt.Sprintf("resources:id:%v", i%resourceCount+1),
Created: now,
Updated: now,
})
}
return sqlStore.WithDbSession(context.Background(), func(sess *db.Session) error {
_, err := sess.Insert(permissions)
return err
})
}); errInsert != nil {
require.NoError(b, errInsert, "could not insert permissions")
return nil, nil
}
// Allow signed in user to view all users permissions in the worst way
userPermissions := map[string][]string{}
for u := 1; u < usersCount+1; u++ {
userPermissions[accesscontrol.ActionUsersPermissionsRead] =
append(userPermissions[accesscontrol.ActionUsersPermissionsRead], fmt.Sprintf("users:id:%v", u))
}
return acService, &user.SignedInUser{OrgID: 1, Permissions: map[int64]map[string][]string{1: userPermissions}}
}
func benchSearchUsersPermissions(b *testing.B, usersCount, resourceCount int) {
acService, siu := setupBenchEnv(b, usersCount, resourceCount)
b.ResetTimer()
for n := 0; n < b.N; n++ {
usersPermissions, err := acService.SearchUsersPermissions(context.Background(), siu, 1, accesscontrol.SearchOptions{ActionPrefix: "resources:"})
require.NoError(b, err)
require.Len(b, usersPermissions, usersCount)
for _, permissions := range usersPermissions {
// action1 on all resource + action2
require.Len(b, permissions, resourceCount+1)
}
}
}
// Lots of resources
func BenchmarkSearchUsersPermissions_10_1K(b *testing.B) { benchSearchUsersPermissions(b, 10, 1000) } // ~0.047s/op
func BenchmarkSearchUsersPermissions_10_10K(b *testing.B) { benchSearchUsersPermissions(b, 10, 10000) } // ~0.5s/op
func BenchmarkSearchUsersPermissions_10_100K(b *testing.B) {
if testing.Short() {
b.Skip("Skipping benchmark in short mode")
}
benchSearchUsersPermissions(b, 10, 100000)
} // ~4.6s/op
func BenchmarkSearchUsersPermissions_10_1M(b *testing.B) {
if testing.Short() {
b.Skip("Skipping benchmark in short mode")
}
benchSearchUsersPermissions(b, 10, 1000000)
} // ~55.36s/op
// Lots of users (most probable case)
func BenchmarkSearchUsersPermissions_1K_10(b *testing.B) { benchSearchUsersPermissions(b, 1000, 10) } // ~0.056s/op
func BenchmarkSearchUsersPermissions_10K_10(b *testing.B) { benchSearchUsersPermissions(b, 10000, 10) } // ~0.58s/op
func BenchmarkSearchUsersPermissions_100K_10(b *testing.B) {
if testing.Short() {
b.Skip("Skipping benchmark in short mode")
}
benchSearchUsersPermissions(b, 100000, 10)
} // ~6.21s/op
func BenchmarkSearchUsersPermissions_1M_10(b *testing.B) {
if testing.Short() {
b.Skip("Skipping benchmark in short mode")
}
benchSearchUsersPermissions(b, 1000000, 10)
} // ~57s/op
// Lots of both
func BenchmarkSearchUsersPermissions_10K_100(b *testing.B) {
if testing.Short() {
b.Skip("Skipping benchmark in short mode")
}
benchSearchUsersPermissions(b, 10000, 100)
} // ~1.45s/op
func BenchmarkSearchUsersPermissions_10K_1K(b *testing.B) {
if testing.Short() {
b.Skip("Skipping benchmark in short mode")
}
benchSearchUsersPermissions(b, 10000, 1000)
} // ~50s/op
// Benchmarking search when we specify Action and Scope
func benchSearchUsersWithPerm(b *testing.B, usersCount, resourceCount int) {
if testing.Short() {
b.Skip("Skipping benchmark in short mode")
}
acService, siu := setupBenchEnv(b, usersCount, resourceCount)
b.ResetTimer()
for n := 0; n < b.N; n++ {
usersPermissions, err := acService.SearchUsersPermissions(context.Background(), siu, 1,
accesscontrol.SearchOptions{Action: "resources:action2", Scope: "resources:id:1"})
require.NoError(b, err)
require.Len(b, usersPermissions, usersCount)
for _, permissions := range usersPermissions {
require.Len(b, permissions, 1)
}
}
}
func BenchmarkSearchUsersWithPerm_1K_10(b *testing.B) { benchSearchUsersWithPerm(b, 1000, 10) } // ~0.045s/op
func BenchmarkSearchUsersWithPerm_1K_100(b *testing.B) { benchSearchUsersWithPerm(b, 1000, 100) } // ~0.038s/op
func BenchmarkSearchUsersWithPerm_1K_1K(b *testing.B) { benchSearchUsersWithPerm(b, 1000, 1000) } // ~0.033s/op
func BenchmarkSearchUsersWithPerm_1K_10K(b *testing.B) { benchSearchUsersWithPerm(b, 1000, 10000) } // ~0.033s/op
func BenchmarkSearchUsersWithPerm_1K_100K(b *testing.B) { benchSearchUsersWithPerm(b, 1000, 100000) } // ~0.056s/op
func BenchmarkSearchUsersWithPerm_10K_10(b *testing.B) { benchSearchUsersWithPerm(b, 10000, 10) } // ~0.11s/op
func BenchmarkSearchUsersWithPerm_10K_100(b *testing.B) { benchSearchUsersWithPerm(b, 10000, 100) } // ~0.12s/op
func BenchmarkSearchUsersWithPerm_10K_1K(b *testing.B) { benchSearchUsersWithPerm(b, 10000, 1000) } // ~0.12s/op
func BenchmarkSearchUsersWithPerm_10K_10K(b *testing.B) { benchSearchUsersWithPerm(b, 10000, 10000) } // ~0.17s/op
func BenchmarkSearchUsersWithPerm_20K_10(b *testing.B) { benchSearchUsersWithPerm(b, 20000, 10) } // ~0.22s/op
func BenchmarkSearchUsersWithPerm_20K_100(b *testing.B) { benchSearchUsersWithPerm(b, 20000, 100) } // ~0.22s/op
func BenchmarkSearchUsersWithPerm_20K_1K(b *testing.B) { benchSearchUsersWithPerm(b, 20000, 1000) } // ~0.25s/op
func BenchmarkSearchUsersWithPerm_20K_10K(b *testing.B) { benchSearchUsersWithPerm(b, 20000, 10000) } // ~s/op
func BenchmarkSearchUsersWithPerm_100K_10(b *testing.B) { benchSearchUsersWithPerm(b, 100000, 10) } // ~0.88s/op
func BenchmarkSearchUsersWithPerm_100K_100(b *testing.B) { benchSearchUsersWithPerm(b, 100000, 100) } // ~0.72s/op