AuthZ Service: Use singleflight group to fetch and build the folder tree (#98299)

* AuthZ Service: Use singleflight group to fetch and build the folder tree

* Change the sfgroup key

* Future proof
This commit is contained in:
Gabriel MABILLE 2024-12-20 10:26:30 +01:00 committed by GitHub
parent 24bf337c56
commit 55f8be62a1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 45 additions and 31 deletions

View File

@ -8,6 +8,7 @@ import (
authzv1 "github.com/grafana/authlib/authz/proto/v1"
"github.com/grafana/authlib/claims"
"golang.org/x/sync/singleflight"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"k8s.io/apiserver/pkg/endpoints/request"
@ -49,6 +50,9 @@ type Service struct {
teamCache *localcache.CacheService
basicRoleCache *localcache.CacheService
folderCache *localcache.CacheService
// Deduplication of concurrent requests
sf *singleflight.Group
}
func NewService(sql legacysql.LegacyDatabaseProvider, identityStore legacy.LegacyIdentityStore, logger log.Logger, tracer tracing.Tracer) *Service {
@ -63,6 +67,7 @@ func NewService(sql legacysql.LegacyDatabaseProvider, identityStore legacy.Legac
teamCache: localcache.New(shortCacheTTL, shortCleanupInterval),
basicRoleCache: localcache.New(longCacheTTL, longCleanupInterval),
folderCache: localcache.New(shortCacheTTL, shortCleanupInterval),
sf: new(singleflight.Group),
}
}
@ -339,38 +344,45 @@ func (s *Service) buildFolderTree(ctx context.Context, ns claims.NamespaceInfo)
return cached.(map[string]FolderNode), nil
}
folders, err := s.store.GetFolders(ctx, ns)
res, err, _ := s.sf.Do(ns.Value+"_buildFolderTree", func() (interface{}, error) {
folders, err := s.store.GetFolders(ctx, ns)
if err != nil {
return nil, fmt.Errorf("could not get folders: %w", err)
}
folderMap := make(map[string]FolderNode, len(folders))
for _, folder := range folders {
if node, has := folderMap[folder.UID]; !has {
folderMap[folder.UID] = FolderNode{
uid: folder.UID,
parentUID: folder.ParentUID,
}
} else {
node.parentUID = folder.ParentUID
folderMap[folder.UID] = node
}
// Register that the parent has this child node
if folder.ParentUID == nil {
continue
}
if parent, has := folderMap[*folder.ParentUID]; has {
parent.childrenUIDs = append(parent.childrenUIDs, folder.UID)
folderMap[*folder.ParentUID] = parent
} else {
folderMap[*folder.ParentUID] = FolderNode{
uid: *folder.ParentUID,
childrenUIDs: []string{folder.UID},
}
}
}
s.folderCache.Set(key, folderMap, 0)
return folderMap, nil
})
if err != nil {
return nil, fmt.Errorf("could not get folders: %w", err)
return nil, err
}
folderMap := make(map[string]FolderNode, len(folders))
for _, folder := range folders {
if node, has := folderMap[folder.UID]; !has {
folderMap[folder.UID] = FolderNode{
uid: folder.UID,
parentUID: folder.ParentUID,
}
} else {
node.parentUID = folder.ParentUID
folderMap[folder.UID] = node
}
// Register that the parent has this child node
if folder.ParentUID == nil {
continue
}
if parent, has := folderMap[*folder.ParentUID]; has {
parent.childrenUIDs = append(parent.childrenUIDs, folder.UID)
folderMap[*folder.ParentUID] = parent
} else {
folderMap[*folder.ParentUID] = FolderNode{
uid: *folder.ParentUID,
childrenUIDs: []string{folder.UID},
}
}
}
s.folderCache.Set(key, folderMap, 0)
return folderMap, nil
return res.(map[string]FolderNode), nil
}

View File

@ -8,6 +8,7 @@ import (
"github.com/grafana/authlib/claims"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/sync/singleflight"
"github.com/grafana/grafana/pkg/infra/localcache"
"github.com/grafana/grafana/pkg/infra/log"
@ -438,6 +439,7 @@ func TestService_buildFolderTree(t *testing.T) {
store: store,
folderCache: cacheService,
logger: log.New("test"),
sf: new(singleflight.Group),
}
tree, err := s.buildFolderTree(ctx, ns)