Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sai/merged sets #535

Closed
wants to merge 20 commits into from
408 changes: 406 additions & 2 deletions codegen/test/api/things.test.io/v1/sets/sets.go

Large diffs are not rendered by default.

209 changes: 208 additions & 1 deletion contrib/codegen/templates/sets/sets.gotmpl
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,214 @@ func (s *{{ $kindLowerCamel }}Set) Clone() {{ $resource.Kind }}Set {
if s == nil {
return nil
}
return &{{ $kindLowerCamel }}Set{set: sksets.NewResourceSet(s.Generic().Clone().List()...)}
return &{{ $kindLowerCamel }}MergedSet{sets: []sksets.ResourceSet{s.Generic()}}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the main effective change, clone now produces a merged set, which is a a list of sets which is iterated through on methods like Find or List

}

type {{ $kindLowerCamel }}MergedSet struct {
sets []sksets.ResourceSet
}

func New{{ $resource.Kind }}MergedSet({{ $kindLowerCamel }}List ...*{{ $import_prefix }}.{{ $resource.Kind }}) {{ $resource.Kind }}Set {
return &{{ $kindLowerCamel }}MergedSet{sets: []sksets.ResourceSet{makeGeneric{{ $resource.Kind }}Set({{ $kindLowerCamel }}List)}}
}

func New{{ $resource.Kind }}MergedSetFromList({{ $kindLowerCamel }}List *{{ $import_prefix }}.{{ $resource.Kind }}List) {{ $resource.Kind }}Set {
list := make([]*{{ $import_prefix }}.{{ $resource.Kind }}, 0, len({{ $kindLowerCamel }}List.Items))
for idx := range {{ $kindLowerCamel }}List.Items {
{{- if $resource.PointerSlices }}
list = append(list, {{ $kindLowerCamel }}List.Items[idx])
{{- else }}
list = append(list, &{{ $kindLowerCamel }}List.Items[idx])
{{- end }}
}
return &{{ $kindLowerCamel }}MergedSet{sets: []sksets.ResourceSet{makeGeneric{{ $resource.Kind }}Set(list)}}
}

func (s *{{ $kindLowerCamel }}MergedSet) Keys() sets.String {
if s == nil {
return sets.String{}
}
toRet := sets.String{}
for _ , set := range s.sets {
toRet = toRet.Union(set.Keys())
}
return toRet
}

func (s *{{ $kindLowerCamel }}MergedSet) List(filterResource ... func(*{{ $import_prefix }}.{{ $resource.Kind }}) bool) []*{{ $import_prefix }}.{{ $resource.Kind }} {
if s == nil {
return nil
}
var genericFilters []func(ezkube.ResourceId) bool
for _, filter := range filterResource {
filter := filter
genericFilters = append(genericFilters, func(obj ezkube.ResourceId) bool {
return filter(obj.(*{{ $import_prefix }}.{{ $resource.Kind }}))
})
}
{{ $kindLowerCamel }}List := []*{{ $import_prefix }}.{{ $resource.Kind }}{}
tracker := map[ezkube.ResourceId]bool{}
for i := len(s.sets) - 1; i >= 0; i-- {
set := s.sets[i]
for _, obj := range set.List(genericFilters...) {
if tracker[obj] {
continue
}
tracker[obj] = true
{{ $kindLowerCamel }}List = append({{ $kindLowerCamel }}List, obj.(*{{ $import_prefix }}.{{ $resource.Kind }}))
}
}
return {{ $kindLowerCamel }}List
}

func (s *{{ $kindLowerCamel }}MergedSet) UnsortedList(filterResource ... func(*{{ $import_prefix }}.{{ $resource.Kind }}) bool) []*{{ $import_prefix }}.{{ $resource.Kind }} {
if s == nil {
return nil
}
var genericFilters []func(ezkube.ResourceId) bool
for _, filter := range filterResource {
filter := filter
genericFilters = append(genericFilters, func(obj ezkube.ResourceId) bool {
return filter(obj.(*{{ $import_prefix }}.{{ $resource.Kind }}))
})
}
{{ $kindLowerCamel }}List := []*{{ $import_prefix }}.{{ $resource.Kind }}{}
tracker := map[ezkube.ResourceId]bool{}
for i := len(s.sets) - 1; i >= 0; i-- {
set := s.sets[i]
for _, obj := range set.UnsortedList(genericFilters...) {
if tracker[obj] {
continue
}
tracker[obj] = true
{{ $kindLowerCamel }}List = append({{ $kindLowerCamel }}List, obj.(*{{ $import_prefix }}.{{ $resource.Kind }}))
}
}
return {{ $kindLowerCamel }}List
}

func (s *{{ $kindLowerCamel }}MergedSet) Map() map[string]*{{ $import_prefix }}.{{ $resource.Kind }} {
if s == nil {
return nil
}

newMap := map[string]*{{ $import_prefix }}.{{ $resource.Kind }}{}
for _, set := range s.sets {
for k, v := range set.Map() {
newMap[k] = v.(*{{ $import_prefix }}.{{ $resource.Kind }})
}
}
return newMap
}

