github.com/matrixorigin/matrixone@v0.7.0/pkg/logservice/hakeeper_client_test.go (about)

     1  // Copyright 2021 - 2022 Matrix Origin
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package logservice
    16  
    17  import (
    18  	"context"
    19  	"sync"
    20  	"testing"
    21  	"time"
    22  
    23  	"github.com/google/uuid"
    24  	"github.com/lni/dragonboat/v4"
    25  	"github.com/lni/goutils/leaktest"
    26  	"github.com/lni/vfs"
    27  	"github.com/stretchr/testify/assert"
    28  	"github.com/stretchr/testify/require"
    29  
    30  	"github.com/matrixorigin/matrixone/pkg/common/moerr"
    31  	"github.com/matrixorigin/matrixone/pkg/common/morpc"
    32  	"github.com/matrixorigin/matrixone/pkg/hakeeper"
    33  	pb "github.com/matrixorigin/matrixone/pkg/pb/logservice"
    34  	"github.com/matrixorigin/matrixone/pkg/testutil"
    35  )
    36  
    37  func TestHAKeeperClientConfigIsValidated(t *testing.T) {
    38  	cfg := HAKeeperClientConfig{}
    39  	cc1, err := NewCNHAKeeperClient(context.TODO(), cfg)
    40  	assert.Nil(t, cc1)
    41  	assert.True(t, moerr.IsMoErrCode(err, moerr.ErrBadConfig))
    42  	cc2, err := NewDNHAKeeperClient(context.TODO(), cfg)
    43  	assert.Nil(t, cc2)
    44  	assert.True(t, moerr.IsMoErrCode(err, moerr.ErrBadConfig))
    45  	cc3, err := NewLogHAKeeperClient(context.TODO(), cfg)
    46  	assert.Nil(t, cc3)
    47  	assert.True(t, moerr.IsMoErrCode(err, moerr.ErrBadConfig))
    48  }
    49  
    50  func TestHAKeeperClientsCanBeCreated(t *testing.T) {
    51  	fn := func(t *testing.T, s *Service) {
    52  		cfg := HAKeeperClientConfig{
    53  			ServiceAddresses: []string{testServiceAddress},
    54  		}
    55  		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
    56  		defer cancel()
    57  		c1, err := NewCNHAKeeperClient(ctx, cfg)
    58  		require.NoError(t, err)
    59  		assert.NoError(t, c1.Close())
    60  		c2, err := NewDNHAKeeperClient(ctx, cfg)
    61  		assert.NoError(t, err)
    62  		assert.NoError(t, c2.Close())
    63  		c3, err := NewLogHAKeeperClient(ctx, cfg)
    64  		assert.NoError(t, err)
    65  		assert.NoError(t, c3.Close())
    66  	}
    67  	runServiceTest(t, true, true, fn)
    68  }
    69  
    70  func TestHAKeeperClientCanNotConnectToNonHAKeeperNode(t *testing.T) {
    71  	fn := func(t *testing.T, s *Service) {
    72  		cfg := HAKeeperClientConfig{
    73  			ServiceAddresses: []string{testServiceAddress},
    74  		}
    75  		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
    76  		defer cancel()
    77  		_, err := NewCNHAKeeperClient(ctx, cfg)
    78  		require.True(t, moerr.IsMoErrCode(err, moerr.ErrNoHAKeeper))
    79  		_, err = NewDNHAKeeperClient(ctx, cfg)
    80  		require.True(t, moerr.IsMoErrCode(err, moerr.ErrNoHAKeeper))
    81  		_, err = NewLogHAKeeperClient(ctx, cfg)
    82  		require.True(t, moerr.IsMoErrCode(err, moerr.ErrNoHAKeeper))
    83  	}
    84  	runServiceTest(t, false, true, fn)
    85  }
    86  
    87  func TestHAKeeperClientConnectByReverseProxy(t *testing.T) {
    88  	fn := func(t *testing.T, s *Service) {
    89  		done := false
    90  		for i := 0; i < 1000; i++ {
    91  			si, ok, err := GetShardInfo(testServiceAddress, hakeeper.DefaultHAKeeperShardID)
    92  			if err != nil || !ok {
    93  				time.Sleep(10 * time.Millisecond)
    94  				continue
    95  			}
    96  			done = true
    97  			require.NoError(t, err)
    98  			assert.True(t, ok)
    99  			assert.Equal(t, uint64(1), si.ReplicaID)
   100  			addr, ok := si.Replicas[si.ReplicaID]
   101  			assert.True(t, ok)
   102  			assert.Equal(t, testServiceAddress, addr)
   103  			break
   104  		}
   105  		if !done {
   106  			t.Fatalf("failed to get shard info")
   107  		}
   108  		// now shard info can be queried
   109  		cfg := HAKeeperClientConfig{
   110  			ServiceAddresses: []string{"localhost:53033"}, // obvious not reachable
   111  			DiscoveryAddress: testServiceAddress,
   112  		}
   113  		ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
   114  		defer cancel()
   115  		c, err := NewLogHAKeeperClient(ctx, cfg)
   116  		require.NoError(t, err)
   117  		defer func() {
   118  			assert.NoError(t, c.Close())
   119  		}()
   120  
   121  		hb := s.store.getHeartbeatMessage()
   122  		cb, err := c.SendLogHeartbeat(ctx, hb)
   123  		require.NoError(t, err)
   124  		assert.Equal(t, 0, len(cb.Commands))
   125  
   126  		sc := pb.ScheduleCommand{
   127  			UUID:        s.ID(),
   128  			ServiceType: pb.DNService,
   129  			ShutdownStore: &pb.ShutdownStore{
   130  				StoreID: "hello world",
   131  			},
   132  		}
   133  		require.NoError(t, s.store.addScheduleCommands(ctx, 0, []pb.ScheduleCommand{sc}))
   134  		cb, err = c.SendLogHeartbeat(ctx, hb)
   135  		require.NoError(t, err)
   136  		require.Equal(t, 1, len(cb.Commands))
   137  		require.Equal(t, sc, cb.Commands[0])
   138  	}
   139  	runServiceTest(t, true, true, fn)
   140  }
   141  
   142  func TestHAKeeperClientSendCNHeartbeat(t *testing.T) {
   143  	fn := func(t *testing.T, s *Service) {
   144  		cfg := HAKeeperClientConfig{
   145  			ServiceAddresses: []string{testServiceAddress},
   146  		}
   147  		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
   148  		defer cancel()
   149  		c1, err := NewCNHAKeeperClient(ctx, cfg)
   150  		require.NoError(t, err)
   151  		defer func() {
   152  			assert.NoError(t, c1.Close())
   153  		}()
   154  
   155  		// should be transparently handled
   156  		cc := c1.(*managedHAKeeperClient)
   157  		assert.NoError(t, cc.mu.client.close())
   158  		cc.mu.client = nil
   159  
   160  		hb := pb.CNStoreHeartbeat{
   161  			UUID:           s.ID(),
   162  			ServiceAddress: "addr1",
   163  		}
   164  		_, err = c1.SendCNHeartbeat(ctx, hb)
   165  		require.NoError(t, err)
   166  
   167  		c2, err := NewDNHAKeeperClient(ctx, cfg)
   168  		require.NoError(t, err)
   169  		defer func() {
   170  			assert.NoError(t, c2.Close())
   171  		}()
   172  
   173  		// should be transparently handled
   174  		cc = c2.(*managedHAKeeperClient)
   175  		assert.NoError(t, cc.mu.client.close())
   176  		cc.mu.client = nil
   177  
   178  		hb2 := pb.DNStoreHeartbeat{
   179  			UUID:                 s.ID(),
   180  			ServiceAddress:       "addr2",
   181  			LogtailServerAddress: "addr3",
   182  		}
   183  		cb, err := c2.SendDNHeartbeat(ctx, hb2)
   184  		require.NoError(t, err)
   185  		assert.Equal(t, 0, len(cb.Commands))
   186  
   187  		// should be transparently handled
   188  		cc = c1.(*managedHAKeeperClient)
   189  		assert.NoError(t, cc.mu.client.close())
   190  		cc.mu.client = nil
   191  
   192  		cd, err := c1.GetClusterDetails(ctx)
   193  		require.NoError(t, err)
   194  		cn := pb.CNStore{
   195  			UUID:           s.ID(),
   196  			ServiceAddress: "addr1",
   197  		}
   198  		dn := pb.DNStore{
   199  			UUID:                 s.ID(),
   200  			ServiceAddress:       "addr2",
   201  			LogtailServerAddress: "addr3",
   202  		}
   203  		assert.Equal(t, []pb.CNStore{cn}, cd.CNStores)
   204  		assert.Equal(t, []pb.DNStore{dn}, cd.DNStores)
   205  	}
   206  	runServiceTest(t, true, true, fn)
   207  }
   208  
   209  func TestHAKeeperClientSendDNHeartbeat(t *testing.T) {
   210  	fn := func(t *testing.T, s *Service) {
   211  		cfg := HAKeeperClientConfig{
   212  			ServiceAddresses: []string{testServiceAddress},
   213  		}
   214  		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
   215  		defer cancel()
   216  		c, err := NewDNHAKeeperClient(ctx, cfg)
   217  		require.NoError(t, err)
   218  		defer func() {
   219  			assert.NoError(t, c.Close())
   220  		}()
   221  		hb := pb.DNStoreHeartbeat{
   222  			UUID: s.ID(),
   223  		}
   224  		cb, err := c.SendDNHeartbeat(ctx, hb)
   225  		require.NoError(t, err)
   226  		assert.Equal(t, 0, len(cb.Commands))
   227  
   228  		sc := pb.ScheduleCommand{
   229  			UUID:        s.ID(),
   230  			ServiceType: pb.DNService,
   231  			ShutdownStore: &pb.ShutdownStore{
   232  				StoreID: "hello world",
   233  			},
   234  		}
   235  		require.NoError(t, s.store.addScheduleCommands(ctx, 0, []pb.ScheduleCommand{sc}))
   236  		cb, err = c.SendDNHeartbeat(ctx, hb)
   237  		require.NoError(t, err)
   238  		require.Equal(t, 1, len(cb.Commands))
   239  		require.Equal(t, sc, cb.Commands[0])
   240  	}
   241  	runServiceTest(t, true, true, fn)
   242  }
   243  
   244  func TestHAKeeperClientSendLogHeartbeat(t *testing.T) {
   245  	fn := func(t *testing.T, s *Service) {
   246  		cfg := HAKeeperClientConfig{
   247  			ServiceAddresses: []string{testServiceAddress},
   248  		}
   249  		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
   250  		defer cancel()
   251  		c, err := NewLogHAKeeperClient(ctx, cfg)
   252  		require.NoError(t, err)
   253  		defer func() {
   254  			assert.NoError(t, c.Close())
   255  		}()
   256  
   257  		// should be transparently handled
   258  		cc := c.(*managedHAKeeperClient)
   259  		assert.NoError(t, cc.mu.client.close())
   260  		cc.mu.client = nil
   261  
   262  		hb := s.store.getHeartbeatMessage()
   263  		cb, err := c.SendLogHeartbeat(ctx, hb)
   264  		require.NoError(t, err)
   265  		assert.Equal(t, 0, len(cb.Commands))
   266  
   267  		sc := pb.ScheduleCommand{
   268  			UUID:        s.ID(),
   269  			ServiceType: pb.DNService,
   270  			ShutdownStore: &pb.ShutdownStore{
   271  				StoreID: "hello world",
   272  			},
   273  		}
   274  		require.NoError(t, s.store.addScheduleCommands(ctx, 0, []pb.ScheduleCommand{sc}))
   275  		cb, err = c.SendLogHeartbeat(ctx, hb)
   276  		require.NoError(t, err)
   277  		require.Equal(t, 1, len(cb.Commands))
   278  		require.Equal(t, sc, cb.Commands[0])
   279  	}
   280  	runServiceTest(t, true, true, fn)
   281  }
   282  
   283  func testNotHAKeeperErrorIsHandled(t *testing.T, fn func(*testing.T, *managedHAKeeperClient)) {
   284  	defer leaktest.AfterTest(t)()
   285  	cfg1 := Config{
   286  		UUID:                uuid.New().String(),
   287  		FS:                  vfs.NewStrictMem(),
   288  		DeploymentID:        1,
   289  		RTTMillisecond:      5,
   290  		DataDir:             "data-1",
   291  		ServiceAddress:      "127.0.0.1:9002",
   292  		RaftAddress:         "127.0.0.1:9000",
   293  		GossipAddress:       "127.0.0.1:9001",
   294  		GossipSeedAddresses: []string{"127.0.0.1:9011"},
   295  		DisableWorkers:      true,
   296  	}
   297  	cfg2 := Config{
   298  		UUID:                uuid.New().String(),
   299  		FS:                  vfs.NewStrictMem(),
   300  		DeploymentID:        1,
   301  		RTTMillisecond:      5,
   302  		DataDir:             "data-2",
   303  		ServiceAddress:      "127.0.0.1:9012",
   304  		RaftAddress:         "127.0.0.1:9010",
   305  		GossipAddress:       "127.0.0.1:9011",
   306  		GossipSeedAddresses: []string{"127.0.0.1:9001"},
   307  		DisableWorkers:      true,
   308  	}
   309  	cfg1.Fill()
   310  	service1, err := NewService(cfg1,
   311  		testutil.NewFS(),
   312  		WithBackendFilter(func(msg morpc.Message, backendAddr string) bool {
   313  			return true
   314  		}),
   315  	)
   316  	require.NoError(t, err)
   317  	defer func() {
   318  		assert.NoError(t, service1.Close())
   319  	}()
   320  	cfg2.Fill()
   321  	service2, err := NewService(cfg2,
   322  		testutil.NewFS(),
   323  		WithBackendFilter(func(msg morpc.Message, backendAddr string) bool {
   324  			return true
   325  		}),
   326  	)
   327  	require.NoError(t, err)
   328  	defer func() {
   329  		assert.NoError(t, service2.Close())
   330  	}()
   331  	// service2 is HAKeeper
   332  	peers := make(map[uint64]dragonboat.Target)
   333  	peers[1] = service2.ID()
   334  	assert.NoError(t, service2.store.startHAKeeperReplica(1, peers, false))
   335  	// manually construct a HAKeeper client that is connected to service1
   336  	pool := &sync.Pool{}
   337  	pool.New = func() interface{} {
   338  		return &RPCRequest{pool: pool}
   339  	}
   340  	respPool := &sync.Pool{}
   341  	respPool.New = func() interface{} {
   342  		return &RPCResponse{pool: respPool}
   343  	}
   344  	cfg := HAKeeperClientConfig{
   345  		ServiceAddresses: []string{cfg1.ServiceAddress, cfg2.ServiceAddress},
   346  	}
   347  	c := &hakeeperClient{
   348  		cfg:      cfg,
   349  		pool:     pool,
   350  		respPool: respPool,
   351  	}
   352  	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
   353  	defer cancel()
   354  	cc, err := getRPCClient(ctx, cfg1.ServiceAddress, c.respPool, defaultMaxMessageSize, false)
   355  	require.NoError(t, err)
   356  	c.addr = cfg1.ServiceAddress
   357  	c.client = cc
   358  	client := &managedHAKeeperClient{cfg: cfg}
   359  	client.mu.client = c
   360  	defer func() {
   361  		require.NoError(t, client.Close())
   362  	}()
   363  	fn(t, client)
   364  }
   365  
   366  func TestGetClusterDetailsWhenNotConnectedToHAKeeper(t *testing.T) {
   367  	fn := func(t *testing.T, c *managedHAKeeperClient) {
   368  		oldc := c.mu.client
   369  		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
   370  		defer cancel()
   371  		_, err := c.GetClusterDetails(ctx)
   372  		require.NoError(t, err)
   373  		require.True(t, oldc != c.mu.client)
   374  	}
   375  	testNotHAKeeperErrorIsHandled(t, fn)
   376  }
   377  
   378  func TestSendCNHeartbeatWhenNotConnectedToHAKeeper(t *testing.T) {
   379  	fn := func(t *testing.T, c *managedHAKeeperClient) {
   380  		oldc := c.mu.client
   381  		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
   382  		defer cancel()
   383  		_, err := c.SendCNHeartbeat(ctx, pb.CNStoreHeartbeat{})
   384  		require.NoError(t, err)
   385  		require.True(t, oldc != c.mu.client)
   386  	}
   387  	testNotHAKeeperErrorIsHandled(t, fn)
   388  }
   389  
   390  func TestSendDNHeartbeatWhenNotConnectedToHAKeeper(t *testing.T) {
   391  	fn := func(t *testing.T, c *managedHAKeeperClient) {
   392  		oldc := c.mu.client
   393  		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
   394  		defer cancel()
   395  		_, err := c.SendDNHeartbeat(ctx, pb.DNStoreHeartbeat{})
   396  		require.NoError(t, err)
   397  		require.True(t, oldc != c.mu.client)
   398  	}
   399  	testNotHAKeeperErrorIsHandled(t, fn)
   400  }
   401  
   402  func TestSendLogHeartbeatWhenNotConnectedToHAKeeper(t *testing.T) {
   403  	fn := func(t *testing.T, c *managedHAKeeperClient) {
   404  		oldc := c.mu.client
   405  		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
   406  		defer cancel()
   407  		_, err := c.SendLogHeartbeat(ctx, pb.LogStoreHeartbeat{})
   408  		require.NoError(t, err)
   409  		require.True(t, oldc != c.mu.client)
   410  	}
   411  	testNotHAKeeperErrorIsHandled(t, fn)
   412  }