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/sqlstore/permissions/dashboards_bench_test.go

290 lines
9.0 KiB

package permissions_test
import (
"context"
"fmt"
"strconv"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/accesscontrol/mock"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/dashboards/dashboardaccess"
"github.com/grafana/grafana/pkg/services/dashboards/database"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/folder"
"github.com/grafana/grafana/pkg/services/folder/folderimpl"
"github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/search/sort"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/sqlstore/permissions"
"github.com/grafana/grafana/pkg/services/supportbundles/supportbundlestest"
"github.com/grafana/grafana/pkg/services/tag/tagimpl"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/storage/legacysql/dualwrite"
)
func benchmarkDashboardPermissionFilter(b *testing.B, numUsers, numDashboards, numFolders, nestingLevel int) {
usr := user.SignedInUser{UserID: 1, OrgID: 1, OrgRole: org.RoleViewer, Permissions: map[int64]map[string][]string{
1: accesscontrol.GroupScopesByActionContext(context.Background(), []accesscontrol.Permission{
{
Action: dashboards.ActionFoldersCreate,
Scope: dashboards.ScopeFoldersAll,
},
}),
}}
features := featuremgmt.WithFeatures()
// if nestingLevel > 0 enable nested folders
if nestingLevel > 0 {
features = featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders)
}
store := setupBenchMark(b, usr, features, numUsers, numDashboards, numFolders, nestingLevel)
recursiveQueriesAreSupported, err := store.RecursiveQueriesAreSupported()
require.NoError(b, err)
b.ResetTimer()
for i := 0; i < b.N; i++ {
filter := permissions.NewAccessControlDashboardPermissionFilter(&usr, dashboardaccess.PERMISSION_VIEW, "", features, recursiveQueriesAreSupported, store.GetDialect())
var result int
err := store.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
q, params := filter.Where()
recQry, recQryParams := filter.With()
params = append(recQryParams, params...)
_, err := sess.SQL(recQry+"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, usr user.SignedInUser, features featuremgmt.FeatureToggles, numUsers, numDashboards, numFolders, nestingLevel int) db.DB {
if nestingLevel > folder.MaxNestedFolderDepth {
nestingLevel = folder.MaxNestedFolderDepth
}
store, cfg := db.InitTestDBWithCfg(b)
dashboardWriteStore, err := database.ProvideDashboardStore(store, cfg, features, tagimpl.ProvideService(store))
require.NoError(b, err)
fStore := folderimpl.ProvideStore(store)
folderSvc := folderimpl.ProvideService(
fStore, mock.New(), bus.ProvideBus(tracing.InitializeTracerForTest()), dashboardWriteStore, folderimpl.ProvideDashboardFolderStore(store),
nil, store, features, supportbundlestest.NewFakeBundleService(), nil, cfg, nil, tracing.InitializeTracerForTest(), nil, dualwrite.ProvideTestService(), sort.ProvideService())
origNewGuardian := guardian.New
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanViewValue: true, CanSaveValue: true})
b.Cleanup(func() {
guardian.New = origNewGuardian
})
rootFolders := make([]*folder.Folder, 0, numFolders)
dashes := make([]dashboards.Dashboard, 0, numDashboards)
parentUID := ""
for i := 0; i < numFolders; i++ {
uid := fmt.Sprintf("f%d", i)
f, err := folderSvc.Create(context.Background(), &folder.CreateFolderCommand{
UID: uid,
OrgID: usr.OrgID,
Title: uid,
SignedInUser: &usr,
ParentUID: parentUID,
})
require.NoError(b, err)
rootFolders = append(rootFolders, f)
parentUID := f.UID
var leaf *folder.Folder
for j := 1; j <= nestingLevel; j++ {
uid := fmt.Sprintf("f%d_%d", i, j)
sf, err := folderSvc.Create(context.Background(), &folder.CreateFolderCommand{
UID: uid,
OrgID: usr.OrgID,
Title: uid,
SignedInUser: &usr,
ParentUID: parentUID,
})
require.NoError(b, err)
parentUID = sf.UID
leaf = sf
}
str := fmt.Sprintf("dashboard under folder %s", leaf.Title)
now := time.Now()
dashes = append(dashes, dashboards.Dashboard{
OrgID: usr.OrgID,
IsFolder: false,
UID: str,
Slug: str,
Title: str,
Data: simplejson.New(),
Created: now,
Updated: now,
FolderUID: leaf.UID,
})
}
err = store.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
now := time.Now()
for i := len(dashes); i < numDashboards; i++ {
str := strconv.Itoa(i)
dashes = append(dashes, dashboards.Dashboard{
OrgID: usr.OrgID,
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 {
// add permission to read dashboards under the general
if dash.FolderUID == "" {
permissions = append(permissions, accesscontrol.Permission{
RoleID: int64(i),
Action: dashboards.ActionDashboardsRead,
Scope: dashboards.ScopeDashboardsProvider.GetResourceScopeUID(dash.UID),
Updated: now,
Created: now,
})
}
}
for _, f := range rootFolders {
// add permission to read folders under specific folders
permissions = append(permissions, accesscontrol.Permission{
RoleID: int64(i),
Action: dashboards.ActionDashboardsRead,
Scope: dashboards.ScopeFoldersProvider.GetResourceScopeUID(f.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_0_0(b *testing.B) {
benchmarkDashboardPermissionFilter(b, 100, 100, 0, 0)
}
func BenchmarkDashboardPermissionFilter_100_100_10_2(b *testing.B) {
benchmarkDashboardPermissionFilter(b, 100, 100, 10, 2)
}
func BenchmarkDashboardPermissionFilter_100_100_10_4(b *testing.B) {
benchmarkDashboardPermissionFilter(b, 100, 100, 10, 4)
}
func BenchmarkDashboardPermissionFilter_100_100_10_8(b *testing.B) {
benchmarkDashboardPermissionFilter(b, 100, 100, 10, 8)
}
func BenchmarkDashboardPermissionFilter_100_1000_0_0(b *testing.B) {
benchmarkDashboardPermissionFilter(b, 100, 1000, 0, 0)
}
func BenchmarkDashboardPermissionFilter_100_1000_10_2(b *testing.B) {
benchmarkDashboardPermissionFilter(b, 100, 1000, 10, 2)
}
func BenchmarkDashboardPermissionFilter_100_1000_10_4(b *testing.B) {
benchmarkDashboardPermissionFilter(b, 100, 1000, 10, 4)
}
func BenchmarkDashboardPermissionFilter_100_1000_10_8(b *testing.B) {
benchmarkDashboardPermissionFilter(b, 100, 1000, 10, 8)
}
func BenchmarkDashboardPermissionFilter_300_10000_0_0(b *testing.B) {
benchmarkDashboardPermissionFilter(b, 300, 10000, 0, 0)
}
func BenchmarkDashboardPermissionFilter_300_10000_10_2(b *testing.B) {
benchmarkDashboardPermissionFilter(b, 300, 10000, 10, 2)
}
func BenchmarkDashboardPermissionFilter_300_10000_10_4(b *testing.B) {
benchmarkDashboardPermissionFilter(b, 300, 10000, 10, 4)
}
func BenchmarkDashboardPermissionFilter_300_10000_10_8(b *testing.B) {
benchmarkDashboardPermissionFilter(b, 300, 10000, 10, 8)
}
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
}