mirror of
https://github.com/open-policy-agent/opa.git
synced 2026-08-28 03:05:04 -06:00
bundle: make merge.InterfacesMaps mutable to improve loading performance (#3882)
Fixes #3860. Signed-off-by: Arvid Prozesky <arvid.prozesky@sap.com>
This commit is contained in:
+36
-12
@@ -8,33 +8,57 @@ package merge
|
||||
|
||||
// InterfaceMaps returns the result of merging a and b. If a and b cannot be
|
||||
// merged because of conflicting key-value pairs, ok is false.
|
||||
func InterfaceMaps(a map[string]interface{}, b map[string]interface{}) (c map[string]interface{}, ok bool) {
|
||||
func InterfaceMaps(a map[string]interface{}, b map[string]interface{}) (map[string]interface{}, bool) {
|
||||
|
||||
c = map[string]interface{}{}
|
||||
for k := range a {
|
||||
c[k] = a[k]
|
||||
if a == nil {
|
||||
return b, true
|
||||
}
|
||||
|
||||
if hasConflicts(a, b) {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return merge(a, b), true
|
||||
}
|
||||
|
||||
func merge(a, b map[string]interface{}) map[string]interface{} {
|
||||
|
||||
for k := range b {
|
||||
|
||||
add := b[k]
|
||||
exist, ok := c[k]
|
||||
exist, ok := a[k]
|
||||
if !ok {
|
||||
a[k] = add
|
||||
continue
|
||||
}
|
||||
|
||||
existObj := exist.(map[string]interface{})
|
||||
addObj := add.(map[string]interface{})
|
||||
|
||||
a[k] = merge(existObj, addObj)
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
func hasConflicts(a, b map[string]interface{}) bool {
|
||||
for k := range b {
|
||||
|
||||
add := b[k]
|
||||
exist, ok := a[k]
|
||||
if !ok {
|
||||
c[k] = add
|
||||
continue
|
||||
}
|
||||
|
||||
existObj, existOk := exist.(map[string]interface{})
|
||||
addObj, addOk := add.(map[string]interface{})
|
||||
if !existOk || !addOk {
|
||||
return nil, false
|
||||
return true
|
||||
}
|
||||
|
||||
c[k], ok = InterfaceMaps(existObj, addObj)
|
||||
if !ok {
|
||||
return nil, false
|
||||
if hasConflicts(existObj, addObj) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return c, true
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -5,12 +5,51 @@
|
||||
package merge
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/open-policy-agent/opa/util"
|
||||
)
|
||||
|
||||
func BenchmarkInterfaceMaps(b *testing.B) {
|
||||
|
||||
for _, size := range []int{100, 1000, 10000} {
|
||||
|
||||
b.Run(fmt.Sprintf("store size %v", size), func(b *testing.B) {
|
||||
aRaw := "{"
|
||||
for i := 0; i < size; i++ {
|
||||
if i != 0 {
|
||||
aRaw += ","
|
||||
}
|
||||
aRaw += fmt.Sprintf(`"%v":{"a":{"b":"c","d":"e"}}`, i)
|
||||
}
|
||||
aRaw += "}"
|
||||
|
||||
aVals := map[string]interface{}{}
|
||||
if err := util.UnmarshalJSON([]byte(aRaw), &aVals); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for i := 0; i < b.N; i++ {
|
||||
bRaw := fmt.Sprintf(`{"a%v":{"b":"c","d":"e"}}`, i)
|
||||
bVals := map[string]interface{}{}
|
||||
if err := util.UnmarshalJSON([]byte(bRaw), &bVals); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
b.StartTimer()
|
||||
_, ok := InterfaceMaps(aVals, bVals)
|
||||
b.StopTimer()
|
||||
|
||||
if !ok {
|
||||
b.Fatal("merging interfaces failed")
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestMergeDocs(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
@@ -30,6 +69,10 @@ func TestMergeDocs(t *testing.T) {
|
||||
if err := util.UnmarshalJSON([]byte(tc.a), &a); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
aInitial := map[string]interface{}{}
|
||||
if err := util.UnmarshalJSON([]byte(tc.a), &aInitial); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
b := map[string]interface{}{}
|
||||
if err := util.UnmarshalJSON([]byte(tc.b), &b); err != nil {
|
||||
@@ -43,6 +86,10 @@ func TestMergeDocs(t *testing.T) {
|
||||
t.Errorf("Expected merge(%v,%v) == false but got: %v", a, b, c)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(a, aInitial) {
|
||||
t.Errorf("Expected conflicting merge to not mutate a (%v) but got a: %v", aInitial, a)
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
expected := map[string]interface{}{}
|
||||
@@ -54,6 +101,11 @@ func TestMergeDocs(t *testing.T) {
|
||||
if !ok || !reflect.DeepEqual(c, expected) {
|
||||
t.Errorf("Expected merge(%v, %v) == %v but got: %v (ok: %v)", a, b, expected, c, ok)
|
||||
}
|
||||
|
||||
if reflect.DeepEqual(a, aInitial) || !reflect.DeepEqual(a, c) {
|
||||
t.Errorf("Expected merge to mutate a (%v) but got %v", aInitial, a)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user