github.com/ethersphere/bee/v2@v2.2.0/pkg/topology/kademlia/internal/metrics/metrics_test.go (about) 1 // Copyright 2021 The Swarm Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package metrics_test 6 7 import ( 8 "testing" 9 "time" 10 11 "github.com/ethersphere/bee/v2/pkg/p2p" 12 "github.com/ethersphere/bee/v2/pkg/shed" 13 "github.com/ethersphere/bee/v2/pkg/swarm" 14 "github.com/ethersphere/bee/v2/pkg/topology/kademlia/internal/metrics" 15 "github.com/ethersphere/bee/v2/pkg/util/testutil" 16 "github.com/google/go-cmp/cmp" 17 ) 18 19 func snapshot(t *testing.T, mc *metrics.Collector, sst time.Time, addr swarm.Address) *metrics.Snapshot { 20 t.Helper() 21 22 ss := mc.Snapshot(sst, addr) 23 if have, want := len(ss), 1; have != want { 24 t.Fatalf("Snapshot(%q, ...): length mismatch: have: %d; want: %d", addr, have, want) 25 } 26 cs, ok := ss[addr.ByteString()] 27 if !ok { 28 t.Fatalf("Snapshot(%q, ...): missing peer metrics", addr) 29 } 30 return cs 31 } 32 33 func TestPeerMetricsCollector(t *testing.T) { 34 t.Parallel() 35 36 db, err := shed.NewDB("", nil) 37 if err != nil { 38 t.Fatal(err) 39 } 40 testutil.CleanupCloser(t, db) 41 42 mc, err := metrics.NewCollector(db) 43 if err != nil { 44 t.Fatal(err) 45 } 46 47 var ( 48 addr = swarm.MustParseHexAddress("0123456789") 49 50 t1 = time.Now() // Login time. 51 t2 = t1.Add(10 * time.Second) // Snapshot time. 52 t3 = t2.Add(55 * time.Second) // Logout time. 53 t4 = 10 * time.Millisecond // Latency duration. 54 t5 = 100 * time.Millisecond // Next latency duration sample. 55 ) 56 57 // Inc session conn retry. 58 mc.Record(addr, metrics.IncSessionConnectionRetry()) 59 ss := snapshot(t, mc, t2, addr) 60 if have, want := ss.SessionConnectionRetry, uint64(1); have != want { 61 t.Fatalf("Snapshot(%q, ...): session connection retry counter mismatch: have %d; want %d", addr, have, want) 62 } 63 64 // Login. 65 mc.Record(addr, metrics.PeerLogIn(t1, metrics.PeerConnectionDirectionInbound)) 66 ss = snapshot(t, mc, t2, addr) 67 if have, want := ss.LastSeenTimestamp, t1.UnixNano(); have != want { 68 t.Fatalf("Snapshot(%q, ...): last seen counter mismatch: have %d; want %d", addr, have, want) 69 } 70 if have, want := ss.SessionConnectionDirection, metrics.PeerConnectionDirectionInbound; have != want { 71 t.Fatalf("Snapshot(%q, ...): session connection direction counter mismatch: have %q; want %q", addr, have, want) 72 } 73 if have, want := ss.SessionConnectionDuration, t2.Sub(t1); have != want { 74 t.Fatalf("Snapshot(%q, ...): session connection duration counter mismatch: have %s; want %s", addr, have, want) 75 } 76 if have, want := ss.ConnectionTotalDuration, t2.Sub(t1); have != want { 77 t.Fatalf("Snapshot(%q, ...): connection total duration counter mismatch: have %s; want %s", addr, have, want) 78 } 79 80 // Login when already logged in. 81 mc.Record(addr, metrics.PeerLogIn(t1.Add(1*time.Second), metrics.PeerConnectionDirectionOutbound)) 82 ss = snapshot(t, mc, t2, addr) 83 if have, want := ss.LastSeenTimestamp, t1.UnixNano(); have != want { 84 t.Fatalf("Snapshot(%q, ...): last seen counter mismatch: have %d; want %d", addr, have, want) 85 } 86 if have, want := ss.SessionConnectionDirection, metrics.PeerConnectionDirectionInbound; have != want { 87 t.Fatalf("Snapshot(%q, ...): session connection direction counter mismatch: have %q; want %q", addr, have, want) 88 } 89 if have, want := ss.SessionConnectionDuration, t2.Sub(t1); have != want { 90 t.Fatalf("Snapshot(%q, ...): session connection duration counter mismatch: have %s; want %s", addr, have, want) 91 } 92 if have, want := ss.ConnectionTotalDuration, t2.Sub(t1); have != want { 93 t.Fatalf("Snapshot(%q, ...): connection total duration counter mismatch: have %s; want %s", addr, have, want) 94 } 95 96 // Inc session conn retry. 97 mc.Record(addr, metrics.IncSessionConnectionRetry()) 98 ss = snapshot(t, mc, t2, addr) 99 if have, want := ss.SessionConnectionRetry, uint64(2); have != want { 100 t.Fatalf("Snapshot(%q, ...): session connection retry counter mismatch: have %d; want %d", addr, have, want) 101 } 102 103 // Logout. 104 mc.Record(addr, metrics.PeerLogOut(t3)) 105 ss = snapshot(t, mc, t2, addr) 106 if have, want := ss.LastSeenTimestamp, t3.UnixNano(); have != want { 107 t.Fatalf("Snapshot(%q, ...): last seen counter mismatch: have %d; want %d", addr, have, want) 108 } 109 if have, want := ss.ConnectionTotalDuration, t3.Sub(t1); have != want { 110 t.Fatalf("Snapshot(%q, ...): connection total duration counter mismatch: have %s; want %s", addr, have, want) 111 } 112 if have, want := ss.SessionConnectionRetry, uint64(2); have != want { 113 t.Fatalf("Snapshot(%q, ...): session connection retry counter mismatch: have %d; want %d", addr, have, want) 114 } 115 if have, want := ss.SessionConnectionDuration, t3.Sub(t1); have != want { 116 t.Fatalf("Snapshot(%q, ...): session connection duration counter mismatch: have %q; want %q", addr, have, want) 117 } 118 119 // Latency. 120 mc.Record(addr, metrics.PeerLatency(t4)) 121 ss = snapshot(t, mc, t2, addr) 122 if have, want := ss.LatencyEWMA, t4; have != want { 123 t.Fatalf("Snapshot(%q, ...): latency mismatch: have %d; want %d", addr, have, want) 124 } 125 mc.Record(addr, metrics.PeerLatency(t5)) 126 ss = snapshot(t, mc, t2, addr) 127 if have, want := ss.LatencyEWMA, 19*time.Millisecond; have != want { 128 t.Fatalf("Snapshot(%q, ...): latency mismatch: have %d; want %d", addr, have, want) 129 } 130 131 // Reachability. 132 ss = snapshot(t, mc, t2, addr) 133 if have, want := ss.Reachability, p2p.ReachabilityStatusUnknown; have != want { 134 t.Fatalf("Snapshot(%q, ...): has reachability status mismatch: have %q; want %q", addr, have, want) 135 } 136 mc.Record(addr, metrics.PeerReachability(p2p.ReachabilityStatusPublic)) 137 ss = snapshot(t, mc, t2, addr) 138 if have, want := ss.Reachability, p2p.ReachabilityStatusPublic; have != want { 139 t.Fatalf("Snapshot(%q, ...): has reachability status mismatch: have %q; want %q", addr, have, want) 140 } 141 142 // Health. 143 ss = snapshot(t, mc, t2, addr) 144 if have, want := ss.Healthy, false; have != want { 145 t.Fatalf("Snapshot(%q, ...): has health status mismatch: have %v; want %v", addr, have, want) 146 } 147 mc.Record(addr, metrics.PeerHealth(true)) 148 ss = snapshot(t, mc, t2, addr) 149 if have, want := ss.Healthy, true; have != want { 150 t.Fatalf("Snapshot(%q, ...): has health status mismatch: have %v; want %v", addr, have, want) 151 } 152 mc.Record(addr, metrics.PeerHealth(false)) 153 ss = snapshot(t, mc, t2, addr) 154 if have, want := ss.Healthy, false; have != want { 155 t.Fatalf("Snapshot(%q, ...): has health status mismatch: have %v; want %v", addr, have, want) 156 } 157 158 // Inspect. 159 have := mc.Inspect(addr) 160 want := ss 161 if diff := cmp.Diff(have, want); diff != "" { 162 t.Fatalf("unexpected snapshot difference:\n%s", diff) 163 } 164 165 // Flush. 166 if err := mc.Flush(); err != nil { 167 t.Fatalf("Flush(): unexpected error: %v", err) 168 } 169 170 // Finalize. 171 mc.Record(addr, metrics.PeerLogIn(t1, metrics.PeerConnectionDirectionInbound)) 172 if err := mc.Finalize(t3, true); err != nil { 173 t.Fatalf("Finalize(%s): unexpected error: %v", t3, err) 174 } 175 if have, want := len(mc.Snapshot(t2, addr)), 0; have != want { 176 t.Fatalf("Finalize(%s): counters length mismatch: have %d; want %d", t3, have, want) 177 } 178 179 // Load the flushed metrics again from the persistent db. 180 mc, err = metrics.NewCollector(db) 181 if err != nil { 182 t.Fatal(err) 183 } 184 if have, want := len(mc.Snapshot(t2, addr)), 1; have != want { 185 t.Fatalf("NewCollector(...): counters length mismatch: have %d; want %d", have, want) 186 } 187 have = mc.Inspect(addr) 188 want = &metrics.Snapshot{ 189 LastSeenTimestamp: ss.LastSeenTimestamp, 190 ConnectionTotalDuration: 2 * ss.ConnectionTotalDuration, // 2x because we've already logout with t3 and login with t1 again. 191 } 192 if diff := cmp.Diff(have, want); diff != "" { 193 t.Fatalf("unexpected snapshot difference:\n%s", diff) 194 } 195 }