func (s *{{ $kindLowerCamel }}MergedSet) Insert(
{{ $kindLowerCamel }}List ...*{{ $import_prefix }}.{{ $resource.Kind }},
) {
if s == nil {
panic("cannot insert into nil set")
}
if len(s.sets) == 0 {
s.sets = append(s.sets, makeGeneric{{ $resource.Kind }}Set({{ $kindLowerCamel }}List))
}
for _, obj := range {{ $kindLowerCamel }}List {
inserted := false
for _, set := range s.sets {
if set.Has(obj) {
set.Insert(obj)
inserted = true
break
}
}
if !inserted {
s.sets[0].Insert(obj)
}
}
}

func (s *{{ $kindLowerCamel }}MergedSet) Has({{ $kindLowerCamel }} ezkube.ResourceId) bool {
if s == nil {
return false
}
for _, set := range s.sets {
if set.Has({{ $kindLowerCamel }}) {
return true
}
}
return false
}

func (s *{{ $kindLowerCamel }}MergedSet) Equal(
{{ $kindLowerCamel }}Set {{ $resource.Kind }}Set,
) bool {
panic("unimplemented")
}

func (s *{{ $kindLowerCamel }}MergedSet) Delete({{ $resource.Kind }} ezkube.ResourceId) {
for _, set := range s.sets {
set.Delete({{ $resource.Kind }})
}
}

func (s *{{ $kindLowerCamel }}MergedSet) Union(set {{ $resource.Kind }}Set) {{ $resource.Kind }}Set {
if s == nil {
return set
}
return &{{ $kindLowerCamel }}MergedSet{sets: append(s.sets, set.Generic())}
}

func (s *{{ $kindLowerCamel }}MergedSet) Difference(set {{ $resource.Kind }}Set) {{ $resource.Kind }}Set {
panic("unimplemented")
}

func (s *{{ $kindLowerCamel }}MergedSet) Intersection(set {{ $resource.Kind }}Set) {{ $resource.Kind }}Set {
panic("unimplemented")
}

func (s *{{ $kindLowerCamel }}MergedSet) Find(id ezkube.ResourceId) (*{{ $import_prefix }}.{{ $resource.Kind }}, error) {
if s == nil {
return nil, eris.Errorf("empty set, cannot find {{ $resource.Kind }} %v", sksets.Key(id))
}

var err error
for _, set := range s.sets {
var obj ezkube.ResourceId
obj, err = set.Find(&{{ $import_prefix }}.{{ $resource.Kind }}{}, id)
if err == nil {
return obj.(*{{ $import_prefix }}.{{ $resource.Kind }}), nil
}
}

return nil, err
}

func (s *{{ $kindLowerCamel }}MergedSet) Length() int {
if s == nil {
return 0
}
totalLen := 0
for _, set := range s.sets {
totalLen += set.Length()
}
return totalLen
}

func (s *{{ $kindLowerCamel }}MergedSet) Generic() sksets.ResourceSet {
res := make([]ezkube.ResourceId, s.Length())
for _, thing := range s.List() {
res = append(res, thing)
}
return sksets.NewResourceSet(res...)
}

func (s *{{ $kindLowerCamel }}MergedSet) Delta(newSet {{ $resource.Kind }}Set) sksets.ResourceDelta {
panic("unimplemented")
}

func (s *{{ $kindLowerCamel }}MergedSet) Clone() {{ $resource.Kind }}Set {
if s == nil {
return nil
}
return &{{ $kindLowerCamel }}MergedSet{sets: s.sets[:]}
}

{{- end }}
1 change: 1 addition & 0 deletions contrib/pkg/sets/sets.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
Delta(newSet ResourceSet) ResourceDelta
// Clone returns a deep copy of the set
Clone() ResourceSet
Merge(otherSet ResourceSet) ResourceSet
}

// ResourceDelta represents the set of changes between two ResourceSets.
Expand All @@ -60,7 +61,7 @@
}

func NewResourceSet(resources ...ezkube.ResourceId) ResourceSet {
return &threadSafeResourceSet{set: newResources(resources...)}

Check failure on line 64 in contrib/pkg/sets/sets.go

View workflow job for this annotation

GitHub Actions / Tests

cannot use &threadSafeResourceSet{…} (value of type *threadSafeResourceSet) as ResourceSet value in return statement: *threadSafeResourceSet does not implement ResourceSet (missing method Merge)
}

