github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/m3em/node/heartbeat_test.go (about)

     1  // Copyright (c) 2017 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package node
    22  
    23  import (
    24  	"sync"
    25  	"testing"
    26  	"time"
    27  
    28  	hb "github.com/m3db/m3/src/m3em/generated/proto/heartbeat"
    29  	"github.com/m3db/m3/src/x/instrument"
    30  
    31  	"github.com/stretchr/testify/require"
    32  	context "golang.org/x/net/context"
    33  )
    34  
    35  var (
    36  	testEndpoint = "a.b.c.d:12345"
    37  )
    38  
    39  func newTestHeartbeatOpts() HeartbeatOptions {
    40  	return NewHeartbeatOptions().
    41  		SetCheckInterval(10 * time.Millisecond).
    42  		SetInterval(20 * time.Millisecond).
    43  		SetTimeout(100 * time.Millisecond)
    44  }
    45  
    46  func newTestListener(t *testing.T) *listener {
    47  	return &listener{
    48  		onProcessTerminate: func(_ ServiceNode, desc string) { require.Fail(t, "onProcessTerminate invoked %s", desc) },
    49  		onHeartbeatTimeout: func(_ ServiceNode, ts time.Time) { require.Fail(t, "onHeartbeatTimeout invoked %s", ts.String()) },
    50  		onOverwrite:        func(_ ServiceNode, desc string) { require.Fail(t, "onOverwrite invoked %s", desc) },
    51  	}
    52  }
    53  
    54  func TestHeartbeaterSimple(t *testing.T) {
    55  	var (
    56  		lg       = newListenerGroup(nil)
    57  		opts     = newTestHeartbeatOpts()
    58  		iopts    = instrument.NewOptions()
    59  		hbServer = newHeartbeater(lg, opts, iopts)
    60  	)
    61  	require.NoError(t, hbServer.start())
    62  
    63  	hbServer.Heartbeat(context.Background(),
    64  		&hb.HeartbeatRequest{
    65  			Code:           hb.HeartbeatCode_HEALTHY,
    66  			ProcessRunning: false,
    67  		})
    68  	time.Sleep(10 * time.Millisecond) // to yield to any pending go routines
    69  	hbServer.stop()
    70  }
    71  
    72  func TestHeartbeatingUnknownCode(t *testing.T) {
    73  	var (
    74  		lg       = newListenerGroup(nil)
    75  		opts     = newTestHeartbeatOpts()
    76  		iopts    = instrument.NewOptions()
    77  		hbServer = newHeartbeater(lg, opts, iopts)
    78  	)
    79  	require.NoError(t, hbServer.start())
    80  
    81  	_, err := hbServer.Heartbeat(context.Background(),
    82  		&hb.HeartbeatRequest{
    83  			Code:           hb.HeartbeatCode_UNKNOWN,
    84  			ProcessRunning: false,
    85  		})
    86  	require.Error(t, err)
    87  	time.Sleep(10 * time.Millisecond) // to yield to any pending go routines
    88  	hbServer.stop()
    89  }
    90  
    91  func TestHeartbeatingProcessTermination(t *testing.T) {
    92  	var (
    93  		lg    = newListenerGroup(nil)
    94  		opts  = newTestHeartbeatOpts()
    95  		iopts = instrument.NewOptions()
    96  	)
    97  
    98  	var (
    99  		lock              sync.Mutex
   100  		processTerminated = false
   101  		lnr               = newTestListener(t)
   102  	)
   103  	lnr.onProcessTerminate = func(ServiceNode, string) {
   104  		lock.Lock()
   105  		processTerminated = true
   106  		lock.Unlock()
   107  	}
   108  	lg.add(lnr)
   109  
   110  	hbServer := newHeartbeater(lg, opts, iopts)
   111  	require.NoError(t, hbServer.start())
   112  	_, err := hbServer.Heartbeat(context.Background(),
   113  		&hb.HeartbeatRequest{
   114  			Code: hb.HeartbeatCode_PROCESS_TERMINATION,
   115  		})
   116  	require.NoError(t, err)
   117  	time.Sleep(10 * time.Millisecond) // to yield to any pending go routines
   118  	hbServer.stop()
   119  
   120  	lock.Lock()
   121  	defer lock.Unlock()
   122  	require.True(t, processTerminated)
   123  }
   124  
   125  func TestHeartbeatingOverwrite(t *testing.T) {
   126  	var (
   127  		lg    = newListenerGroup(nil)
   128  		opts  = newTestHeartbeatOpts()
   129  		iopts = instrument.NewOptions()
   130  	)
   131  
   132  	var (
   133  		lock        sync.Mutex
   134  		overwritten = false
   135  		lnr         = newTestListener(t)
   136  	)
   137  	lnr.onOverwrite = func(ServiceNode, string) {
   138  		lock.Lock()
   139  		overwritten = true
   140  		lock.Unlock()
   141  	}
   142  	lg.add(lnr)
   143  
   144  	hbServer := newHeartbeater(lg, opts, iopts)
   145  	require.NoError(t, hbServer.start())
   146  	_, err := hbServer.Heartbeat(context.Background(),
   147  		&hb.HeartbeatRequest{
   148  			Code: hb.HeartbeatCode_OVERWRITTEN,
   149  		})
   150  	require.NoError(t, err)
   151  	time.Sleep(10 * time.Millisecond) // to yield to any pending go routines
   152  	hbServer.stop()
   153  
   154  	lock.Lock()
   155  	defer lock.Unlock()
   156  	require.True(t, overwritten)
   157  }
   158  
   159  func TestHeartbeatingTimeout(t *testing.T) {
   160  	var (
   161  		now       = time.Now()
   162  		callCount = 0
   163  		nowFn     = func() time.Time {
   164  			callCount++
   165  			if callCount == 1 {
   166  				return now
   167  			}
   168  			return now.Add(time.Hour)
   169  		}
   170  		lg    = newListenerGroup(nil)
   171  		opts  = newTestHeartbeatOpts().SetNowFn(nowFn)
   172  		iopts = instrument.NewOptions()
   173  	)
   174  
   175  	var (
   176  		lock     sync.Mutex
   177  		timedout = false
   178  		lnr      = newTestListener(t)
   179  	)
   180  	lnr.onHeartbeatTimeout = func(_ ServiceNode, ts time.Time) {
   181  		lock.Lock()
   182  		timedout = true
   183  		lock.Unlock()
   184  	}
   185  	lg.add(lnr)
   186  
   187  	hbServer := newHeartbeater(lg, opts, iopts)
   188  	require.NoError(t, hbServer.start())
   189  	_, err := hbServer.Heartbeat(context.Background(),
   190  		&hb.HeartbeatRequest{
   191  			Code: hb.HeartbeatCode_HEALTHY,
   192  		})
   193  	require.NoError(t, err)
   194  	time.Sleep(100 * time.Millisecond)
   195  	hbServer.stop()
   196  
   197  	lock.Lock()
   198  	defer lock.Unlock()
   199  	require.True(t, timedout)
   200  }
   201  
   202  func TestHeartbeatRouterValidRoute(t *testing.T) {
   203  	var (
   204  		lg     = newListenerGroup(nil)
   205  		opts   = newTestHeartbeatOpts()
   206  		iopts  = instrument.NewOptions()
   207  		router = NewHeartbeatRouter(testEndpoint)
   208  	)
   209  	require.Equal(t, testEndpoint, router.Endpoint())
   210  
   211  	var (
   212  		lock        sync.Mutex
   213  		overwritten = false
   214  		lnr         = newTestListener(t)
   215  	)
   216  	lnr.onOverwrite = func(ServiceNode, string) {
   217  		lock.Lock()
   218  		overwritten = true
   219  		lock.Unlock()
   220  	}
   221  	lg.add(lnr)
   222  	hbServer := newHeartbeater(lg, opts, iopts)
   223  	require.NoError(t, hbServer.start())
   224  
   225  	testUUID := "123456789"
   226  	require.NoError(t, router.Register(testUUID, hbServer))
   227  	require.Error(t, router.Register(testUUID, nil))
   228  
   229  	_, err := router.Heartbeat(context.Background(),
   230  		&hb.HeartbeatRequest{
   231  			OperatorUuid: testUUID,
   232  			Code:         hb.HeartbeatCode_OVERWRITTEN,
   233  		})
   234  	require.NoError(t, err)
   235  	time.Sleep(10 * time.Millisecond) // to yield to any pending go routines
   236  	hbServer.stop()
   237  
   238  	lock.Lock()
   239  	defer lock.Unlock()
   240  	require.True(t, overwritten)
   241  }
   242  
   243  func TestHeartbeatRouterDeregister(t *testing.T) {
   244  	var (
   245  		lg     = newListenerGroup(nil)
   246  		opts   = newTestHeartbeatOpts()
   247  		iopts  = instrument.NewOptions()
   248  		router = NewHeartbeatRouter(testEndpoint)
   249  	)
   250  	require.Equal(t, testEndpoint, router.Endpoint())
   251  	hbServer := newHeartbeater(lg, opts, iopts)
   252  	testUUID := "123456789"
   253  
   254  	require.NoError(t, router.Register(testUUID, hbServer))
   255  	_, err := router.Heartbeat(context.Background(),
   256  		&hb.HeartbeatRequest{
   257  			OperatorUuid: string(testUUID),
   258  			Code:         hb.HeartbeatCode_OVERWRITTEN,
   259  		})
   260  	require.NoError(t, err)
   261  
   262  	require.NoError(t, router.Deregister(testUUID))
   263  	_, err = router.Heartbeat(context.Background(),
   264  		&hb.HeartbeatRequest{
   265  			OperatorUuid: string(testUUID),
   266  			Code:         hb.HeartbeatCode_OVERWRITTEN,
   267  		})
   268  	require.Error(t, err)
   269  }
   270  
   271  func TestHeartbeatRouterInvalidRoute(t *testing.T) {
   272  	var (
   273  		router   = NewHeartbeatRouter(testEndpoint)
   274  		testUUID = "123456789"
   275  	)
   276  	_, err := router.Heartbeat(context.Background(),
   277  		&hb.HeartbeatRequest{
   278  			OperatorUuid: testUUID,
   279  			Code:         hb.HeartbeatCode_OVERWRITTEN,
   280  		})
   281  	require.Error(t, err)
   282  }