github.com/zoomfoo/nomad@v0.8.5-0.20180907175415-f28fd3a1a056/nomad/heartbeat_test.go (about)

     1  package nomad
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  	"time"
     7  
     8  	memdb "github.com/hashicorp/go-memdb"
     9  	"github.com/hashicorp/net-rpc-msgpackrpc"
    10  	"github.com/hashicorp/nomad/nomad/mock"
    11  	"github.com/hashicorp/nomad/nomad/structs"
    12  	"github.com/hashicorp/nomad/testutil"
    13  	"github.com/stretchr/testify/require"
    14  )
    15  
    16  func TestHeartbeat_InitializeHeartbeatTimers(t *testing.T) {
    17  	t.Parallel()
    18  	s1 := TestServer(t, nil)
    19  	defer s1.Shutdown()
    20  	testutil.WaitForLeader(t, s1.RPC)
    21  
    22  	node := mock.Node()
    23  	state := s1.fsm.State()
    24  	err := state.UpsertNode(1, node)
    25  	if err != nil {
    26  		t.Fatalf("err: %v", err)
    27  	}
    28  
    29  	// Reset the heartbeat timers
    30  	err = s1.initializeHeartbeatTimers()
    31  	if err != nil {
    32  		t.Fatalf("err: %v", err)
    33  	}
    34  
    35  	// Check that we have a timer
    36  	_, ok := s1.heartbeatTimers[node.ID]
    37  	if !ok {
    38  		t.Fatalf("missing heartbeat timer")
    39  	}
    40  }
    41  
    42  func TestHeartbeat_ResetHeartbeatTimer(t *testing.T) {
    43  	t.Parallel()
    44  	s1 := TestServer(t, nil)
    45  	defer s1.Shutdown()
    46  	testutil.WaitForLeader(t, s1.RPC)
    47  
    48  	// Create a new timer
    49  	ttl, err := s1.resetHeartbeatTimer("test")
    50  	if err != nil {
    51  		t.Fatalf("err: %v", err)
    52  	}
    53  	if ttl < s1.config.MinHeartbeatTTL || ttl > 2*s1.config.MinHeartbeatTTL {
    54  		t.Fatalf("bad: %#v", ttl)
    55  	}
    56  
    57  	// Check that we have a timer
    58  	_, ok := s1.heartbeatTimers["test"]
    59  	if !ok {
    60  		t.Fatalf("missing heartbeat timer")
    61  	}
    62  }
    63  
    64  func TestHeartbeat_ResetHeartbeatTimer_Nonleader(t *testing.T) {
    65  	t.Parallel()
    66  	require := require.New(t)
    67  	s1 := TestServer(t, func(c *Config) {
    68  		c.BootstrapExpect = 3 // Won't become leader
    69  		c.DevDisableBootstrap = true
    70  	})
    71  	defer s1.Shutdown()
    72  
    73  	require.False(s1.IsLeader())
    74  
    75  	// Create a new timer
    76  	_, err := s1.resetHeartbeatTimer("test")
    77  	require.NotNil(err)
    78  	require.EqualError(err, heartbeatNotLeader)
    79  }
    80  
    81  func TestHeartbeat_ResetHeartbeatTimerLocked(t *testing.T) {
    82  	t.Parallel()
    83  	s1 := TestServer(t, nil)
    84  	defer s1.Shutdown()
    85  	testutil.WaitForLeader(t, s1.RPC)
    86  
    87  	s1.heartbeatTimersLock.Lock()
    88  	s1.resetHeartbeatTimerLocked("foo", 5*time.Millisecond)
    89  	s1.heartbeatTimersLock.Unlock()
    90  
    91  	if _, ok := s1.heartbeatTimers["foo"]; !ok {
    92  		t.Fatalf("missing timer")
    93  	}
    94  
    95  	time.Sleep(time.Duration(testutil.TestMultiplier()*10) * time.Millisecond)
    96  
    97  	if _, ok := s1.heartbeatTimers["foo"]; ok {
    98  		t.Fatalf("timer should be gone")
    99  	}
   100  }
   101  
   102  func TestHeartbeat_ResetHeartbeatTimerLocked_Renew(t *testing.T) {
   103  	t.Parallel()
   104  	s1 := TestServer(t, nil)
   105  	defer s1.Shutdown()
   106  	testutil.WaitForLeader(t, s1.RPC)
   107  
   108  	s1.heartbeatTimersLock.Lock()
   109  	s1.resetHeartbeatTimerLocked("foo", 30*time.Millisecond)
   110  	s1.heartbeatTimersLock.Unlock()
   111  
   112  	if _, ok := s1.heartbeatTimers["foo"]; !ok {
   113  		t.Fatalf("missing timer")
   114  	}
   115  
   116  	time.Sleep(2 * time.Millisecond)
   117  
   118  	// Renew the heartbeat
   119  	s1.heartbeatTimersLock.Lock()
   120  	s1.resetHeartbeatTimerLocked("foo", 30*time.Millisecond)
   121  	s1.heartbeatTimersLock.Unlock()
   122  	renew := time.Now()
   123  
   124  	// Watch for invalidation
   125  	for time.Now().Sub(renew) < time.Duration(testutil.TestMultiplier()*100)*time.Millisecond {
   126  		s1.heartbeatTimersLock.Lock()
   127  		_, ok := s1.heartbeatTimers["foo"]
   128  		s1.heartbeatTimersLock.Unlock()
   129  		if !ok {
   130  			end := time.Now()
   131  			if diff := end.Sub(renew); diff < 30*time.Millisecond {
   132  				t.Fatalf("early invalidate %v", diff)
   133  			}
   134  			return
   135  		}
   136  		time.Sleep(2 * time.Millisecond)
   137  	}
   138  	t.Fatalf("should have expired")
   139  }
   140  
   141  func TestHeartbeat_InvalidateHeartbeat(t *testing.T) {
   142  	t.Parallel()
   143  	require := require.New(t)
   144  	s1 := TestServer(t, nil)
   145  	defer s1.Shutdown()
   146  	testutil.WaitForLeader(t, s1.RPC)
   147  
   148  	// Create a node
   149  	node := mock.Node()
   150  	state := s1.fsm.State()
   151  	require.NoError(state.UpsertNode(1, node))
   152  
   153  	// This should cause a status update
   154  	s1.invalidateHeartbeat(node.ID)
   155  
   156  	// Check it is updated
   157  	ws := memdb.NewWatchSet()
   158  	out, err := state.NodeByID(ws, node.ID)
   159  	require.NoError(err)
   160  	require.True(out.TerminalStatus())
   161  	require.Len(out.Events, 2)
   162  	require.Equal(NodeHeartbeatEventMissed, out.Events[1].Message)
   163  }
   164  
   165  func TestHeartbeat_ClearHeartbeatTimer(t *testing.T) {
   166  	t.Parallel()
   167  	s1 := TestServer(t, nil)
   168  	defer s1.Shutdown()
   169  	testutil.WaitForLeader(t, s1.RPC)
   170  
   171  	s1.heartbeatTimersLock.Lock()
   172  	s1.resetHeartbeatTimerLocked("foo", 5*time.Millisecond)
   173  	s1.heartbeatTimersLock.Unlock()
   174  
   175  	err := s1.clearHeartbeatTimer("foo")
   176  	if err != nil {
   177  		t.Fatalf("err: %v", err)
   178  	}
   179  
   180  	if _, ok := s1.heartbeatTimers["foo"]; ok {
   181  		t.Fatalf("timer should be gone")
   182  	}
   183  }
   184  
   185  func TestHeartbeat_ClearAllHeartbeatTimers(t *testing.T) {
   186  	t.Parallel()
   187  	s1 := TestServer(t, nil)
   188  	defer s1.Shutdown()
   189  	testutil.WaitForLeader(t, s1.RPC)
   190  
   191  	s1.heartbeatTimersLock.Lock()
   192  	s1.resetHeartbeatTimerLocked("foo", 10*time.Millisecond)
   193  	s1.resetHeartbeatTimerLocked("bar", 10*time.Millisecond)
   194  	s1.resetHeartbeatTimerLocked("baz", 10*time.Millisecond)
   195  	s1.heartbeatTimersLock.Unlock()
   196  
   197  	err := s1.clearAllHeartbeatTimers()
   198  	if err != nil {
   199  		t.Fatalf("err: %v", err)
   200  	}
   201  
   202  	if len(s1.heartbeatTimers) != 0 {
   203  		t.Fatalf("timers should be gone")
   204  	}
   205  }
   206  
   207  func TestHeartbeat_Server_HeartbeatTTL_Failover(t *testing.T) {
   208  	t.Parallel()
   209  	s1 := TestServer(t, nil)
   210  	defer s1.Shutdown()
   211  
   212  	s2 := TestServer(t, func(c *Config) {
   213  		c.DevDisableBootstrap = true
   214  	})
   215  	defer s2.Shutdown()
   216  
   217  	s3 := TestServer(t, func(c *Config) {
   218  		c.DevDisableBootstrap = true
   219  	})
   220  	defer s3.Shutdown()
   221  	servers := []*Server{s1, s2, s3}
   222  	TestJoin(t, s1, s2, s3)
   223  
   224  	testutil.WaitForResult(func() (bool, error) {
   225  		peers, _ := s1.numPeers()
   226  		return peers == 3, nil
   227  	}, func(err error) {
   228  		t.Fatalf("should have 3 peers")
   229  	})
   230  
   231  	// Find the leader
   232  	var leader *Server
   233  	for _, s := range servers {
   234  		// Check that s.heartbeatTimers is empty
   235  		if len(s.heartbeatTimers) != 0 {
   236  			t.Fatalf("should have no heartbeatTimers")
   237  		}
   238  		// Find the leader too
   239  		if s.IsLeader() {
   240  			leader = s
   241  		}
   242  	}
   243  	if leader == nil {
   244  		t.Fatalf("Should have a leader")
   245  	}
   246  	codec := rpcClient(t, leader)
   247  
   248  	// Create the register request
   249  	node := mock.Node()
   250  	req := &structs.NodeRegisterRequest{
   251  		Node:         node,
   252  		WriteRequest: structs.WriteRequest{Region: "global"},
   253  	}
   254  
   255  	// Fetch the response
   256  	var resp structs.GenericResponse
   257  	if err := msgpackrpc.CallWithCodec(codec, "Node.Register", req, &resp); err != nil {
   258  		t.Fatalf("err: %v", err)
   259  	}
   260  
   261  	// Check that heartbeatTimers has the heartbeat ID
   262  	if _, ok := leader.heartbeatTimers[node.ID]; !ok {
   263  		t.Fatalf("missing heartbeat timer")
   264  	}
   265  
   266  	// Shutdown the leader!
   267  	leader.Shutdown()
   268  
   269  	// heartbeatTimers should be cleared on leader shutdown
   270  	testutil.WaitForResult(func() (bool, error) {
   271  		return len(leader.heartbeatTimers) == 0, nil
   272  	}, func(err error) {
   273  		t.Fatalf("heartbeat timers should be empty on the shutdown leader")
   274  	})
   275  
   276  	// Find the new leader
   277  	testutil.WaitForResult(func() (bool, error) {
   278  		leader = nil
   279  		for _, s := range servers {
   280  			if s.IsLeader() {
   281  				leader = s
   282  			}
   283  		}
   284  		if leader == nil {
   285  			return false, fmt.Errorf("Should have a new leader")
   286  		}
   287  
   288  		// Ensure heartbeat timer is restored
   289  		if _, ok := leader.heartbeatTimers[node.ID]; !ok {
   290  			return false, fmt.Errorf("missing heartbeat timer")
   291  		}
   292  
   293  		return true, nil
   294  	}, func(err error) {
   295  		t.Fatalf("err: %s", err)
   296  	})
   297  }