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