github.com/matrixorigin/matrixone@v1.2.0/pkg/tnservice/store_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 tnservice
    16  
    17  import (
    18  	"context"
    19  	"math"
    20  	"os"
    21  	"sync"
    22  	"sync/atomic"
    23  	"testing"
    24  	"time"
    25  
    26  	"github.com/matrixorigin/matrixone/pkg/common/runtime"
    27  	"github.com/matrixorigin/matrixone/pkg/defines"
    28  	"github.com/matrixorigin/matrixone/pkg/fileservice"
    29  	"github.com/matrixorigin/matrixone/pkg/logservice"
    30  	"github.com/matrixorigin/matrixone/pkg/logutil"
    31  	logservicepb "github.com/matrixorigin/matrixone/pkg/pb/logservice"
    32  	"github.com/matrixorigin/matrixone/pkg/pb/metadata"
    33  	"github.com/matrixorigin/matrixone/pkg/txn/clock"
    34  	"github.com/matrixorigin/matrixone/pkg/txn/service"
    35  	"github.com/matrixorigin/matrixone/pkg/txn/storage/mem"
    36  	"github.com/stretchr/testify/assert"
    37  )
    38  
    39  var (
    40  	testTNStoreAddr      = "unix:///tmp/test-dnstore.sock"
    41  	testTNLogtailAddress = "127.0.0.1:22001"
    42  )
    43  
    44  func TestNewAndStartAndCloseService(t *testing.T) {
    45  	runTNStoreTest(t, func(s *store) {
    46  		thc := s.hakeeperClient.(*testHAKeeperClient)
    47  		for {
    48  			if v := thc.getCount(); v > 0 {
    49  				return
    50  			}
    51  		}
    52  	})
    53  }
    54  
    55  func TestAddReplica(t *testing.T) {
    56  	runTNStoreTest(t, func(s *store) {
    57  		addTestReplica(t, s, 1, 2, 3)
    58  	})
    59  }
    60  
    61  func TestHandleShutdown(t *testing.T) {
    62  	fn := func(s *store) {
    63  		cmd := logservicepb.ScheduleCommand{
    64  			UUID: s.cfg.UUID,
    65  			ShutdownStore: &logservicepb.ShutdownStore{
    66  				StoreID: s.cfg.UUID,
    67  			},
    68  			ServiceType: logservicepb.TNService,
    69  		}
    70  
    71  		shutdownC := make(chan struct{})
    72  		exit := atomic.Bool{}
    73  		go func() {
    74  			ctx, cancel := context.WithTimeout(context.Background(), time.Second)
    75  			defer func() {
    76  				cancel()
    77  				exit.Store(true)
    78  			}()
    79  
    80  			select {
    81  			case <-ctx.Done():
    82  				panic("deadline reached")
    83  			case <-shutdownC:
    84  				runtime.DefaultRuntime().Logger().Info("received shutdown command")
    85  			}
    86  		}()
    87  
    88  		s.shutdownC = shutdownC
    89  
    90  		for !exit.Load() {
    91  			s.handleCommands([]logservicepb.ScheduleCommand{cmd})
    92  			time.Sleep(time.Millisecond)
    93  		}
    94  
    95  	}
    96  	runTNStoreTest(t, fn)
    97  }
    98  
    99  func TestStartWithReplicas(t *testing.T) {
   100  	localFS, err := fileservice.NewMemoryFS(defines.LocalFileServiceName, fileservice.DisabledCacheConfig, nil)
   101  	assert.NoError(t, err)
   102  
   103  	factory := func(name string) (*fileservice.FileServices, error) {
   104  		s3fs, err := fileservice.NewMemoryFS(defines.SharedFileServiceName, fileservice.DisabledCacheConfig, nil)
   105  		if err != nil {
   106  			return nil, err
   107  		}
   108  		return fileservice.NewFileServices(
   109  			"",
   110  			s3fs,
   111  			localFS,
   112  		)
   113  	}
   114  
   115  	runTNStoreTestWithFileServiceFactory(t, func(s *store) {
   116  		addTestReplica(t, s, 1, 2, 3)
   117  	}, factory)
   118  
   119  	runTNStoreTestWithFileServiceFactory(t, func(s *store) {
   120  
   121  	}, factory)
   122  }
   123  
   124  func TestStartReplica(t *testing.T) {
   125  	runTNStoreTest(t, func(s *store) {
   126  		assert.NoError(t, s.StartTNReplica(newTestTNShard(1, 2, 3)))
   127  		r := s.getReplica(1)
   128  		r.waitStarted()
   129  		assert.Equal(t, newTestTNShard(1, 2, 3), r.shard)
   130  	})
   131  }
   132  
   133  func TestRemoveReplica(t *testing.T) {
   134  	runTNStoreTest(t, func(s *store) {
   135  		assert.NoError(t, s.StartTNReplica(newTestTNShard(1, 2, 3)))
   136  		r := s.getReplica(1)
   137  		r.waitStarted()
   138  
   139  		thc := s.hakeeperClient.(*testHAKeeperClient)
   140  		thc.setCommandBatch(logservicepb.CommandBatch{
   141  			Commands: []logservicepb.ScheduleCommand{
   142  				{
   143  					ServiceType: logservicepb.TNService,
   144  					ConfigChange: &logservicepb.ConfigChange{
   145  						ChangeType: logservicepb.RemoveReplica,
   146  						Replica: logservicepb.Replica{
   147  							LogShardID: 3,
   148  							ReplicaID:  2,
   149  							ShardID:    1,
   150  						},
   151  					},
   152  				},
   153  			},
   154  		})
   155  
   156  		for {
   157  			r := s.getReplica(1)
   158  			if r == nil {
   159  				return
   160  			}
   161  			time.Sleep(time.Millisecond * 10)
   162  		}
   163  	})
   164  }
   165  
   166  func TestCloseReplica(t *testing.T) {
   167  	runTNStoreTest(t, func(s *store) {
   168  		shard := newTestTNShard(1, 2, 3)
   169  		assert.NoError(t, s.StartTNReplica(shard))
   170  		r := s.getReplica(1)
   171  		r.waitStarted()
   172  		assert.Equal(t, shard, r.shard)
   173  
   174  		assert.NoError(t, s.CloseTNReplica(shard))
   175  		assert.Nil(t, s.getReplica(1))
   176  	})
   177  }
   178  
   179  func runTNStoreTest(
   180  	t *testing.T,
   181  	testFn func(*store),
   182  	opts ...Option) {
   183  	runTNStoreTestWithFileServiceFactory(t, testFn, func(name string) (*fileservice.FileServices, error) {
   184  		local, err := fileservice.NewMemoryFS(
   185  			defines.LocalFileServiceName,
   186  			fileservice.DisabledCacheConfig, nil,
   187  		)
   188  		if err != nil {
   189  			return nil, err
   190  		}
   191  		s3, err := fileservice.NewMemoryFS(
   192  			defines.SharedFileServiceName,
   193  			fileservice.DisabledCacheConfig, nil,
   194  		)
   195  		if err != nil {
   196  			return nil, err
   197  		}
   198  		etl, err := fileservice.NewMemoryFS(
   199  			defines.ETLFileServiceName,
   200  			fileservice.DisabledCacheConfig, nil,
   201  		)
   202  		if err != nil {
   203  			return nil, err
   204  		}
   205  		return fileservice.NewFileServices(name, local, s3, etl)
   206  	}, opts...)
   207  }
   208  
   209  func runTNStoreTestWithFileServiceFactory(
   210  	t *testing.T,
   211  	testFn func(*store),
   212  	fsFactory fileservice.NewFileServicesFunc,
   213  	opts ...Option) {
   214  	runtime.SetupProcessLevelRuntime(runtime.DefaultRuntime())
   215  	thc := newTestHAKeeperClient()
   216  	opts = append(opts,
   217  		WithHAKeeperClientFactory(func() (logservice.TNHAKeeperClient, error) {
   218  			return thc, nil
   219  		}),
   220  		WithLogServiceClientFactory(func(d metadata.TNShard) (logservice.Client, error) {
   221  			return mem.NewMemLog(), nil
   222  		}),
   223  		WithConfigAdjust(func(c *Config) {
   224  			c.HAKeeper.HeatbeatInterval.Duration = time.Millisecond * 10
   225  			c.Txn.Storage.Backend = StorageMEMKV
   226  		}))
   227  
   228  	if fsFactory == nil {
   229  		fsFactory = func(name string) (*fileservice.FileServices, error) {
   230  			fs, err := fileservice.NewMemoryFS(name, fileservice.DisabledCacheConfig, nil)
   231  			if err != nil {
   232  				return nil, err
   233  			}
   234  			return fileservice.NewFileServices(name, fs)
   235  		}
   236  	}
   237  	s := newTestStore(t, "u1", fsFactory, opts...)
   238  	defer func() {
   239  		assert.NoError(t, s.Close())
   240  	}()
   241  	assert.NoError(t, s.Start())
   242  	testFn(s)
   243  }
   244  
   245  func addTestReplica(t *testing.T, s *store, shardID, replicaID, logShardID uint64) {
   246  	thc := s.hakeeperClient.(*testHAKeeperClient)
   247  	thc.setCommandBatch(logservicepb.CommandBatch{
   248  		Commands: []logservicepb.ScheduleCommand{
   249  			{
   250  				ServiceType: logservicepb.TNService,
   251  				ConfigChange: &logservicepb.ConfigChange{
   252  					ChangeType: logservicepb.AddReplica,
   253  					Replica: logservicepb.Replica{
   254  						LogShardID: logShardID,
   255  						ReplicaID:  replicaID,
   256  						ShardID:    shardID,
   257  					},
   258  				},
   259  			},
   260  		},
   261  	})
   262  
   263  	for {
   264  		r := s.getReplica(1)
   265  		if r != nil {
   266  			r.waitStarted()
   267  			assert.Equal(t, newTestTNShard(shardID, replicaID, logShardID), r.shard)
   268  			return
   269  		}
   270  		time.Sleep(time.Millisecond * 10)
   271  	}
   272  }
   273  
   274  func newTestStore(
   275  	t *testing.T,
   276  	uuid string,
   277  	fsFactory fileservice.NewFileServicesFunc,
   278  	options ...Option) *store {
   279  	assert.NoError(t, os.RemoveAll(testTNStoreAddr[7:]))
   280  	c := &Config{
   281  		UUID:           uuid,
   282  		ListenAddress:  testTNStoreAddr,
   283  		ServiceAddress: testTNStoreAddr,
   284  	}
   285  	c.LogtailServer.ListenAddress = testTNLogtailAddress
   286  	fs, err := fsFactory(defines.LocalFileServiceName)
   287  	assert.Nil(t, err)
   288  
   289  	rt := runtime.NewRuntime(
   290  		metadata.ServiceType_TN,
   291  		"",
   292  		logutil.Adjust(nil),
   293  		runtime.WithClock(
   294  			clock.NewHLCClock(
   295  				func() int64 { return time.Now().UTC().UnixNano() },
   296  				time.Duration(math.MaxInt64))))
   297  	s, err := NewService(
   298  		c,
   299  		rt,
   300  		fs,
   301  		nil,
   302  		options...)
   303  	assert.NoError(t, err)
   304  	return s.(*store)
   305  }
   306  
   307  func newTestTNShard(shardID, replicaID, logShardID uint64) metadata.TNShard {
   308  	tnShard := service.NewTestTNShard(shardID)
   309  	tnShard.ReplicaID = replicaID
   310  	tnShard.LogShardID = logShardID
   311  	tnShard.Address = testTNStoreAddr
   312  	return tnShard
   313  }
   314  
   315  type testHAKeeperClient struct {
   316  	mu struct {
   317  		sync.RWMutex
   318  		commandBatch logservicepb.CommandBatch
   319  	}
   320  
   321  	atomic struct {
   322  		count uint64
   323  	}
   324  }
   325  
   326  func newTestHAKeeperClient() *testHAKeeperClient {
   327  	return &testHAKeeperClient{}
   328  }
   329  
   330  func (thc *testHAKeeperClient) setCommandBatch(commandBatch logservicepb.CommandBatch) {
   331  	thc.mu.Lock()
   332  	defer thc.mu.Unlock()
   333  	thc.mu.commandBatch = commandBatch
   334  }
   335  
   336  func (thc *testHAKeeperClient) getCount() uint64 {
   337  	return atomic.LoadUint64(&thc.atomic.count)
   338  }
   339  
   340  func (thc *testHAKeeperClient) Close() error {
   341  	return nil
   342  }
   343  
   344  func (thc *testHAKeeperClient) SendTNHeartbeat(ctx context.Context, hb logservicepb.TNStoreHeartbeat) (logservicepb.CommandBatch, error) {
   345  	atomic.AddUint64(&thc.atomic.count, 1)
   346  	thc.mu.RLock()
   347  	defer thc.mu.RUnlock()
   348  	return thc.mu.commandBatch, nil
   349  }
   350  
   351  var nextID uint64
   352  
   353  func (thc *testHAKeeperClient) AllocateID(ctx context.Context) (uint64, error) {
   354  	return atomic.AddUint64(&nextID, 1), nil
   355  }
   356  
   357  func (thc *testHAKeeperClient) AllocateIDByKey(ctx context.Context, key string) (uint64, error) {
   358  	return atomic.AddUint64(&nextID, 1), nil
   359  }
   360  
   361  func (thc *testHAKeeperClient) AllocateIDByKeyWithBatch(ctx context.Context, key string, batch uint64) (uint64, error) {
   362  	return atomic.AddUint64(&nextID, 1), nil
   363  }
   364  
   365  func (thc *testHAKeeperClient) GetClusterDetails(ctx context.Context) (logservicepb.ClusterDetails, error) {
   366  	return logservicepb.ClusterDetails{}, nil
   367  }
   368  func (thc *testHAKeeperClient) GetClusterState(ctx context.Context) (logservicepb.CheckerState, error) {
   369  	return logservicepb.CheckerState{}, nil
   370  }