feat: parallel processChange (#112198)

* feat: parallel processChange

* all except move work

* fix: tests and order of operations

* fix: tests

* chore: review feedback

* chore: review feedback
This commit is contained in:
Costa Alexoglou
2025-10-16 19:17:04 +02:00
committed by GitHub
parent aa8af6b798
commit 163a88056e
11 changed files with 381 additions and 126 deletions
@@ -3,6 +3,7 @@ package resources
import (
"context"
"fmt"
"sync"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -30,11 +31,18 @@ type folderTree struct {
tree map[string]string
folders map[string]Folder
count int
mu sync.RWMutex
}
// In determines if the given folder is in the tree at all. That is, it answers "does the folder even exist in the Grafana instance?"
// An empty folder string means the root folder, and is special-cased to always return true.
func (t *folderTree) In(folder string) bool {
t.mu.RLock()
defer t.mu.RUnlock()
return t.in(folder)
}
func (t *folderTree) in(folder string) bool {
_, ok := t.tree[folder]
return ok || folder == ""
}
@@ -47,7 +55,19 @@ func (t *folderTree) In(folder string) bool {
// If In(folder) or In(baseFolder) is false, this will return ok=false, because it would be undefined behaviour.
// If baseFolder is not a parent of folder, ok=false is returned.
func (t *folderTree) DirPath(folder, baseFolder string) (fid Folder, ok bool) {
if !t.In(folder) || !t.In(baseFolder) {
t.mu.RLock()
defer t.mu.RUnlock()
return t.dirPath(folder, baseFolder)
}
// dirPath is the internal implementation that assumes the mutex is already held
// Needed to avoid deadlock when called from other methods that hold locks like Walk()
func (t *folderTree) dirPath(folder, baseFolder string) (fid Folder, ok bool) {
// Inline In() logic to avoid deadlock when called from other methods that hold locks
folderInTree := t.in(folder)
baseFolderInTree := t.in(baseFolder)
if !folderInTree || !baseFolderInTree {
return Folder{}, false
}
if folder == "" && baseFolder != "" {
@@ -76,21 +96,27 @@ func (t *folderTree) DirPath(folder, baseFolder string) (fid Folder, ok bool) {
}
func (t *folderTree) Add(folder Folder, parent string) {
t.mu.Lock()
defer t.mu.Unlock()
t.tree[folder.ID] = parent
t.folders[folder.ID] = folder
t.count++
}
func (t *folderTree) Count() int {
t.mu.RLock()
defer t.mu.RUnlock()
return t.count
}
type WalkFunc func(ctx context.Context, folder Folder, parent string) error
func (t *folderTree) Walk(ctx context.Context, fn WalkFunc) error {
t.mu.RLock()
defer t.mu.RUnlock()
toWalk := make([]Folder, 0, len(t.folders))
for _, folder := range t.folders {
folder, _ := t.DirPath(folder.ID, "")
folder, _ := t.dirPath(folder.ID, "")
toWalk = append(toWalk, folder)
}
@@ -123,6 +149,8 @@ func (t *folderTree) AddUnstructured(item *unstructured.Unstructured) error {
Title: meta.FindTitle(item.GetName()),
ID: item.GetName(),
}
t.mu.Lock()
defer t.mu.Unlock()
t.tree[folder.ID] = meta.GetFolder()
t.folders[folder.ID] = folder
t.count++