From b138dda3b381eb0b7ff2f096b7a6ceebb8ea38f4 Mon Sep 17 00:00:00 2001 From: Peter Bourgon Date: Tue, 25 Aug 2015 13:31:25 +0100 Subject: [PATCH] Move merge functions with their types --- report/merge.go | 116 --------------------------------------------- report/report.go | 19 ++++++++ report/topology.go | 94 +++++++++++++++++++++++++++++++++++- 3 files changed, 111 insertions(+), 118 deletions(-) delete mode 100644 report/merge.go diff --git a/report/merge.go b/report/merge.go deleted file mode 100644 index 223ffa433..000000000 --- a/report/merge.go +++ /dev/null @@ -1,116 +0,0 @@ -package report - -// Merge functions for all topology datatypes. The general semantics are that -// the receiver is modified, and what's merged in isn't. - -// Merge merges another Report into the receiver. Pass addWindows true if the -// reports represent distinct (non-overlapping) periods of time. -func (r *Report) Merge(other Report) { - r.Endpoint.Merge(other.Endpoint) - r.Address.Merge(other.Address) - r.Process.Merge(other.Process) - r.Container.Merge(other.Container) - r.ContainerImage.Merge(other.ContainerImage) - r.Host.Merge(other.Host) - r.Overlay.Merge(other.Overlay) - r.Sampling.Merge(other.Sampling) - r.Window += other.Window -} - -// Merge merges another Topology into the receiver. -func (t *Topology) Merge(other Topology) { - t.Adjacency.Merge(other.Adjacency) - t.EdgeMetadatas.Merge(other.EdgeMetadatas) - t.NodeMetadatas.Merge(other.NodeMetadatas) -} - -// Merge merges another Adjacency list into the receiver. -func (a *Adjacency) Merge(other Adjacency) { - for addr, adj := range other { - (*a)[addr] = (*a)[addr].Merge(adj) - } -} - -// Merge merges another NodeMetadatas into the receiver. -func (m *NodeMetadatas) Merge(other NodeMetadatas) { - for id, meta := range other { - if _, ok := (*m)[id]; !ok { - (*m)[id] = meta // not a copy - } - } -} - -// Merge merges two node metadata maps together. In case of conflict, the -// other (right-hand) side wins. Always reassign the result of merge to the -// destination. Merge is defined on the value-type, but node metadata map is -// itself a reference type, so if you want to maintain immutability, use copy. -func (nm NodeMetadata) Merge(other NodeMetadata) NodeMetadata { - for k, v := range other.Metadata { - nm.Metadata[k] = v // other takes precedence - } - for k, v := range other.Counters { - nm.Counters[k] = nm.Counters[k] + v - } - return nm -} - -// Merge merges another EdgeMetadatas into the receiver. If other is from -// another probe this is the union of both metadatas. Keys present in both are -// summed. -func (e *EdgeMetadatas) Merge(other EdgeMetadatas) { - for id, edgemeta := range other { - local := (*e)[id] - local.Merge(edgemeta) - (*e)[id] = local - } -} - -// Merge merges another EdgeMetadata into the receiver. The two edge metadatas -// should represent the same edge on different times. -func (m *EdgeMetadata) Merge(other EdgeMetadata) { - m.EgressPacketCount = merge(m.EgressPacketCount, other.EgressPacketCount, sum) - m.IngressPacketCount = merge(m.IngressPacketCount, other.IngressPacketCount, sum) - m.EgressByteCount = merge(m.EgressByteCount, other.EgressByteCount, sum) - m.IngressByteCount = merge(m.IngressByteCount, other.IngressByteCount, sum) - m.MaxConnCountTCP = merge(m.MaxConnCountTCP, other.MaxConnCountTCP, max) -} - -// Flatten sums two EdgeMetadatas. Their windows should be the same duration; -// they should represent different edges at the same time. -func (m *EdgeMetadata) Flatten(other EdgeMetadata) { - m.EgressPacketCount = merge(m.EgressPacketCount, other.EgressPacketCount, sum) - m.IngressPacketCount = merge(m.IngressPacketCount, other.IngressPacketCount, sum) - m.EgressByteCount = merge(m.EgressByteCount, other.EgressByteCount, sum) - m.IngressByteCount = merge(m.IngressByteCount, other.IngressByteCount, sum) - // Note that summing of two maximums doesn't always give us the true - // maximum. But it's a best effort. - m.MaxConnCountTCP = merge(m.MaxConnCountTCP, other.MaxConnCountTCP, sum) -} - -// Merge combines two sampling structures via simple addition. -func (s *Sampling) Merge(other Sampling) { - s.Count += other.Count - s.Total += other.Total -} - -func merge(dst, src *uint64, op func(uint64, uint64) uint64) *uint64 { - if src == nil { - return dst - } - if dst == nil { - dst = new(uint64) - } - (*dst) = op(*dst, *src) - return dst -} - -func sum(dst, src uint64) uint64 { - return dst + src -} - -func max(dst, src uint64) uint64 { - if dst > src { - return dst - } - return src -} diff --git a/report/report.go b/report/report.go index 990d5bab0..05df26599 100644 --- a/report/report.go +++ b/report/report.go @@ -70,6 +70,19 @@ func MakeReport() Report { } } +// Merge merges another Report into the receiver. +func (r *Report) Merge(other Report) { + r.Endpoint.Merge(other.Endpoint) + r.Address.Merge(other.Address) + r.Process.Merge(other.Process) + r.Container.Merge(other.Container) + r.ContainerImage.Merge(other.ContainerImage) + r.Host.Merge(other.Host) + r.Overlay.Merge(other.Overlay) + r.Sampling.Merge(other.Sampling) + r.Window += other.Window +} + // Topologies returns a slice of Topologies in this report func (r Report) Topologies() []Topology { return []Topology{ @@ -118,6 +131,12 @@ func (s Sampling) Rate() float64 { return float64(s.Count) / float64(s.Total) } +// Merge combines two sampling structures via simple addition. +func (s *Sampling) Merge(other Sampling) { + s.Count += other.Count + s.Total += other.Total +} + const ( // HostNodeID is a metadata foreign key, linking a node in any topology to // a node in the host topology. That host node is the origin host, where diff --git a/report/topology.go b/report/topology.go index 6c4760ea3..857c55e0c 100644 --- a/report/topology.go +++ b/report/topology.go @@ -5,8 +5,6 @@ import ( "strings" ) -const localUnknown = "localUnknown" - // Topology describes a specific view of a network. It consists of nodes and // edges, represented by Adjacency, and metadata about those nodes and edges, // represented by EdgeMetadatas and NodeMetadatas respectively. @@ -16,18 +14,52 @@ type Topology struct { NodeMetadatas } +// Merge merges another Topology into the receiver. +func (t *Topology) Merge(other Topology) { + t.Adjacency.Merge(other.Adjacency) + t.EdgeMetadatas.Merge(other.EdgeMetadatas) + t.NodeMetadatas.Merge(other.NodeMetadatas) +} + // Adjacency is an adjacency-list encoding of the topology. Keys are node IDs, // as produced by the relevant MappingFunc for the topology. type Adjacency map[string]IDList +// Merge merges another Adjacency list into the receiver. +func (a *Adjacency) Merge(other Adjacency) { + for addr, adj := range other { + (*a)[addr] = (*a)[addr].Merge(adj) + } +} + // EdgeMetadatas collect metadata about each edge in a topology. Keys are a // concatenation of node IDs. type EdgeMetadatas map[string]EdgeMetadata +// Merge merges another EdgeMetadatas into the receiver. If other is from +// another probe this is the union of both metadatas. Keys present in both are +// summed. +func (e *EdgeMetadatas) Merge(other EdgeMetadatas) { + for id, edgemeta := range other { + local := (*e)[id] + local.Merge(edgemeta) + (*e)[id] = local + } +} + // NodeMetadatas collect metadata about each node in a topology. Keys are node // IDs. type NodeMetadatas map[string]NodeMetadata +// Merge merges another NodeMetadatas into the receiver. +func (m *NodeMetadatas) Merge(other NodeMetadatas) { + for id, meta := range other { + if _, ok := (*m)[id]; !ok { + (*m)[id] = meta // not a copy + } + } +} + // EdgeMetadata describes a superset of the metadata that probes can possibly // collect about a directed edge between two nodes in any topology. type EdgeMetadata struct { @@ -38,6 +70,28 @@ type EdgeMetadata struct { MaxConnCountTCP *uint64 `json:"max_conn_count_tcp,omitempty"` } +// Merge merges another EdgeMetadata into the receiver. The two edge metadatas +// should represent the same edge on different times. +func (m *EdgeMetadata) Merge(other EdgeMetadata) { + m.EgressPacketCount = merge(m.EgressPacketCount, other.EgressPacketCount, sum) + m.IngressPacketCount = merge(m.IngressPacketCount, other.IngressPacketCount, sum) + m.EgressByteCount = merge(m.EgressByteCount, other.EgressByteCount, sum) + m.IngressByteCount = merge(m.IngressByteCount, other.IngressByteCount, sum) + m.MaxConnCountTCP = merge(m.MaxConnCountTCP, other.MaxConnCountTCP, max) +} + +// Flatten sums two EdgeMetadatas. Their windows should be the same duration; +// they should represent different edges at the same time. +func (m *EdgeMetadata) Flatten(other EdgeMetadata) { + m.EgressPacketCount = merge(m.EgressPacketCount, other.EgressPacketCount, sum) + m.IngressPacketCount = merge(m.IngressPacketCount, other.IngressPacketCount, sum) + m.EgressByteCount = merge(m.EgressByteCount, other.EgressByteCount, sum) + m.IngressByteCount = merge(m.IngressByteCount, other.IngressByteCount, sum) + // Note that summing of two maximums doesn't always give us the true + // maximum. But it's a best effort. + m.MaxConnCountTCP = merge(m.MaxConnCountTCP, other.MaxConnCountTCP, sum) +} + // NodeMetadata describes a superset of the metadata that probes can collect // about a given node in a given topology. type NodeMetadata struct { @@ -58,6 +112,20 @@ func MakeNodeMetadataWith(m map[string]string) NodeMetadata { } } +// Merge merges two node metadata maps together. In case of conflict, the +// other (right-hand) side wins. Always reassign the result of merge to the +// destination. Merge is defined on the value-type, but node metadata map is +// itself a reference type, so if you want to maintain immutability, use copy. +func (nm NodeMetadata) Merge(other NodeMetadata) NodeMetadata { + for k, v := range other.Metadata { + nm.Metadata[k] = v // other takes precedence + } + for k, v := range other.Counters { + nm.Counters[k] = nm.Counters[k] + v + } + return nm +} + // Copy returns a value copy, useful for tests. func (nm NodeMetadata) Copy() NodeMetadata { cp := MakeNodeMetadata() @@ -140,3 +208,25 @@ func (t Topology) Validate() error { return nil } + +func merge(dst, src *uint64, op func(uint64, uint64) uint64) *uint64 { + if src == nil { + return dst + } + if dst == nil { + dst = new(uint64) + } + (*dst) = op(*dst, *src) + return dst +} + +func sum(dst, src uint64) uint64 { + return dst + src +} + +func max(dst, src uint64) uint64 { + if dst > src { + return dst + } + return src +}