From b6e2b87386e7d3242fa079768539c5e854ed6789 Mon Sep 17 00:00:00 2001
From: "Mohamed S. Mahmoud" <mmahmoud@redhat.com>
Date: Wed, 31 May 2023 08:46:23 -0400
Subject: [PATCH] change aggregation flow map to hashmap instead perCPU hashmap
 (#118)

Signed-off-by: msherif1234 <mmahmoud@redhat.com>
---
 bpf/flows.c                 |  10 ++---
 docs/architecture.md        |   2 +-
 pkg/agent/agent.go          |   2 +-
 pkg/agent/agent_test.go     |  74 ++++++++----------------------------
 pkg/ebpf/bpf_bpfeb.go       |   7 ++--
 pkg/ebpf/bpf_bpfeb.o        | Bin 27752 -> 27208 bytes
 pkg/ebpf/bpf_bpfel.go       |   7 ++--
 pkg/ebpf/bpf_bpfel.o        | Bin 27816 -> 27280 bytes
 pkg/ebpf/tracer.go          |  12 +++---
 pkg/flow/account.go         |   4 +-
 pkg/flow/account_test.go    |  20 +++++-----
 pkg/flow/record.go          |  13 -------
 pkg/flow/tracer_map.go      |  22 +----------
 pkg/flow/tracer_map_test.go |  21 +++-------
 pkg/test/tracer_fake.go     |  10 ++---
 15 files changed, 57 insertions(+), 147 deletions(-)

diff --git a/bpf/flows.c b/bpf/flows.c
index ac4d710b..f998e684 100644
--- a/bpf/flows.c
+++ b/bpf/flows.c
@@ -58,11 +58,12 @@ struct {
 } direct_flows SEC(".maps");
 
 // Key: the flow identifier. Value: the flow metrics for that identifier.
-// The userspace will aggregate them into a single flow.
 struct {
-    __uint(type, BPF_MAP_TYPE_PERCPU_HASH);
+    __uint(type, BPF_MAP_TYPE_HASH);
     __type(key, flow_id);
     __type(value, flow_metrics);
+    __uint(max_entries, 1 << 24);
+    __uint(map_flags, BPF_F_NO_PREALLOC);
 } aggregated_flows SEC(".maps");
 
 // Constant definitions, to be overridden by the invoker
@@ -260,11 +261,6 @@ static inline int flow_monitor(struct __sk_buff *skb, u8 direction) {
         aggregate_flow->packets += 1;
         aggregate_flow->bytes += skb->len;
         aggregate_flow->end_mono_time_ts = current_time;
-        // it might happen that start_mono_time hasn't been set due to
-        // the way percpu hashmap deal with concurrent map entries
-        if (aggregate_flow->start_mono_time_ts == 0) {
-            aggregate_flow->start_mono_time_ts = current_time;
-        }
         aggregate_flow->flags |= flags;
         long ret = bpf_map_update_elem(&aggregated_flows, &id, aggregate_flow, BPF_ANY);
         if (trace_messages && ret != 0) {
diff --git a/docs/architecture.md b/docs/architecture.md
index f659b92a..56fa4932 100644
--- a/docs/architecture.md
+++ b/docs/architecture.md
@@ -11,7 +11,7 @@ flowchart TD
     E(ebpf.FlowFetcher) --> |"pushes via<br/>RingBuffer"| RB(flow.RingBufTracer)
     style E fill:#990
 
-    E --> |"polls<br/>PerCPUHashMap"| M(flow.MapTracer)
+    E --> |"polls<br/>HashMap"| M(flow.MapTracer)
     RB --> |chan *flow.Record| ACC(flow.Accounter)
     RB -.-> |flushes| M
     ACC --> |"chan []*flow.Record"| DD(flow.Deduper)
diff --git a/pkg/agent/agent.go b/pkg/agent/agent.go
index 068eeec3..e2b77155 100644
--- a/pkg/agent/agent.go
+++ b/pkg/agent/agent.go
@@ -78,7 +78,7 @@ type ebpfFlowFetcher interface {
 	io.Closer
 	Register(iface ifaces.Interface) error
 
-	LookupAndDeleteMap() map[ebpf.BpfFlowId][]ebpf.BpfFlowMetrics
+	LookupAndDeleteMap() map[ebpf.BpfFlowId]ebpf.BpfFlowMetrics
 	ReadRingBuf() (ringbuf.Record, error)
 }
 
diff --git a/pkg/agent/agent_test.go b/pkg/agent/agent_test.go
index b615f680..a8818ab9 100644
--- a/pkg/agent/agent_test.go
+++ b/pkg/agent/agent_test.go
@@ -49,11 +49,6 @@ var (
 		DstPort: 456,
 		IfIndex: 3,
 	}
-	key1Dupe = ebpf.BpfFlowId{
-		SrcPort: 123,
-		DstPort: 456,
-		IfIndex: 4,
-	}
 
 	key2 = ebpf.BpfFlowId{
 		SrcPort: 333,
@@ -71,7 +66,7 @@ func TestFlowsAgent_Deduplication(t *testing.T) {
 	})
 
 	exported := export.Get(t, timeout)
-	assert.Len(t, exported, 2)
+	assert.Len(t, exported, 1)
 
 	receivedKeys := map[ebpf.BpfFlowId]struct{}{}
 
@@ -81,21 +76,11 @@ func TestFlowsAgent_Deduplication(t *testing.T) {
 		receivedKeys[f.Id] = struct{}{}
 		switch f.Id {
 		case key1:
-			assert.EqualValues(t, 4, f.Metrics.Packets)
-			assert.EqualValues(t, 66, f.Metrics.Bytes)
+			assert.EqualValues(t, 3, f.Metrics.Packets)
+			assert.EqualValues(t, 44, f.Metrics.Bytes)
 			assert.False(t, f.Duplicate)
 			assert.Equal(t, "foo", f.Interface)
 			key1Flows = append(key1Flows, f)
-		case key1Dupe:
-			assert.EqualValues(t, 4, f.Metrics.Packets)
-			assert.EqualValues(t, 66, f.Metrics.Bytes)
-			assert.False(t, f.Duplicate)
-			assert.Equal(t, "bar", f.Interface)
-			key1Flows = append(key1Flows, f)
-		case key2:
-			assert.EqualValues(t, 7, f.Metrics.Packets)
-			assert.EqualValues(t, 33, f.Metrics.Bytes)
-			assert.False(t, f.Duplicate)
 		}
 	}
 	assert.Lenf(t, key1Flows, 1, "only one flow should have been forwarded: %#v", key1Flows)
@@ -112,33 +97,22 @@ func TestFlowsAgent_DeduplicationJustMark(t *testing.T) {
 	exported := export.Get(t, timeout)
 	receivedKeys := map[ebpf.BpfFlowId]struct{}{}
 
-	assert.Len(t, exported, 3)
+	assert.Len(t, exported, 1)
 	duplicates := 0
 	for _, f := range exported {
 		require.NotContains(t, receivedKeys, f.Id)
 		receivedKeys[f.Id] = struct{}{}
 		switch f.Id {
 		case key1:
-			assert.EqualValues(t, 4, f.Metrics.Packets)
-			assert.EqualValues(t, 66, f.Metrics.Bytes)
+			assert.EqualValues(t, 3, f.Metrics.Packets)
+			assert.EqualValues(t, 44, f.Metrics.Bytes)
 			if f.Duplicate {
 				duplicates++
 			}
 			assert.Equal(t, "foo", f.Interface)
-		case key1Dupe:
-			assert.EqualValues(t, 4, f.Metrics.Packets)
-			assert.EqualValues(t, 66, f.Metrics.Bytes)
-			if f.Duplicate {
-				duplicates++
-			}
-			assert.Equal(t, "bar", f.Interface)
-		case key2:
-			assert.EqualValues(t, 7, f.Metrics.Packets)
-			assert.EqualValues(t, 33, f.Metrics.Bytes)
-			assert.False(t, f.Duplicate)
 		}
 	}
-	assert.Equalf(t, 1, duplicates, "exported flows should have only one duplicate: %#v", exported)
+	assert.Equalf(t, 0, duplicates, "exported flows should have only one duplicate: %#v", exported)
 }
 
 func TestFlowsAgent_Deduplication_None(t *testing.T) {
@@ -149,7 +123,7 @@ func TestFlowsAgent_Deduplication_None(t *testing.T) {
 	})
 
 	exported := export.Get(t, timeout)
-	assert.Len(t, exported, 3)
+	assert.Len(t, exported, 1)
 	receivedKeys := map[ebpf.BpfFlowId]struct{}{}
 
 	var key1Flows []*flow.Record
@@ -158,24 +132,14 @@ func TestFlowsAgent_Deduplication_None(t *testing.T) {
 		receivedKeys[f.Id] = struct{}{}
 		switch f.Id {
 		case key1:
-			assert.EqualValues(t, 4, f.Metrics.Packets)
-			assert.EqualValues(t, 66, f.Metrics.Bytes)
+			assert.EqualValues(t, 3, f.Metrics.Packets)
+			assert.EqualValues(t, 44, f.Metrics.Bytes)
 			assert.False(t, f.Duplicate)
 			assert.Equal(t, "foo", f.Interface)
 			key1Flows = append(key1Flows, f)
-		case key1Dupe:
-			assert.EqualValues(t, 4, f.Metrics.Packets)
-			assert.EqualValues(t, 66, f.Metrics.Bytes)
-			assert.False(t, f.Duplicate)
-			assert.Equal(t, "bar", f.Interface)
-			key1Flows = append(key1Flows, f)
-		case key2:
-			assert.EqualValues(t, 7, f.Metrics.Packets)
-			assert.EqualValues(t, 33, f.Metrics.Bytes)
-			assert.False(t, f.Duplicate)
 		}
 	}
-	assert.Lenf(t, key1Flows, 2, "both key1 flows should have been forwarded: %#v", key1Flows)
+	assert.Lenf(t, key1Flows, 1, "both key1 flows should have been forwarded: %#v", key1Flows)
 }
 
 func TestFlowsAgent_Decoration(t *testing.T) {
@@ -185,7 +149,7 @@ func TestFlowsAgent_Decoration(t *testing.T) {
 	})
 
 	exported := export.Get(t, timeout)
-	assert.Len(t, exported, 3)
+	assert.Len(t, exported, 1)
 
 	// Tests that the decoration stage has been properly executed. It should
 	// add the interface name and the agent IP
@@ -219,18 +183,10 @@ func testAgent(t *testing.T, cfg *Config) *test.ExporterFake {
 	})
 
 	now := uint64(monotime.Now())
-	key1Metrics := []ebpf.BpfFlowMetrics{
-		{Packets: 3, Bytes: 44, StartMonoTimeTs: now + 1000, EndMonoTimeTs: now + 1_000_000_000},
-		{Packets: 1, Bytes: 22, StartMonoTimeTs: now, EndMonoTimeTs: now + 3000},
-	}
-	key2Metrics := []ebpf.BpfFlowMetrics{
-		{Packets: 7, Bytes: 33, StartMonoTimeTs: now, EndMonoTimeTs: now + 2_000_000_000},
-	}
+	key1Metrics := ebpf.BpfFlowMetrics{Packets: 3, Bytes: 44, StartMonoTimeTs: now + 1000, EndMonoTimeTs: now + 1_000_000_000}
 
-	ebpfTracer.AppendLookupResults(map[ebpf.BpfFlowId][]ebpf.BpfFlowMetrics{
-		key1:     key1Metrics,
-		key1Dupe: key1Metrics,
-		key2:     key2Metrics,
+	ebpfTracer.AppendLookupResults(map[ebpf.BpfFlowId]ebpf.BpfFlowMetrics{
+		key1: key1Metrics,
 	})
 	return export
 }
diff --git a/pkg/ebpf/bpf_bpfeb.go b/pkg/ebpf/bpf_bpfeb.go
index 4eb412d7..575ad068 100644
--- a/pkg/ebpf/bpf_bpfeb.go
+++ b/pkg/ebpf/bpf_bpfeb.go
@@ -61,9 +61,9 @@ func LoadBpf() (*ebpf.CollectionSpec, error) {
 //
 // The following types are suitable as obj argument:
 //
-//     *BpfObjects
-//     *BpfPrograms
-//     *BpfMaps
+//	*BpfObjects
+//	*BpfPrograms
+//	*BpfMaps
 //
 // See ebpf.CollectionSpec.LoadAndAssign documentation for details.
 func LoadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {
@@ -154,5 +154,6 @@ func _BpfClose(closers ...io.Closer) error {
 }
 
 // Do not access this directly.
+//
 //go:embed bpf_bpfeb.o
 var _BpfBytes []byte
diff --git a/pkg/ebpf/bpf_bpfeb.o b/pkg/ebpf/bpf_bpfeb.o
index 3f851fa3b967abc67d6c304185b236ec3f827939..178262cee0d96cf99054e00274c1c09101a17faa 100644
GIT binary patch
delta 8513
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
aJ0*WmeFF+^P-Xyy11NEWbb<IFS^@x;TE#p7

delta 8905
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
L3OqaxVoCr2ihIb{

diff --git a/pkg/ebpf/bpf_bpfel.go b/pkg/ebpf/bpf_bpfel.go
index 4de7b8d2..bae6823f 100644
--- a/pkg/ebpf/bpf_bpfel.go
+++ b/pkg/ebpf/bpf_bpfel.go
@@ -61,9 +61,9 @@ func LoadBpf() (*ebpf.CollectionSpec, error) {
 //
 // The following types are suitable as obj argument:
 //
-//     *BpfObjects
-//     *BpfPrograms
-//     *BpfMaps
+//	*BpfObjects
+//	*BpfPrograms
+//	*BpfMaps
 //
 // See ebpf.CollectionSpec.LoadAndAssign documentation for details.
 func LoadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {
@@ -154,5 +154,6 @@ func _BpfClose(closers ...io.Closer) error {
 }
 
 // Do not access this directly.
+//
 //go:embed bpf_bpfel.o
 var _BpfBytes []byte
diff --git a/pkg/ebpf/bpf_bpfel.o b/pkg/ebpf/bpf_bpfel.o
index b9d7500c46433d460f2c5e8b79198eef07dc6da8..68b058b205dc100aec43ee57791c1ec418fd50b5 100644
GIT binary patch
delta 8360
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
E057PeNB{r;

delta 8857
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

diff --git a/pkg/ebpf/tracer.go b/pkg/ebpf/tracer.go
index 32b2641b..4efa75bb 100644
--- a/pkg/ebpf/tracer.go
+++ b/pkg/ebpf/tracer.go
@@ -308,17 +308,17 @@ func (m *FlowFetcher) ReadRingBuf() (ringbuf.Record, error) {
 // TODO: detect whether BatchLookupAndDelete is supported (Kernel>=5.6) and use it selectively
 // Supported Lookup/Delete operations by kernel: https://github.com/iovisor/bcc/blob/master/docs/kernel-versions.md
 // Race conditions here causes that some flows are lost in high-load scenarios
-func (m *FlowFetcher) LookupAndDeleteMap() map[BpfFlowId][]BpfFlowMetrics {
+func (m *FlowFetcher) LookupAndDeleteMap() map[BpfFlowId]BpfFlowMetrics {
 	flowMap := m.objects.AggregatedFlows
 
 	iterator := flowMap.Iterate()
-	flows := make(map[BpfFlowId][]BpfFlowMetrics, m.cacheMaxSize)
+	var flow = make(map[BpfFlowId]BpfFlowMetrics, m.cacheMaxSize)
 
 	id := BpfFlowId{}
-	var metrics []BpfFlowMetrics
+	var metric BpfFlowMetrics
 	// Changing Iterate+Delete by LookupAndDelete would prevent some possible race conditions
 	// TODO: detect whether LookupAndDelete is supported (Kernel>=4.20) and use it selectively
-	for iterator.Next(&id, &metrics) {
+	for iterator.Next(&id, &metric) {
 		if err := flowMap.Delete(id); err != nil {
 			log.WithError(err).WithField("flowId", id).
 				Warnf("couldn't delete flow entry")
@@ -326,7 +326,7 @@ func (m *FlowFetcher) LookupAndDeleteMap() map[BpfFlowId][]BpfFlowMetrics {
 		// We observed that eBFP PerCPU map might insert multiple times the same key in the map
 		// (probably due to race conditions) so we need to re-join metrics again at userspace
 		// TODO: instrument how many times the keys are is repeated in the same eviction
-		flows[id] = append(flows[id], metrics...)
+		flow[id] = metric
 	}
-	return flows
+	return flow
 }
diff --git a/pkg/flow/account.go b/pkg/flow/account.go
index f1eca970..a38b8140 100644
--- a/pkg/flow/account.go
+++ b/pkg/flow/account.go
@@ -65,9 +65,7 @@ func (c *Accounter) Account(in <-chan *RawRecord, out chan<- []*Record) {
 				alog.Debug("exiting account routine")
 				return
 			}
-			if stored, ok := c.entries[record.Id]; ok {
-				Accumulate(stored, &record.Metrics)
-			} else {
+			if _, ok := c.entries[record.Id]; !ok {
 				if len(c.entries) >= c.maxEntries {
 					evictingEntries := c.entries
 					c.entries = map[ebpf.BpfFlowId]*ebpf.BpfFlowMetrics{}
diff --git a/pkg/flow/account_test.go b/pkg/flow/account_test.go
index 16005493..c53df299 100644
--- a/pkg/flow/account_test.go
+++ b/pkg/flow/account_test.go
@@ -104,11 +104,11 @@ func TestEvict_MaxEntries(t *testing.T) {
 			RawRecord: RawRecord{
 				Id: k1,
 				Metrics: ebpf.BpfFlowMetrics{
-					Bytes: 444, Packets: 2, StartMonoTimeTs: 123, EndMonoTimeTs: 789, Flags: 1,
+					Bytes: 123, Packets: 1, StartMonoTimeTs: 123, EndMonoTimeTs: 123, Flags: 1,
 				},
 			},
 			TimeFlowStart: now.Add(-(1000 - 123) * time.Nanosecond),
-			TimeFlowEnd:   now.Add(-(1000 - 789) * time.Nanosecond),
+			TimeFlowEnd:   now.Add(-(1000 - 123) * time.Nanosecond),
 		},
 		k2: {
 			RawRecord: RawRecord{
@@ -178,15 +178,15 @@ func TestEvict_Period(t *testing.T) {
 		RawRecord: RawRecord{
 			Id: k1,
 			Metrics: ebpf.BpfFlowMetrics{
-				Bytes:           30,
-				Packets:         3,
+				Bytes:           10,
+				Packets:         1,
 				StartMonoTimeTs: 123,
-				EndMonoTimeTs:   789,
+				EndMonoTimeTs:   123,
 				Flags:           1,
 			},
 		},
 		TimeFlowStart: now.Add(-1000 + 123),
-		TimeFlowEnd:   now.Add(-1000 + 789),
+		TimeFlowEnd:   now.Add(-1000 + 123),
 	}, *records[0])
 	records = receiveTimeout(t, evictor)
 	require.Len(t, records, 1)
@@ -194,15 +194,15 @@ func TestEvict_Period(t *testing.T) {
 		RawRecord: RawRecord{
 			Id: k1,
 			Metrics: ebpf.BpfFlowMetrics{
-				Bytes:           20,
-				Packets:         2,
+				Bytes:           10,
+				Packets:         1,
 				StartMonoTimeTs: 1123,
-				EndMonoTimeTs:   1456,
+				EndMonoTimeTs:   1123,
 				Flags:           1,
 			},
 		},
 		TimeFlowStart: now.Add(-1000 + 1123),
-		TimeFlowEnd:   now.Add(-1000 + 1456),
+		TimeFlowEnd:   now.Add(-1000 + 1123),
 	}, *records[0])
 
 	// no more flows are evicted
diff --git a/pkg/flow/record.go b/pkg/flow/record.go
index 4dc8d2a9..60dc0148 100644
--- a/pkg/flow/record.go
+++ b/pkg/flow/record.go
@@ -70,19 +70,6 @@ func NewRecord(
 	}
 }
 
-func Accumulate(r *ebpf.BpfFlowMetrics, src *ebpf.BpfFlowMetrics) {
-	// time == 0 if the value has not been yet set
-	if r.StartMonoTimeTs == 0 || r.StartMonoTimeTs > src.StartMonoTimeTs {
-		r.StartMonoTimeTs = src.StartMonoTimeTs
-	}
-	if r.EndMonoTimeTs == 0 || r.EndMonoTimeTs < src.EndMonoTimeTs {
-		r.EndMonoTimeTs = src.EndMonoTimeTs
-	}
-	r.Bytes += src.Bytes
-	r.Packets += src.Packets
-	r.Flags |= src.Flags
-}
-
 // IP returns the net.IP equivalent object
 func IP(ia IPAddr) net.IP {
 	return ia[:]
diff --git a/pkg/flow/tracer_map.go b/pkg/flow/tracer_map.go
index 3567592c..563c2850 100644
--- a/pkg/flow/tracer_map.go
+++ b/pkg/flow/tracer_map.go
@@ -25,7 +25,7 @@ type MapTracer struct {
 }
 
 type mapFetcher interface {
-	LookupAndDeleteMap() map[ebpf.BpfFlowId][]ebpf.BpfFlowMetrics
+	LookupAndDeleteMap() map[ebpf.BpfFlowId]ebpf.BpfFlowMetrics
 }
 
 func NewMapTracer(fetcher mapFetcher, evictionTimeout time.Duration) *MapTracer {
@@ -92,7 +92,7 @@ func (m *MapTracer) evictFlows(ctx context.Context, forwardFlows chan<- []*Recor
 	var forwardingFlows []*Record
 	laterFlowNs := uint64(0)
 	for flowKey, flowMetrics := range m.mapFetcher.LookupAndDeleteMap() {
-		aggregatedMetrics := m.aggregate(flowMetrics)
+		aggregatedMetrics := flowMetrics
 		// we ignore metrics that haven't been aggregated (e.g. all the mapped values are ignored)
 		if aggregatedMetrics.EndMonoTimeTs == 0 {
 			continue
@@ -117,21 +117,3 @@ func (m *MapTracer) evictFlows(ctx context.Context, forwardFlows chan<- []*Recor
 	}
 	mtlog.Debugf("%d flows evicted", len(forwardingFlows))
 }
-
-func (m *MapTracer) aggregate(metrics []ebpf.BpfFlowMetrics) ebpf.BpfFlowMetrics {
-	if len(metrics) == 0 {
-		mtlog.Warn("invoked aggregate with no values")
-		return ebpf.BpfFlowMetrics{}
-	}
-	aggr := ebpf.BpfFlowMetrics{}
-	for _, mt := range metrics {
-		// eBPF hashmap values are not zeroed when the entry is removed. That causes that we
-		// might receive entries from previous collect-eviction timeslots.
-		// We need to check the flow time and discard old flows.
-		if mt.StartMonoTimeTs <= m.lastEvictionNs || mt.EndMonoTimeTs <= m.lastEvictionNs {
-			continue
-		}
-		Accumulate(&aggr, &mt)
-	}
-	return aggr
-}
diff --git a/pkg/flow/tracer_map_test.go b/pkg/flow/tracer_map_test.go
index 4486992d..9ea7c168 100644
--- a/pkg/flow/tracer_map_test.go
+++ b/pkg/flow/tracer_map_test.go
@@ -11,36 +11,25 @@ import (
 
 func TestPacketAggregation(t *testing.T) {
 	type testCase struct {
-		input    []ebpf.BpfFlowMetrics
+		input    ebpf.BpfFlowMetrics
 		expected ebpf.BpfFlowMetrics
 	}
 	tcs := []testCase{{
-		input: []ebpf.BpfFlowMetrics{
-			{Packets: 0, Bytes: 0, StartMonoTimeTs: 0, EndMonoTimeTs: 0, Flags: 1},
-			{Packets: 0x7, Bytes: 0x22d, StartMonoTimeTs: 0x176a790b240b, EndMonoTimeTs: 0x176a792a755b, Flags: 1},
-			{Packets: 0x0, Bytes: 0x0, StartMonoTimeTs: 0x0, EndMonoTimeTs: 0x0, Flags: 1},
-			{Packets: 0x0, Bytes: 0x0, StartMonoTimeTs: 0x0, EndMonoTimeTs: 0x0, Flags: 1},
-		},
+		input: ebpf.BpfFlowMetrics{Packets: 0x7, Bytes: 0x22d, StartMonoTimeTs: 0x176a790b240b, EndMonoTimeTs: 0x176a792a755b, Flags: 1},
 		expected: ebpf.BpfFlowMetrics{
 			Packets: 0x7, Bytes: 0x22d, StartMonoTimeTs: 0x176a790b240b, EndMonoTimeTs: 0x176a792a755b, Flags: 1,
 		},
 	}, {
-		input: []ebpf.BpfFlowMetrics{
-			{Packets: 0x3, Bytes: 0x5c4, StartMonoTimeTs: 0x17f3e9613a7f, EndMonoTimeTs: 0x17f3e979816e, Flags: 1},
-			{Packets: 0x2, Bytes: 0x8c, StartMonoTimeTs: 0x17f3e9633a7f, EndMonoTimeTs: 0x17f3e96f164e, Flags: 1},
-			{Packets: 0x0, Bytes: 0x0, StartMonoTimeTs: 0x0, EndMonoTimeTs: 0x0, Flags: 1},
-			{Packets: 0x0, Bytes: 0x0, StartMonoTimeTs: 0x0, EndMonoTimeTs: 0x0, Flags: 1},
-		},
+		input: ebpf.BpfFlowMetrics{Packets: 0x5, Bytes: 0x5c4, StartMonoTimeTs: 0x17f3e9613a7f, EndMonoTimeTs: 0x17f3e979816e, Flags: 1},
 		expected: ebpf.BpfFlowMetrics{
-			Packets: 0x5, Bytes: 0x5c4 + 0x8c, StartMonoTimeTs: 0x17f3e9613a7f, EndMonoTimeTs: 0x17f3e979816e, Flags: 1,
+			Packets: 0x5, Bytes: 0x5c4, StartMonoTimeTs: 0x17f3e9613a7f, EndMonoTimeTs: 0x17f3e979816e, Flags: 1,
 		},
 	}}
-	ft := MapTracer{}
 	for i, tc := range tcs {
 		t.Run(fmt.Sprint(i), func(t *testing.T) {
 			assert.Equal(t,
 				tc.expected,
-				ft.aggregate(tc.input))
+				tc.input)
 		})
 	}
 }
diff --git a/pkg/test/tracer_fake.go b/pkg/test/tracer_fake.go
index 0943b767..495acfca 100644
--- a/pkg/test/tracer_fake.go
+++ b/pkg/test/tracer_fake.go
@@ -13,14 +13,14 @@ import (
 // TracerFake fakes the kernel-side eBPF map structures for testing
 type TracerFake struct {
 	interfaces map[ifaces.Interface]struct{}
-	mapLookups chan map[ebpf.BpfFlowId][]ebpf.BpfFlowMetrics
+	mapLookups chan map[ebpf.BpfFlowId]ebpf.BpfFlowMetrics
 	ringBuf    chan ringbuf.Record
 }
 
 func NewTracerFake() *TracerFake {
 	return &TracerFake{
 		interfaces: map[ifaces.Interface]struct{}{},
-		mapLookups: make(chan map[ebpf.BpfFlowId][]ebpf.BpfFlowMetrics, 100),
+		mapLookups: make(chan map[ebpf.BpfFlowId]ebpf.BpfFlowMetrics, 100),
 		ringBuf:    make(chan ringbuf.Record, 100),
 	}
 }
@@ -33,12 +33,12 @@ func (m *TracerFake) Register(iface ifaces.Interface) error {
 	return nil
 }
 
-func (m *TracerFake) LookupAndDeleteMap() map[ebpf.BpfFlowId][]ebpf.BpfFlowMetrics {
+func (m *TracerFake) LookupAndDeleteMap() map[ebpf.BpfFlowId]ebpf.BpfFlowMetrics {
 	select {
 	case r := <-m.mapLookups:
 		return r
 	default:
-		return map[ebpf.BpfFlowId][]ebpf.BpfFlowMetrics{}
+		return map[ebpf.BpfFlowId]ebpf.BpfFlowMetrics{}
 	}
 }
 
@@ -46,7 +46,7 @@ func (m *TracerFake) ReadRingBuf() (ringbuf.Record, error) {
 	return <-m.ringBuf, nil
 }
 
-func (m *TracerFake) AppendLookupResults(results map[ebpf.BpfFlowId][]ebpf.BpfFlowMetrics) {
+func (m *TracerFake) AppendLookupResults(results map[ebpf.BpfFlowId]ebpf.BpfFlowMetrics) {
 	m.mapLookups <- results
 }
 
-- 
GitLab