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  }