mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-07-19 14:32:36 +00:00
caching the call for GetNamespaceFilter
Introduces a concurrent-safe sync.Map cache to the backup Request struct to memoize GetNamespaceFilter results. This avoids re-evaluating glob patterns for every item, significantly improving backup performance while preserving the original exact-match precedence logic. Signed-off-by: Adam Zhang <adam.zhang@broadcom.com>
This commit is contained in:
@@ -0,0 +1 @@
|
||||
Fix issue #9907, add cache for the GetNamespaceFilter call
|
||||
@@ -6107,15 +6107,43 @@ func TestGetNamespaceFilter(t *testing.T) {
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// First call (populates cache)
|
||||
result := req.GetNamespaceFilter(tt.namespace)
|
||||
|
||||
if tt.expectNil {
|
||||
assert.Nil(t, result)
|
||||
|
||||
// Verify negative cache
|
||||
val, ok := req.NamespaceFilterCache.Load(tt.namespace)
|
||||
assert.True(t, ok)
|
||||
assert.Nil(t, val)
|
||||
} else {
|
||||
assert.NotNil(t, result)
|
||||
// Ensure the returned filter points to the correct reference in our map
|
||||
assert.Same(t, filterMap[tt.expectMatched], result)
|
||||
|
||||
// Verify positive cache
|
||||
val, ok := req.NamespaceFilterCache.Load(tt.namespace)
|
||||
assert.True(t, ok)
|
||||
assert.Same(t, filterMap[tt.expectMatched], val)
|
||||
}
|
||||
|
||||
// Second call (hits cache)
|
||||
result2 := req.GetNamespaceFilter(tt.namespace)
|
||||
assert.Same(t, result, result2)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetNamespaceFilter_CacheBypass(t *testing.T) {
|
||||
req := &Request{
|
||||
NamespacedFilterMap: make(map[string]*ResolvedNamespaceFilter),
|
||||
}
|
||||
|
||||
cachedFilter := &ResolvedNamespaceFilter{}
|
||||
req.NamespaceFilterCache.Store("cached-ns", cachedFilter)
|
||||
|
||||
// Since NamespacedFilterMap is empty, this would normally return nil,
|
||||
// but the cache should return our cachedFilter.
|
||||
assert.Same(t, cachedFilter, req.GetNamespaceFilter("cached-ns"))
|
||||
}
|
||||
|
||||
+23
-4
@@ -100,6 +100,10 @@ type Request struct {
|
||||
// NamespacedFilterPatterns preserves the order of patterns for first-match semantics
|
||||
// and caches pre-compiled globs to avoid repeated compilation in the hot path.
|
||||
NamespacedFilterPatterns []NamespacedFilterPattern
|
||||
|
||||
// NamespaceFilterCache memoizes the resolved filter for a given namespace.
|
||||
// sync.Map is used because item backuppers access this concurrently.
|
||||
NamespaceFilterCache sync.Map
|
||||
}
|
||||
|
||||
// NamespacedFilterPattern pairs a namespace pattern string with its pre-compiled
|
||||
@@ -149,22 +153,37 @@ func (r *Request) StopWorkerPool() {
|
||||
|
||||
// GetNamespaceFilter returns the resolved filter for a namespace, or nil
|
||||
// if the namespace should use global filters. Uses first-match semantics
|
||||
// when multiple patterns could match the same namespace.
|
||||
// when multiple patterns could match the same namespace, but exact matches
|
||||
// always take precedence over glob patterns regardless of definition order.
|
||||
func (r *Request) GetNamespaceFilter(namespace string) *ResolvedNamespaceFilter {
|
||||
if r.NamespacedFilterMap == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// First check for exact match
|
||||
// 1. Check the concurrent cache first
|
||||
if val, ok := r.NamespaceFilterCache.Load(namespace); ok {
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
return val.(*ResolvedNamespaceFilter)
|
||||
}
|
||||
|
||||
// 2. Check for exact match first
|
||||
if f, ok := r.NamespacedFilterMap[namespace]; ok {
|
||||
r.NamespaceFilterCache.Store(namespace, f)
|
||||
return f
|
||||
}
|
||||
|
||||
// Walk patterns in definition order using pre-compiled globs (no allocation per call)
|
||||
// 3. Walk patterns in definition order using pre-compiled globs
|
||||
for _, p := range r.NamespacedFilterPatterns {
|
||||
if p.Compiled != nil && p.Compiled.Match(namespace) {
|
||||
return r.NamespacedFilterMap[p.Pattern]
|
||||
filter := r.NamespacedFilterMap[p.Pattern]
|
||||
r.NamespaceFilterCache.Store(namespace, filter)
|
||||
return filter
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Cache the miss
|
||||
r.NamespaceFilterCache.Store(namespace, nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user