func (t *threadSafeResourceSet) Keys() sets.String {
Expand Down Expand Up @@ -132,19 +133,19 @@
func (t *threadSafeResourceSet) Union(set ResourceSet) ResourceSet {
t.lock.Lock()
defer t.lock.Unlock()
return &threadSafeResourceSet{set: t.set.Union(set.Map())}

Check failure on line 136 in contrib/pkg/sets/sets.go

View workflow job for this annotation

GitHub Actions / Tests

cannot use &threadSafeResourceSet{…} (value of type *threadSafeResourceSet) as ResourceSet value in return statement: *threadSafeResourceSet does not implement ResourceSet (missing method Merge)
}

func (t *threadSafeResourceSet) Difference(set ResourceSet) ResourceSet {
t.lock.RLock()
defer t.lock.RUnlock()
return &threadSafeResourceSet{set: t.set.Difference(set.Map())}

Check failure on line 142 in contrib/pkg/sets/sets.go

View workflow job for this annotation

GitHub Actions / Tests

cannot use &threadSafeResourceSet{…} (value of type *threadSafeResourceSet) as ResourceSet value in return statement: *threadSafeResourceSet does not implement ResourceSet (missing method Merge)
}

func (t *threadSafeResourceSet) Intersection(set ResourceSet) ResourceSet {
t.lock.RLock()
defer t.lock.RUnlock()
return &threadSafeResourceSet{set: t.set.Intersection(set.Map())}

Check failure on line 148 in contrib/pkg/sets/sets.go

View workflow job for this annotation

GitHub Actions / Tests

cannot use &threadSafeResourceSet{…} (value of type *threadSafeResourceSet) as ResourceSet value in return statement: *threadSafeResourceSet does not implement ResourceSet (missing method Merge)
}

func (t *threadSafeResourceSet) Find(
Expand Down
122 changes: 122 additions & 0 deletions contrib/pkg/sets/sets_benchmark_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package sets

import (
"fmt"
"testing"

v1 "github.com/solo-io/skv2/pkg/api/core.skv2.solo.io/v1"
"github.com/solo-io/skv2/pkg/ezkube"
)

// Define a global variable to prevent compiler optimizations
var result interface{}

var scale = 10000

// func Benchmark(b *testing.B) {
// b.Run("Resources_Insert", BenchmarkResources_Insert)
// b.Run("Resources_Find", BenchmarkResources_Find)
// // b.Run("Resources_Delete", BenchmarkResources_Delete)
// b.Run("Resources_List", BenchmarkResources_List)
// b.Run("Resources_UnsortedList", BenchmarkResources_UnsortedList)
// }

func BenchmarkResources_Insert(b *testing.B) {
var r Resources
for i := 0; i < b.N; i++ {
r = newResources() // Assume newResources is corrected to initialize correctly
for j := 0; j < scale; j++ {
resource := &v1.ObjectRef{Namespace: "namespace", Name: "name" + fmt.Sprint(j)}
r.Insert(resource)
}
}
// Store the result to a package level variable
// so the compiler cannot eliminate the Benchmark itself.
result = r
}

func BenchmarkResources_Find(b *testing.B) {
r := newResources()
for j := 0; j < scale; j++ {
resource := &v1.ObjectRef{Namespace: "namespace", Name: "name" + fmt.Sprint(j)}
r.Insert(resource)
}

b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = r.Find(&v1.TypedObjectRef{}, &v1.ObjectRef{Namespace: "namespace", Name: "name500"})
}
}

func BenchmarkResources_Delete(b *testing.B) {
var r Resources
for i := 0; i < b.N; i++ {
r = newResources()
for j := 0; j < scale; j++ {
resource := &v1.ObjectRef{Namespace: "namespace", Name: "name" + fmt.Sprint(j)}
r.Insert(resource)
}
b.ResetTimer()
for j := 0; j < scale; j++ {
r.Delete(&v1.ObjectRef{Namespace: "namespace", Name: "name" + fmt.Sprint(j)})
}
b.StopTimer()
}
result = r
}

func BenchmarkResources_List(b *testing.B) {
r := newResources()
for j := 0; j < scale; j++ {
resource := &v1.ObjectRef{Namespace: "namespace", Name: "name" + fmt.Sprint(j)}
r.Insert(resource)
}

b.ResetTimer()
var res []ezkube.ResourceId
for i := 0; i < b.N; i++ {
res = r.List()
}
// Store the result to prevent the compiler from optimizing the loop away.
result = res
}

func BenchmarkResources_UnsortedList(b *testing.B) {
r := newResources()
for j := 0; j < scale; j++ {
resource := &v1.ObjectRef{Namespace: "namespace", Name: "name" + fmt.Sprint(j)}
r.Insert(resource)
}

b.ResetTimer()
var res []ezkube.ResourceId
for i := 0; i < b.N; i++ {
res = r.UnsortedList()
}
// Store the result to prevent the compiler from optimizing the loop away.
result = res
}

// func BenchmarkResources_Delta(b *testing.B) {
// // Define the scale for the number of resources in each set.
// const scale = 1000

// // Create and populate the original set with resources.
// originalSet := newResources()
// for i := 0; i < scale; i++ {
// resource := &v1.ObjectRef{Namespace: "namespace", Name: "originalName" + fmt.Sprint(i)}
// originalSet.Insert(resource)
// }

// // Create and populate a new set with some overlapping and some unique resources.
// newSet := newResources()
// for i := scale / 2; i < scale+(scale/2); i++ {
// resource := &v1.ObjectRef{Namespace: "namespace", Name: "newName" + fmt.Sprint(i)}
// newSet.Insert(resource)
// }

// b.ResetTimer() // Start timing the benchmark loop.
// for i := 0; i < b.N; i++ {
// _ = originalSet.Delta(newSet)
// }
// }
Loading
Loading