grafana/pkg/services/sqlstore/permissions/dashboards_bench_test.go
Karl Persson 7386f8652c
RBAC: Improve performance of dashboard filter query (#56813)
* RBAC: Move UserRolesFilter to domain package

* Dashboard Permissions: Rewrite rbac filter to check access in sql

* RBAC: Add break when wildcard is found

* RBAC: Add tests for dashboard filter

* RBAC: Update tests

* RBAC: Cover more test cases

Co-authored-by: Ieva <ieva.vasiljeva@grafana.com>
2022-10-25 11:14:27 +02:00

144 lines
3.9 KiB
Go

package permissions_test
import (
"context"
"fmt"
"strconv"
"testing"
"time"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/sqlstore/permissions"
"github.com/grafana/grafana/pkg/services/user"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func benchmarkDashboardPermissionFilter(b *testing.B, numUsers, numDashboards int) {
store := setupBenchMark(b, numUsers, numDashboards)
b.ResetTimer()
for i := 0; i < b.N; i++ {
usr := &user.SignedInUser{UserID: 1, OrgID: 1, OrgRole: org.RoleViewer, Permissions: map[int64]map[string][]string{1: {}}}
filter := permissions.NewAccessControlDashboardPermissionFilter(usr, models.PERMISSION_VIEW, "")
var result int
err := store.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
q, params := filter.Where()
_, err := sess.SQL("SELECT COUNT(*) FROM dashboard WHERE "+q, params...).Get(&result)
return err
})
require.NoError(b, err)
assert.Equal(b, numDashboards, result)
}
}
func setupBenchMark(b *testing.B, numUsers, numDashboards int) db.DB {
store := db.InitTestDB(b)
now := time.Now()
err := store.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
dashes := make([]models.Dashboard, 0, numDashboards)
for i := 1; i <= numDashboards; i++ {
str := strconv.Itoa(i)
dashes = append(dashes, models.Dashboard{
OrgId: 1,
IsFolder: false,
Uid: str,
Slug: str,
Title: str,
Data: simplejson.New(),
Created: now,
Updated: now,
})
}
err := batch(len(dashes), 1000, func(start, end int) error {
_, err := sess.InsertMulti(dashes[start:end])
return err
})
require.NoError(b, err)
roles := make([]accesscontrol.Role, 0, numUsers)
assignments := make([]accesscontrol.UserRole, 0, numUsers)
permissions := make([]accesscontrol.Permission, 0, numUsers*numDashboards)
for i := 1; i <= numUsers; i++ {
name := fmt.Sprintf("managed_%d", i)
roles = append(roles, accesscontrol.Role{
UID: name,
Name: name,
Updated: now,
Created: now,
})
assignments = append(assignments, accesscontrol.UserRole{
RoleID: int64(i),
UserID: int64(i),
Created: now,
})
for _, dash := range dashes {
permissions = append(permissions, accesscontrol.Permission{
RoleID: int64(i),
Action: dashboards.ActionDashboardsRead,
Scope: dashboards.ScopeDashboardsProvider.GetResourceScopeUID(dash.Uid),
Updated: now,
Created: now,
})
}
}
err = batch(len(roles), 5000, func(start, end int) error {
_, err := sess.InsertMulti(roles[start:end])
return err
})
require.NoError(b, err)
err = batch(len(assignments), 5000, func(start, end int) error {
_, err := sess.InsertMulti(assignments[start:end])
return err
})
require.NoError(b, err)
err = batch(len(permissions), 5000, func(start, end int) error {
_, err := sess.InsertMulti(permissions[start:end])
return err
})
require.NoError(b, err)
return nil
})
require.NoError(b, err)
return store
}
func BenchmarkDashboardPermissionFilter_100_100(b *testing.B) {
benchmarkDashboardPermissionFilter(b, 100, 100)
}
func BenchmarkDashboardPermissionFilter_100_1000(b *testing.B) {
benchmarkDashboardPermissionFilter(b, 100, 1000)
}
func BenchmarkDashboardPermissionFilter_300_10000(b *testing.B) {
benchmarkDashboardPermissionFilter(b, 300, 10000)
}
func batch(count, batchSize int, eachFn func(start, end int) error) error {
for i := 0; i < count; {
end := i + batchSize
if end > count {
end = count
}
if err := eachFn(i, end); err != nil {
return err
}
i = end
}
return nil
}