github.com/m3db/m3@v1.5.0/src/dbnode/storage/block/lease_test.go (about)

     1  // Copyright (c) 2019 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 block
    22  
    23  import (
    24  	"errors"
    25  	"sync"
    26  	"testing"
    27  	"time"
    28  
    29  	"github.com/m3db/m3/src/x/ident"
    30  	xtest "github.com/m3db/m3/src/x/test"
    31  	xtime "github.com/m3db/m3/src/x/time"
    32  
    33  	"github.com/golang/mock/gomock"
    34  	"github.com/stretchr/testify/require"
    35  )
    36  
    37  func TestRegisterLeaser(t *testing.T) {
    38  	ctrl := xtest.NewController(t)
    39  	defer ctrl.Finish()
    40  
    41  	var (
    42  		leaser   = NewMockLeaser(ctrl)
    43  		leaseMgr = NewLeaseManager(nil)
    44  	)
    45  
    46  	require.NoError(t, leaseMgr.RegisterLeaser(leaser))
    47  	require.Equal(t, errLeaserAlreadyRegistered, leaseMgr.RegisterLeaser(leaser))
    48  }
    49  
    50  func TestUnregisterLeaser(t *testing.T) {
    51  	ctrl := xtest.NewController(t)
    52  	defer ctrl.Finish()
    53  
    54  	var (
    55  		leaser1  = NewMockLeaser(ctrl)
    56  		leaser2  = NewMockLeaser(ctrl)
    57  		leaseMgr = NewLeaseManager(nil)
    58  	)
    59  
    60  	// Cant unregister if not registered.
    61  	require.Equal(t, errLeaserNotRegistered, leaseMgr.UnregisterLeaser(leaser1))
    62  	require.Equal(t, errLeaserNotRegistered, leaseMgr.UnregisterLeaser(leaser2))
    63  
    64  	// Register.
    65  	require.NoError(t, leaseMgr.RegisterLeaser(leaser1))
    66  	require.NoError(t, leaseMgr.RegisterLeaser(leaser2))
    67  
    68  	// Ensure registered.
    69  	require.Equal(t, errLeaserAlreadyRegistered, leaseMgr.RegisterLeaser(leaser1))
    70  	require.Equal(t, errLeaserAlreadyRegistered, leaseMgr.RegisterLeaser(leaser2))
    71  
    72  	// Ensure unregistering works.
    73  	require.NoError(t, leaseMgr.UnregisterLeaser(leaser1))
    74  	require.NoError(t, leaseMgr.RegisterLeaser(leaser1))
    75  
    76  	// Ensure unregistering leaser1 does not unregister leaser2.
    77  	require.Equal(t, errLeaserAlreadyRegistered, leaseMgr.RegisterLeaser(leaser2))
    78  }
    79  
    80  func TestOpenLease(t *testing.T) {
    81  	ctrl := xtest.NewController(t)
    82  	defer ctrl.Finish()
    83  
    84  	var (
    85  		leaser    = NewMockLeaser(ctrl)
    86  		verifier  = NewMockLeaseVerifier(ctrl)
    87  		leaseMgr  = NewLeaseManager(verifier)
    88  		leaseDesc = LeaseDescriptor{
    89  			Namespace:  ident.StringID("test-ns"),
    90  			Shard:      1,
    91  			BlockStart: xtime.Now().Truncate(2 * time.Hour),
    92  		}
    93  		leaseState = LeaseState{
    94  			Volume: 1,
    95  		}
    96  	)
    97  	verifier.EXPECT().VerifyLease(leaseDesc, leaseState)
    98  
    99  	require.Equal(t, errLeaserNotRegistered, leaseMgr.OpenLease(leaser, leaseDesc, leaseState))
   100  	require.NoError(t, leaseMgr.RegisterLeaser(leaser))
   101  	require.NoError(t, leaseMgr.OpenLease(leaser, leaseDesc, leaseState))
   102  }
   103  
   104  func TestOpenLeaseErrorIfNoVerifier(t *testing.T) {
   105  	ctrl := xtest.NewController(t)
   106  	defer ctrl.Finish()
   107  
   108  	var (
   109  		leaser    = NewMockLeaser(ctrl)
   110  		leaseMgr  = NewLeaseManager(nil)
   111  		leaseDesc = LeaseDescriptor{
   112  			Namespace:  ident.StringID("test-ns"),
   113  			Shard:      1,
   114  			BlockStart: xtime.Now().Truncate(2 * time.Hour),
   115  		}
   116  		leaseState = LeaseState{
   117  			Volume: 1,
   118  		}
   119  	)
   120  
   121  	require.NoError(t, leaseMgr.RegisterLeaser(leaser))
   122  	require.Equal(t, errOpenLeaseVerifierNotSet, leaseMgr.OpenLease(leaser, leaseDesc, leaseState))
   123  
   124  	verifier := NewMockLeaseVerifier(ctrl)
   125  	verifier.EXPECT().VerifyLease(leaseDesc, leaseState)
   126  	require.NoError(t, leaseMgr.SetLeaseVerifier(verifier))
   127  	require.NoError(t, leaseMgr.OpenLease(leaser, leaseDesc, leaseState))
   128  }
   129  
   130  func TestOpenLatestLease(t *testing.T) {
   131  	ctrl := xtest.NewController(t)
   132  	defer ctrl.Finish()
   133  
   134  	var (
   135  		leaser    = NewMockLeaser(ctrl)
   136  		verifier  = NewMockLeaseVerifier(ctrl)
   137  		leaseMgr  = NewLeaseManager(verifier)
   138  		leaseDesc = LeaseDescriptor{
   139  			Namespace:  ident.StringID("test-ns"),
   140  			Shard:      1,
   141  			BlockStart: xtime.Now().Truncate(2 * time.Hour),
   142  		}
   143  		leaseState = LeaseState{
   144  			Volume: 1,
   145  		}
   146  	)
   147  	verifier.EXPECT().LatestState(leaseDesc).Return(leaseState, nil)
   148  
   149  	require.Equal(t, errLeaserNotRegistered, leaseMgr.OpenLease(leaser, leaseDesc, leaseState))
   150  	require.NoError(t, leaseMgr.RegisterLeaser(leaser))
   151  	latestState, err := leaseMgr.OpenLatestLease(leaser, leaseDesc)
   152  	require.NoError(t, err)
   153  	require.Equal(t, leaseState, latestState)
   154  }
   155  
   156  func TestOpenLatestLeaseErrorIfNoVerifier(t *testing.T) {
   157  	ctrl := xtest.NewController(t)
   158  	defer ctrl.Finish()
   159  
   160  	var (
   161  		leaser    = NewMockLeaser(ctrl)
   162  		leaseMgr  = NewLeaseManager(nil)
   163  		leaseDesc = LeaseDescriptor{
   164  			Namespace:  ident.StringID("test-ns"),
   165  			Shard:      1,
   166  			BlockStart: xtime.Now().Truncate(2 * time.Hour),
   167  		}
   168  		leaseState = LeaseState{
   169  			Volume: 1,
   170  		}
   171  	)
   172  	require.NoError(t, leaseMgr.RegisterLeaser(leaser))
   173  	_, err := leaseMgr.OpenLatestLease(leaser, leaseDesc)
   174  	require.Equal(t, errOpenLeaseVerifierNotSet, err)
   175  
   176  	verifier := NewMockLeaseVerifier(ctrl)
   177  	verifier.EXPECT().LatestState(leaseDesc).Return(leaseState, nil)
   178  	require.NoError(t, leaseMgr.SetLeaseVerifier(verifier))
   179  	latestState, err := leaseMgr.OpenLatestLease(leaser, leaseDesc)
   180  	require.NoError(t, err)
   181  	require.Equal(t, leaseState, latestState)
   182  }
   183  
   184  func TestUpdateOpenLeases(t *testing.T) {
   185  	ctrl := xtest.NewController(t)
   186  	defer ctrl.Finish()
   187  
   188  	var (
   189  		verifier = NewMockLeaseVerifier(ctrl)
   190  		leaseMgr = NewLeaseManager(verifier)
   191  
   192  		leaseDesc = LeaseDescriptor{
   193  			Namespace:  ident.StringID("test-ns"),
   194  			Shard:      1,
   195  			BlockStart: xtime.Now().Truncate(2 * time.Hour),
   196  		}
   197  		leaseState = LeaseState{
   198  			Volume: 1,
   199  		}
   200  		leasers = []*MockLeaser{NewMockLeaser(ctrl), NewMockLeaser(ctrl)}
   201  	)
   202  	verifier.EXPECT().VerifyLease(leaseDesc, leaseState).Times(len(leasers))
   203  
   204  	// Expect that the leasers return NoOpenLease the first time to simulate the situation
   205  	// where they don't have an open lease on the LeaseDescriptor that should be updated.
   206  	leasers[0].EXPECT().
   207  		UpdateOpenLease(leaseDesc, leaseState).
   208  		Return(NoOpenLease, nil)
   209  	leasers[1].EXPECT().
   210  		UpdateOpenLease(leaseDesc, leaseState).
   211  		Return(NoOpenLease, nil)
   212  
   213  	// Expect that the leasers return UpdateOpenLease the second time to simulate the situation
   214  	// where they do have an open lease on the LeaseDescriptor that should be updated.
   215  	leasers[0].EXPECT().
   216  		UpdateOpenLease(leaseDesc, leaseState).
   217  		Return(UpdateOpenLease, nil)
   218  	leasers[1].EXPECT().
   219  		UpdateOpenLease(leaseDesc, leaseState).
   220  		Return(UpdateOpenLease, nil)
   221  
   222  	// Expect that the first leaser returns an error the third time to simulate the situation
   223  	// where one of the leasers returns an error and make sure that UpdateOpenLeases() bails out
   224  	// early and returns an error.
   225  	leasers[0].EXPECT().
   226  		UpdateOpenLease(leaseDesc, leaseState).
   227  		Return(UpdateOpenLeaseResult(0), errors.New("some-error"))
   228  
   229  	for _, leaser := range leasers {
   230  		require.NoError(t, leaseMgr.RegisterLeaser(leaser))
   231  	}
   232  
   233  	// First time the leasers will return that they didn't have an open lease.
   234  	result, err := leaseMgr.UpdateOpenLeases(leaseDesc, leaseState)
   235  	require.NoError(t, err)
   236  	require.Equal(t, result, UpdateLeasesResult{LeasersNoOpenLease: 2})
   237  
   238  	for _, leaser := range leasers {
   239  		err := leaseMgr.OpenLease(leaser, leaseDesc, leaseState)
   240  		require.NoError(t, err)
   241  	}
   242  
   243  	// Second time the leasers will return that they did have an open lease.
   244  	result, err = leaseMgr.UpdateOpenLeases(leaseDesc, leaseState)
   245  	require.NoError(t, err)
   246  	require.Equal(t, result, UpdateLeasesResult{LeasersUpdatedLease: 2})
   247  
   248  	// Third time the first leaser will return an error.
   249  	result, err = leaseMgr.UpdateOpenLeases(leaseDesc, leaseState)
   250  	require.Error(t, err)
   251  }
   252  
   253  func TestUpdateOpenLeasesErrorIfNoVerifier(t *testing.T) {
   254  	ctrl := xtest.NewController(t)
   255  	defer ctrl.Finish()
   256  
   257  	var (
   258  		leaseMgr  = NewLeaseManager(nil)
   259  		leaseDesc = LeaseDescriptor{
   260  			Namespace:  ident.StringID("test-ns"),
   261  			Shard:      1,
   262  			BlockStart: xtime.Now().Truncate(2 * time.Hour),
   263  		}
   264  		leaseState = LeaseState{
   265  			Volume: 1,
   266  		}
   267  		leasers = []*MockLeaser{NewMockLeaser(ctrl), NewMockLeaser(ctrl)}
   268  	)
   269  
   270  	for _, leaser := range leasers {
   271  		require.NoError(t, leaseMgr.RegisterLeaser(leaser))
   272  	}
   273  
   274  	// First time the leasers will return that they didn't have an open lease.
   275  	_, err := leaseMgr.UpdateOpenLeases(leaseDesc, leaseState)
   276  	require.Equal(t, errUpdateOpenLeasesVerifierNotSet, err)
   277  }
   278  
   279  // TestUpdateOpenLeasesDoesNotDeadlockIfLeasersCallsBack verifies that a deadlock does
   280  // not occur if a Leaser calls OpenLease or OpenLatestLease while the LeaseManager is
   281  // waiting for a call to UpdateOpenLease() to complete on the same leaser.
   282  func TestUpdateOpenLeasesDoesNotDeadlockIfLeasersCallsBack(t *testing.T) {
   283  	ctrl := xtest.NewController(t)
   284  	defer ctrl.Finish()
   285  
   286  	var (
   287  		leaser   = NewMockLeaser(ctrl)
   288  		verifier = NewMockLeaseVerifier(ctrl)
   289  		leaseMgr = NewLeaseManager(verifier)
   290  	)
   291  	verifier.EXPECT().VerifyLease(gomock.Any(), gomock.Any()).AnyTimes()
   292  	verifier.EXPECT().LatestState(gomock.Any()).AnyTimes()
   293  	leaser.EXPECT().UpdateOpenLease(gomock.Any(), gomock.Any()).Do(func(_ LeaseDescriptor, _ LeaseState) {
   294  		require.NoError(t, leaseMgr.OpenLease(leaser, LeaseDescriptor{}, LeaseState{}))
   295  		_, err := leaseMgr.OpenLatestLease(leaser, LeaseDescriptor{})
   296  		require.NoError(t, err)
   297  	})
   298  
   299  	require.NoError(t, leaseMgr.RegisterLeaser(leaser))
   300  	_, err := leaseMgr.UpdateOpenLeases(LeaseDescriptor{}, LeaseState{})
   301  	require.NoError(t, err)
   302  }
   303  
   304  // TestUpdateOpenLeasesConcurrentNotAllowed verifies that concurrent calls to UpdateOpenLeases()
   305  // with the same descriptor are not allowed which ensures that leasers receive all
   306  // updates and in the correct order.
   307  func TestUpdateOpenLeasesConcurrentNotAllowed(t *testing.T) {
   308  	ctrl := xtest.NewController(t)
   309  	defer ctrl.Finish()
   310  
   311  	var (
   312  		leaser   = NewMockLeaser(ctrl)
   313  		verifier = NewMockLeaseVerifier(ctrl)
   314  		leaseMgr = NewLeaseManager(verifier)
   315  		wg       sync.WaitGroup
   316  
   317  		descriptor LeaseDescriptor
   318  	)
   319  
   320  	wg.Add(1)
   321  	leaser.EXPECT().UpdateOpenLease(descriptor, gomock.Any()).Do(func(_ LeaseDescriptor, _ LeaseState) {
   322  		go func() {
   323  			defer wg.Done()
   324  			_, err := leaseMgr.UpdateOpenLeases(descriptor, LeaseState{})
   325  			require.Equal(t, errConcurrentUpdateOpenLeases, err)
   326  		}()
   327  		wg.Wait()
   328  	})
   329  
   330  	require.NoError(t, leaseMgr.RegisterLeaser(leaser))
   331  	_, err := leaseMgr.UpdateOpenLeases(descriptor, LeaseState{})
   332  	require.NoError(t, err)
   333  }
   334  
   335  func TestUpdateOpenLeasesDifferentDescriptorsConcurrent(t *testing.T) {
   336  	ctrl := xtest.NewController(t)
   337  	defer ctrl.Finish()
   338  
   339  	var (
   340  		leaser   = NewMockLeaser(ctrl)
   341  		verifier = NewMockLeaseVerifier(ctrl)
   342  		leaseMgr = NewLeaseManager(verifier)
   343  		wg       sync.WaitGroup
   344  
   345  		blockStart  = xtime.Now().Truncate(time.Hour)
   346  		descriptor1 = LeaseDescriptor{Namespace: ident.StringID("ns"), BlockStart: blockStart, Shard: 1}
   347  		descriptor2 = LeaseDescriptor{Namespace: ident.StringID("ns"), BlockStart: blockStart, Shard: 2}
   348  	)
   349  
   350  	wg.Add(1)
   351  	leaser.EXPECT().UpdateOpenLease(descriptor1, gomock.Any()).Do(func(descriptor LeaseDescriptor, _ LeaseState) {
   352  		go func() {
   353  			defer wg.Done()
   354  			_, err := leaseMgr.UpdateOpenLeases(descriptor2, LeaseState{})
   355  			require.NoError(t, err)
   356  		}()
   357  		wg.Wait()
   358  	})
   359  	leaser.EXPECT().UpdateOpenLease(descriptor2, gomock.Any())
   360  
   361  	require.NoError(t, leaseMgr.RegisterLeaser(leaser))
   362  	_, err := leaseMgr.UpdateOpenLeases(descriptor1, LeaseState{})
   363  	require.NoError(t, err)
   364  }
   365  
   366  // TestUpdateOpenLeasesConcurrencyTest spins up a number of goroutines to call UpdateOpenLeases(),
   367  // OpenLease(), and OpenLatestLease() concurrently and ensure there are no deadlocks.
   368  func TestUpdateOpenLeasesConcurrencyTest(t *testing.T) {
   369  	ctrl := xtest.NewController(t)
   370  	defer ctrl.Finish()
   371  
   372  	var (
   373  		leaser     = NewMockLeaser(ctrl)
   374  		verifier   = NewMockLeaseVerifier(ctrl)
   375  		leaseMgr   = NewLeaseManager(verifier)
   376  		wg         sync.WaitGroup
   377  		doneCh     = make(chan struct{}, 1)
   378  		numWorkers = 1
   379  	)
   380  	verifier.EXPECT().VerifyLease(gomock.Any(), gomock.Any()).AnyTimes()
   381  	verifier.EXPECT().LatestState(gomock.Any()).AnyTimes()
   382  	leaser.EXPECT().UpdateOpenLease(gomock.Any(), gomock.Any()).Do(func(_ LeaseDescriptor, _ LeaseState) {
   383  		// Call back into the LeaseManager from the Leaser on each call to UpdateOpenLease to ensure there
   384  		// are no deadlocks there.
   385  		require.NoError(t, leaseMgr.OpenLease(leaser, LeaseDescriptor{}, LeaseState{}))
   386  		_, err := leaseMgr.OpenLatestLease(leaser, LeaseDescriptor{})
   387  		require.NoError(t, err)
   388  	}).AnyTimes()
   389  	require.NoError(t, leaseMgr.RegisterLeaser(leaser))
   390  
   391  	// One goroutine calling UpdateOpenLeases in a loop.
   392  	wg.Add(1)
   393  	go func() {
   394  		for {
   395  			select {
   396  			case <-doneCh:
   397  				wg.Done()
   398  				return
   399  			default:
   400  				_, err := leaseMgr.UpdateOpenLeases(LeaseDescriptor{}, LeaseState{})
   401  				if err != nil {
   402  					panic(err)
   403  				}
   404  			}
   405  		}
   406  	}()
   407  
   408  	// Several goroutines calling OpenLease and OpenLatestLease.
   409  	for i := 0; i < numWorkers; i++ {
   410  		wg.Add(2)
   411  		go func() {
   412  			for {
   413  				select {
   414  				case <-doneCh:
   415  					wg.Done()
   416  					return
   417  				default:
   418  					if err := leaseMgr.OpenLease(leaser, LeaseDescriptor{}, LeaseState{}); err != nil {
   419  						panic(err)
   420  					}
   421  				}
   422  			}
   423  		}()
   424  
   425  		go func() {
   426  			for {
   427  				select {
   428  				case <-doneCh:
   429  					wg.Done()
   430  					return
   431  				default:
   432  					if _, err := leaseMgr.OpenLatestLease(leaser, LeaseDescriptor{}); err != nil {
   433  						panic(err)
   434  					}
   435  				}
   436  			}
   437  		}()
   438  	}
   439  
   440  	time.Sleep(100 * time.Millisecond)
   441  	close(doneCh)
   442  	wg.Wait()
   443  }