Repository: nats-io/nats-server
Pull request #3133 (2022-05-18 19:37:25)

[ADDED] Monitoring: Routez's individual route has now more info
Added Start, LastActivity, Uptime and Idle that we normally have
in a Connz for non route connections. This info can be useful
to determine if a route is recent, etc..

Signed-off-by: Ivan Kozlovic <ivan@synadia.com>



Patch:
diff --git a/server/monitor.go b/server/monitor.go
index 492dec7b..9c257911 100644
--- a/server/monitor.go
+++ b/server/monitor.go
@@ -749,10 +749,14 @@ type RouteInfo struct {
 	IsConfigured bool               `json:"is_configured"`
 	IP           string             `json:"ip"`
 	Port         int                `json:"port"`
+	Start        time.Time          `json:"start"`
+	LastActivity time.Time          `json:"last_activity"`
+	RTT          string             `json:"rtt,omitempty"`
+	Uptime       string             `json:"uptime"`
+	Idle         string             `json:"idle"`
 	Import       *SubjectPermission `json:"import,omitempty"`
 	Export       *SubjectPermission `json:"export,omitempty"`
 	Pending      int                `json:"pending_size"`
-	RTT          string             `json:"rtt,omitempty"`
 	InMsgs       int64              `json:"in_msgs"`
 	OutMsgs      int64              `json:"out_msgs"`
 	InBytes      int64              `json:"in_bytes"`
@@ -799,6 +803,10 @@ func (s *Server) Routez(routezOpts *RoutezOptions) (*Routez, error) {
 			Import:       r.opts.Import,
 			Export:       r.opts.Export,
 			RTT:          r.getRTT().String(),
+			Start:        r.start,
+			LastActivity: r.last,
+			Uptime:       myUptime(rs.Now.Sub(r.start)),
+			Idle:         myUptime(rs.Now.Sub(r.last)),
 		}
 
 		if len(r.subs) > 0 {
diff --git a/server/raft.go b/server/raft.go
index 39163a43..e78c15cf 100644
--- a/server/raft.go
+++ b/server/raft.go
@@ -228,11 +228,11 @@ type lps struct {
 }
 
 const (
-	minElectionTimeoutDefault      = 4 * time.Second
-	maxElectionTimeoutDefault      = 9 * time.Second
+	minElectionTimeoutDefault      = 2 * time.Second
+	maxElectionTimeoutDefault      = 5 * time.Second
 	minCampaignTimeoutDefault      = 100 * time.Millisecond
 	maxCampaignTimeoutDefault      = 8 * minCampaignTimeoutDefault
-	hbIntervalDefault              = 1 * time.Second
+	hbIntervalDefault              = 500 * time.Millisecond
 	lostQuorumIntervalDefault      = hbIntervalDefault * 20 // 10 seconds
 	lostQuorumCheckIntervalDefault = hbIntervalDefault * 20 // 10 seconds
 
@@ -2606,6 +2606,12 @@ func (n *raft) handleAppendEntry(sub *subscription, c *client, _ *Account, subje
 	} else {
 		n.warn("AppendEntry failed to be placed on internal channel: corrupt entry")
 	}
+	n.Lock()
+	// Don't reset here if we have been asked to assume leader position.
+	if !n.lxfer {
+		n.resetElectionTimeout()
+	}
+	n.Unlock()
 }
 
 // Lock should be held.
@@ -2674,11 +2680,6 @@ func (n *raft) updateLeader(newLeader string) {
 func (n *raft) processAppendEntry(ae *appendEntry, sub *subscription) {
 	n.Lock()
 
-	// Don't reset here if we have been asked to assume leader position.
-	if !n.lxfer {
-		n.resetElectionTimeout()
-	}
-
 	// Just return if closed or we had previous write error.
 	if n.state == Closed || n.werr != nil {
 		n.Unlock()
diff --git a/server/route.go b/server/route.go
index 927faf3b..aead8697 100644
--- a/server/route.go
+++ b/server/route.go
@@ -1289,7 +1289,7 @@ func (s *Server) createRoute(conn net.Conn, rURL *url.URL) *client {
 		}
 	}
 
-	c := &client{srv: s, nc: conn, opts: ClientOpts{}, kind: ROUTER, msubs: -1, mpay: -1, route: r}
+	c := &client{srv: s, nc: conn, opts: ClientOpts{}, kind: ROUTER, msubs: -1, mpay: -1, route: r, start: time.Now()}
 
 	// Grab server variables
 	s.mu.Lock()


Test patch:
diff --git a/server/jetstream_cluster_test.go b/server/jetstream_cluster_test.go
index 9bd79eb5..4de1e8f9 100644
--- a/server/jetstream_cluster_test.go
+++ b/server/jetstream_cluster_test.go
@@ -3996,13 +3996,6 @@ func TestJetStreamClusterNoDuplicateOnNodeRestart(t *testing.T) {
 		t.Fatalf("Unexpected error: %v", err)
 	}
 
-	sl := c.streamLeader("$G", "TEST")
-	if s == sl {
-		nc.Close()
-		nc, js = jsClientConnect(t, s)
-		defer nc.Close()
-	}
-
 	sub, err := js.SubscribeSync("foo", nats.Durable("dlc"))
 	if err != nil {
 		t.Fatalf("Unexpected error: %v", err)
@@ -4015,14 +4008,14 @@ func TestJetStreamClusterNoDuplicateOnNodeRestart(t *testing.T) {
 		m.AckSync()
 	}
 
+	sl := c.streamLeader("$G", "TEST")
 	sl.Shutdown()
 	c.restartServer(sl)
 	c.waitOnStreamLeader("$G", "TEST")
-	c.waitOnConsumerLeader("$G", "TEST", "dlc")
 
 	// Send second msg
 	js.Publish("foo", []byte("msg2"))
-	msg, err := sub.NextMsg(5 * time.Second)
+	msg, err := sub.NextMsg(time.Second)
 	if err != nil {
 		t.Fatalf("Error getting message: %v", err)
 	}
diff --git a/server/jetstream_helpers_test.go b/server/jetstream_helpers_test.go
index ef168b99..21129d5e 100644
--- a/server/jetstream_helpers_test.go
+++ b/server/jetstream_helpers_test.go
@@ -424,7 +424,7 @@ func (sc *supercluster) leader() *Server {
 
 func (sc *supercluster) waitOnLeader() {
 	sc.t.Helper()
-	expires := time.Now().Add(10 * time.Second)
+	expires := time.Now().Add(5 * time.Second)
 	for time.Now().Before(expires) {
 		for _, c := range sc.clusters {
 			if leader := c.leader(); leader != nil {
diff --git a/server/monitor_test.go b/server/monitor_test.go
index 7b49813b..e4b6bc30 100644
--- a/server/monitor_test.go
+++ b/server/monitor_test.go
@@ -1313,6 +1313,7 @@ func TestConnzWithRoutes(t *testing.T) {
 	routeURL, _ := url.Parse(fmt.Sprintf("nats-route://127.0.0.1:%d", s.ClusterAddr().Port))
 	opts.Routes = []*url.URL{routeURL}
 
+	start := time.Now()
 	sc := RunServer(opts)
 	defer sc.Shutdown()
 
@@ -1324,10 +1325,10 @@ func TestConnzWithRoutes(t *testing.T) {
 		// Test contents..
 		// Make sure routes don't show up under connz, but do under routez
 		if c.NumConns != 0 {
-			t.Fatalf("Expected 0 connections, got %d\n", c.NumConns)
+			t.Fatalf("Expected 0 connections, got %d", c.NumConns)
 		}
 		if c.Conns == nil || len(c.Conns) != 0 {
-			t.Fatalf("Expected 0 connections in array, got %p\n", c.Conns)
+			t.Fatalf("Expected 0 connections in array, got %p", c.Conns)
 		}
 	}
 
@@ -1345,17 +1346,32 @@ func TestConnzWithRoutes(t *testing.T) {
 			rz := pollRoutez(t, s, mode, url+urlSuffix, &RoutezOptions{Subscriptions: subs == 1, SubscriptionsDetail: subs == 2})
 
 			if rz.NumRoutes != 1 {
-				t.Fatalf("Expected 1 route, got %d\n", rz.NumRoutes)
+				t.Fatalf("Expected 1 route, got %d", rz.NumRoutes)
 			}
 
 			if len(rz.Routes) != 1 {
-				t.Fatalf("Expected route array of 1, got %v\n", len(rz.Routes))
+				t.Fatalf("Expected route array of 1, got %v", len(rz.Routes))
 			}
 
 			route := rz.Routes[0]
 
 			if route.DidSolicit {
-				t.Fatalf("Expected unsolicited route, got %v\n", route.DidSolicit)
+				t.Fatalf("Expected unsolicited route, got %v", route.DidSolicit)
+			}
+
+			if route.Start.IsZero() {
+				t.Fatalf("Expected Start to be set, got %+v", route)
+			} else if route.Start.Before(start) {
+				t.Fatalf("Unexpected start time: route was started around %v, got %v", start, route.Start)
+			}
+			if route.LastActivity.IsZero() {
+				t.Fatalf("Expected LastActivity to be set, got %+v", route)
+			}
+			if route.Uptime == _EMPTY_ {
+				t.Fatalf("Expected Uptime to be set, it was not")
+			}
+			if route.Idle == _EMPTY_ {
+				t.Fatalf("Expected Idle to be set, it was not")
 			}
 
 			// Don't ask for subs, so there should not be any