diff --git a/report/counters.go b/report/counters.go index 9993e11dc..ac454c57c 100644 --- a/report/counters.go +++ b/report/counters.go @@ -48,19 +48,35 @@ func (c Counters) Lookup(key string) (int, bool) { return 0, false } +// Size returns the number of counters +func (c Counters) Size() int { + return c.psMap.Size() +} + // Merge produces a fresh Counters, container the keys from both inputs. When // both inputs container the same key, the latter value is used. func (c Counters) Merge(other Counters) Counters { - output := c.psMap - - other.psMap.ForEach(func(key string, otherVal interface{}) { + var ( + cSize = c.Size() + otherSize = other.Size() + output = c.psMap + iter = other.psMap + ) + switch { + case cSize == 0: + return other + case otherSize == 0: + return c + case cSize < otherSize: + output, iter = iter, output + } + iter.ForEach(func(key string, otherVal interface{}) { if val, ok := output.Lookup(key); ok { output = output.Set(key, otherVal.(int)+val.(int)) } else { output = output.Set(key, otherVal) } }) - return Counters{output} } diff --git a/report/edge_metadatas.go b/report/edge_metadatas.go index 52c34b06b..4d3edbaf8 100644 --- a/report/edge_metadatas.go +++ b/report/edge_metadatas.go @@ -49,19 +49,35 @@ func (c EdgeMetadatas) Lookup(key string) (EdgeMetadata, bool) { return EdgeMetadata{}, false } +// Size is the number of elements +func (c EdgeMetadatas) Size() int { + return c.psMap.Size() +} + // Merge produces a fresh Counters, container the keys from both inputs. When // both inputs container the same key, the latter value is used. func (c EdgeMetadatas) Merge(other EdgeMetadatas) EdgeMetadatas { - output := c.psMap - - other.psMap.ForEach(func(key string, otherVal interface{}) { + var ( + cSize = c.Size() + otherSize = other.Size() + output = c.psMap + iter = other.psMap + ) + switch { + case cSize == 0: + return other + case otherSize == 0: + return c + case cSize < otherSize: + output, iter = iter, output + } + iter.ForEach(func(key string, otherVal interface{}) { if val, ok := output.Lookup(key); ok { output = output.Set(key, otherVal.(EdgeMetadata).Merge(val.(EdgeMetadata))) } else { output = output.Set(key, otherVal) } }) - return EdgeMetadatas{output} } diff --git a/report/latest_map.go b/report/latest_map.go index 226438ab8..dd758336f 100644 --- a/report/latest_map.go +++ b/report/latest_map.go @@ -45,21 +45,36 @@ func (m LatestMap) Copy() LatestMap { return m } +// Size returns the number of elements +func (m LatestMap) Size() int { + return m.Map.Size() +} + // Merge produces a fresh LatestMap, container the kers from both inputs. When // both inputs container the same key, the latter value is used. -func (m LatestMap) Merge(newer LatestMap) LatestMap { - // expect people to do old.Merge(new), optimise for that. - // ie if you do {k: v}.Merge({k: v'}), we end up just returning - // newer, unmodified. - output := newer.Map +func (m LatestMap) Merge(other LatestMap) LatestMap { + var ( + mSize = m.Size() + otherSize = other.Size() + output = m.Map + iter = other.Map + ) + switch { + case mSize == 0: + return other + case otherSize == 0: + return m + case mSize < otherSize: + output, iter = iter, output + } - m.Map.ForEach(func(key string, olderVal interface{}) { - if newerVal, ok := newer.Map.Lookup(key); ok { - if newerVal.(LatestEntry).Timestamp.Before(olderVal.(LatestEntry).Timestamp) { - output = output.Set(key, olderVal) + iter.ForEach(func(key string, iterVal interface{}) { + if existingVal, ok := output.Lookup(key); ok { + if existingVal.(LatestEntry).Timestamp.Before(iterVal.(LatestEntry).Timestamp) { + output = output.Set(key, iterVal) } } else { - output = output.Set(key, olderVal) + output = output.Set(key, iterVal) } }) diff --git a/report/node_test.go b/report/node_test.go index fd186f392..70586aa41 100644 --- a/report/node_test.go +++ b/report/node_test.go @@ -95,10 +95,9 @@ func TestMergeNodes(t *testing.T) { }, b: report.Nodes{ ":192.168.1.1:12345": report.MakeNodeWith(map[string]string{ // <-- same ID - PID: "0", Name: "curl", Domain: "node-a.local", - }), + }).WithLatest(PID, time.Now().Add(-1*time.Minute), "0"), }, want: report.Nodes{ ":192.168.1.1:12345": report.MakeNodeWith(map[string]string{ @@ -118,10 +117,9 @@ func TestMergeNodes(t *testing.T) { }, b: report.Nodes{ ":192.168.1.1:12345": report.MakeNodeWith(map[string]string{ // <-- same ID - PID: "0", Name: "curl", Domain: "node-a.local", - }), + }).WithLatest(PID, time.Now().Add(-1*time.Minute), "0"), }, want: report.Nodes{ ":192.168.1.1:12345": report.MakeNodeWith(map[string]string{ diff --git a/report/sets.go b/report/sets.go index 4a086246f..8ea9e6dcd 100644 --- a/report/sets.go +++ b/report/sets.go @@ -48,12 +48,30 @@ func (s Sets) Lookup(key string) (StringSet, bool) { return EmptyStringSet, false } +// Size returns the number of elements +func (s Sets) Size() int { + return s.psMap.Size() +} + // Merge merges two sets maps into a fresh set, performing set-union merges as // appropriate. func (s Sets) Merge(other Sets) Sets { - result := s.psMap + var ( + sSize = s.Size() + otherSize = other.Size() + result = s.psMap + iter = other.psMap + ) + switch { + case sSize == 0: + return other + case otherSize == 0: + return s + case sSize < otherSize: + result, iter = iter, result + } - other.psMap.ForEach(func(key string, value interface{}) { + iter.ForEach(func(key string, value interface{}) { set := value.(StringSet) if existingSet, ok := result.Lookup(key); ok { set = set.Merge(existingSet.(StringSet))