github.com/matrixorigin/matrixone@v1.2.0/pkg/hakeeper/bootstrap/bootstrap_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 bootstrap
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"testing"
    21  
    22  	"github.com/matrixorigin/matrixone/pkg/common/moerr"
    23  	"github.com/matrixorigin/matrixone/pkg/common/runtime"
    24  	"github.com/matrixorigin/matrixone/pkg/hakeeper/checkers/util"
    25  	"github.com/matrixorigin/matrixone/pkg/logutil"
    26  	pb "github.com/matrixorigin/matrixone/pkg/pb/logservice"
    27  	"github.com/matrixorigin/matrixone/pkg/pb/metadata"
    28  	"github.com/stretchr/testify/assert"
    29  )
    30  
    31  func TestMain(m *testing.M) {
    32  	logutil.SetupMOLogger(&logutil.LogConfig{
    33  		Level:  "debug",
    34  		Format: "console",
    35  	})
    36  
    37  	runtime.SetupProcessLevelRuntime(runtime.NewRuntime(metadata.ServiceType_LOG, "test", logutil.GetGlobalLogger()))
    38  	m.Run()
    39  }
    40  
    41  func TestNewBootstrapManager(t *testing.T) {
    42  	cases := []struct {
    43  		cluster  pb.ClusterInfo
    44  		expected *Manager
    45  	}{
    46  		{
    47  			cluster:  pb.ClusterInfo{},
    48  			expected: &Manager{},
    49  		},
    50  		{
    51  			cluster: pb.ClusterInfo{
    52  				TNShards: []metadata.TNShardRecord{{
    53  					ShardID:    1,
    54  					LogShardID: 1,
    55  				}},
    56  				LogShards: []metadata.LogShardRecord{{ShardID: 2}},
    57  			},
    58  			expected: &Manager{
    59  				cluster: pb.ClusterInfo{
    60  					TNShards: []metadata.TNShardRecord{{
    61  						ShardID:    1,
    62  						LogShardID: 1,
    63  					}},
    64  					LogShards: []metadata.LogShardRecord{{ShardID: 2}},
    65  				},
    66  			},
    67  		},
    68  	}
    69  
    70  	for _, c := range cases {
    71  		bm := NewBootstrapManager(c.cluster)
    72  		assert.Equal(t, c.expected.cluster, bm.cluster)
    73  		c.expected.cluster = pb.ClusterInfo{XXX_sizecache: 1}
    74  		assert.NotEqual(t, c.expected.cluster, bm.cluster)
    75  	}
    76  }
    77  
    78  func TestBootstrap(t *testing.T) {
    79  	cases := []struct {
    80  		desc string
    81  
    82  		cluster pb.ClusterInfo
    83  		tn      pb.TNState
    84  		log     pb.LogState
    85  
    86  		expectedNum            int
    87  		expectedInitialMembers map[uint64]string
    88  		expectedTNLogShardID   uint64
    89  		err                    error
    90  	}{
    91  		{
    92  			desc: "1 log shard with 1 replicas",
    93  
    94  			cluster: pb.ClusterInfo{
    95  				LogShards: []metadata.LogShardRecord{{
    96  					ShardID:          1,
    97  					NumberOfReplicas: 1,
    98  				}},
    99  			},
   100  			log: pb.LogState{
   101  				Stores: map[string]pb.LogStoreInfo{
   102  					"log-a": {Tick: 100},
   103  				},
   104  			},
   105  
   106  			expectedNum: 1,
   107  			expectedInitialMembers: map[uint64]string{
   108  				1: "log-a",
   109  			},
   110  		},
   111  		{
   112  			desc: "1 log shard with 3 replicas and 1 tn shard",
   113  
   114  			cluster: pb.ClusterInfo{
   115  				TNShards: []metadata.TNShardRecord{{ShardID: 1, LogShardID: 1}},
   116  				LogShards: []metadata.LogShardRecord{{
   117  					ShardID:          1,
   118  					NumberOfReplicas: 3,
   119  				}},
   120  			},
   121  			tn: pb.TNState{
   122  				Stores: map[string]pb.TNStoreInfo{"dn-a": {}},
   123  			},
   124  			log: pb.LogState{
   125  				Stores: map[string]pb.LogStoreInfo{
   126  					"log-a": {Tick: 100},
   127  					"log-b": {Tick: 110},
   128  					"log-c": {Tick: 120},
   129  					"log-d": {Tick: 130},
   130  				},
   131  			},
   132  
   133  			expectedNum: 4,
   134  			expectedInitialMembers: map[uint64]string{
   135  				1: "log-d",
   136  				2: "log-c",
   137  				3: "log-b",
   138  			},
   139  			expectedTNLogShardID: 1,
   140  		},
   141  		{
   142  			desc: "ignore shard 0",
   143  
   144  			cluster: pb.ClusterInfo{
   145  				TNShards: []metadata.TNShardRecord{},
   146  				LogShards: []metadata.LogShardRecord{{
   147  					ShardID:          0,
   148  					NumberOfReplicas: 3,
   149  				}},
   150  			},
   151  			tn: pb.TNState{
   152  				Stores: map[string]pb.TNStoreInfo{},
   153  			},
   154  			log: pb.LogState{
   155  				Stores: map[string]pb.LogStoreInfo{
   156  					"log-a": {Tick: 100},
   157  					"log-b": {Tick: 110},
   158  				},
   159  			},
   160  
   161  			expectedNum: 0,
   162  			err:         nil,
   163  		},
   164  		{
   165  			desc: "1 log shard with 3 replicas and 1 tn shard",
   166  
   167  			cluster: pb.ClusterInfo{
   168  				TNShards: []metadata.TNShardRecord{{ShardID: 1, LogShardID: 1}},
   169  				LogShards: []metadata.LogShardRecord{{
   170  					ShardID:          1,
   171  					NumberOfReplicas: 3,
   172  				}},
   173  			},
   174  			tn: pb.TNState{
   175  				Stores: map[string]pb.TNStoreInfo{"dn-a": {}},
   176  			},
   177  			log: pb.LogState{
   178  				Stores: map[string]pb.LogStoreInfo{
   179  					"log-a": {Tick: 100},
   180  					"log-b": {Tick: 110},
   181  					"log-c": {Tick: 120},
   182  					"log-d": {Tick: 130},
   183  				},
   184  			},
   185  
   186  			expectedNum: 4,
   187  			expectedInitialMembers: map[uint64]string{
   188  				1: "log-d",
   189  				2: "log-c",
   190  				3: "log-b",
   191  			},
   192  			expectedTNLogShardID: 1,
   193  		},
   194  	}
   195  
   196  	for i, c := range cases {
   197  		fmt.Printf("case %v: %s\n", i, c.desc)
   198  
   199  		alloc := util.NewTestIDAllocator(0)
   200  		bm := NewBootstrapManager(c.cluster)
   201  		output, err := bm.Bootstrap(alloc, c.tn, c.log)
   202  		assert.Equal(t, c.err, err)
   203  		if err != nil {
   204  			continue
   205  		}
   206  		assert.Equal(t, c.expectedNum, len(output))
   207  		if len(output) != 0 {
   208  			assert.Equal(t, c.expectedInitialMembers, output[0].ConfigChange.InitialMembers)
   209  			assert.Equal(t, pb.StartReplica, output[0].ConfigChange.ChangeType)
   210  		}
   211  
   212  		for _, command := range output {
   213  			if command.ServiceType == pb.TNService {
   214  				assert.Equal(t, c.expectedTNLogShardID, command.ConfigChange.Replica.LogShardID)
   215  			}
   216  		}
   217  	}
   218  }
   219  
   220  func TestCheckBootstrap(t *testing.T) {
   221  	cases := []struct {
   222  		desc string
   223  
   224  		cluster pb.ClusterInfo
   225  		log     pb.LogState
   226  
   227  		expected bool
   228  	}{
   229  		{
   230  			desc: "failed to start 1 replica",
   231  			cluster: pb.ClusterInfo{
   232  				LogShards: []metadata.LogShardRecord{
   233  					{ShardID: 1, NumberOfReplicas: 1},
   234  				},
   235  			},
   236  			log: pb.LogState{
   237  				Shards: map[uint64]pb.LogShardInfo{
   238  					1: {ShardID: 1, Replicas: map[uint64]string{}},
   239  				},
   240  			},
   241  			expected: false,
   242  		},
   243  		{
   244  			desc: "successfully started 1 replica",
   245  			cluster: pb.ClusterInfo{
   246  				LogShards: []metadata.LogShardRecord{
   247  					{ShardID: 1, NumberOfReplicas: 1},
   248  				},
   249  			},
   250  			log: pb.LogState{
   251  				Shards: map[uint64]pb.LogShardInfo{
   252  					1: {ShardID: 1, Replicas: map[uint64]string{1: "a"}},
   253  				},
   254  			},
   255  			expected: true,
   256  		},
   257  		{
   258  			desc: "successfully started 3 replicas",
   259  			cluster: pb.ClusterInfo{
   260  				LogShards: []metadata.LogShardRecord{
   261  					{ShardID: 1, NumberOfReplicas: 3},
   262  					{ShardID: 2, NumberOfReplicas: 3},
   263  					{ShardID: 3, NumberOfReplicas: 3},
   264  				},
   265  			},
   266  			log: pb.LogState{
   267  				Shards: map[uint64]pb.LogShardInfo{
   268  					1: {ShardID: 1, Replicas: map[uint64]string{1: "a", 2: "b"}},
   269  					2: {ShardID: 2, Replicas: map[uint64]string{1: "a", 3: "c"}},
   270  					3: {ShardID: 3, Replicas: map[uint64]string{2: "b", 3: "c"}},
   271  				},
   272  			},
   273  			expected: true,
   274  		},
   275  		{
   276  			desc: "shard 1 not started",
   277  			cluster: pb.ClusterInfo{
   278  				LogShards: []metadata.LogShardRecord{
   279  					{ShardID: 1, NumberOfReplicas: 3},
   280  					{ShardID: 2, NumberOfReplicas: 3},
   281  					{ShardID: 3, NumberOfReplicas: 3},
   282  				},
   283  			},
   284  			log: pb.LogState{
   285  				Shards: map[uint64]pb.LogShardInfo{
   286  					1: {ShardID: 1, Replicas: map[uint64]string{1: "a"}},
   287  					2: {ShardID: 2, Replicas: map[uint64]string{1: "a", 3: "c"}},
   288  					3: {ShardID: 3, Replicas: map[uint64]string{2: "b", 3: "c"}},
   289  				},
   290  			},
   291  			expected: false,
   292  		},
   293  		{
   294  			desc: "shard 1 not exists in log state",
   295  			cluster: pb.ClusterInfo{
   296  				LogShards: []metadata.LogShardRecord{
   297  					{ShardID: 1, NumberOfReplicas: 3},
   298  				},
   299  			},
   300  			log: pb.LogState{
   301  				Shards: map[uint64]pb.LogShardInfo{},
   302  			},
   303  			expected: false,
   304  		},
   305  	}
   306  
   307  	for i, c := range cases {
   308  		fmt.Printf("case %v: %s\n", i, c.desc)
   309  		bm := NewBootstrapManager(c.cluster)
   310  		output := bm.CheckBootstrap(c.log)
   311  		assert.Equal(t, c.expected, output)
   312  	}
   313  }
   314  
   315  func TestSortLogStores(t *testing.T) {
   316  	cases := []struct {
   317  		logStores map[string]pb.LogStoreInfo
   318  		expected  []string
   319  	}{{
   320  		logStores: map[string]pb.LogStoreInfo{
   321  			"a": {Tick: 100},
   322  			"b": {Tick: 120},
   323  			"c": {Tick: 90},
   324  			"d": {Tick: 95},
   325  		},
   326  		expected: []string{"b", "a", "d", "c"},
   327  	}}
   328  
   329  	for _, c := range cases {
   330  		output := logStoresSortedByTick(c.logStores)
   331  		assert.Equal(t, c.expected, output)
   332  	}
   333  }
   334  
   335  func TestSortTNStores(t *testing.T) {
   336  	cases := []struct {
   337  		tnStores map[string]pb.TNStoreInfo
   338  		expected []string
   339  	}{{
   340  		tnStores: map[string]pb.TNStoreInfo{
   341  			"a": {Tick: 100},
   342  			"b": {Tick: 120},
   343  			"c": {Tick: 90},
   344  			"d": {Tick: 95},
   345  		},
   346  		expected: []string{"b", "a", "d", "c"},
   347  	}}
   348  
   349  	for _, c := range cases {
   350  		output := tnStoresSortedByTick(c.tnStores)
   351  		assert.Equal(t, c.expected, output)
   352  	}
   353  }
   354  
   355  func TestIssue3814(t *testing.T) {
   356  	cases := []struct {
   357  		desc string
   358  
   359  		cluster pb.ClusterInfo
   360  		tn      pb.TNState
   361  		log     pb.LogState
   362  
   363  		expected error
   364  	}{
   365  		{
   366  			desc: "case not enough log store",
   367  			cluster: pb.ClusterInfo{
   368  				LogShards: []metadata.LogShardRecord{{
   369  					ShardID:          1,
   370  					NumberOfReplicas: 3,
   371  				}},
   372  			},
   373  			log:      pb.LogState{},
   374  			expected: moerr.NewInternalError(context.TODO(), "not enough log stores"),
   375  		},
   376  		{
   377  			desc: "case not enough tn stores",
   378  			cluster: pb.ClusterInfo{
   379  				TNShards: []metadata.TNShardRecord{{
   380  					ShardID:    1,
   381  					LogShardID: 1,
   382  				}},
   383  			},
   384  			tn: pb.TNState{
   385  				Stores: map[string]pb.TNStoreInfo{},
   386  			},
   387  			expected: nil,
   388  		},
   389  	}
   390  
   391  	for _, c := range cases {
   392  		alloc := util.NewTestIDAllocator(0)
   393  		bm := NewBootstrapManager(c.cluster)
   394  		_, err := bm.Bootstrap(alloc, c.tn, c.log)
   395  		assert.Equal(t, c.expected, err)
   396  	}
   397  }
   398  
   399  func TestIssue3845(t *testing.T) {
   400  	cases := []struct {
   401  		desc string
   402  
   403  		cluster pb.ClusterInfo
   404  		log     pb.LogState
   405  
   406  		expected bool
   407  	}{
   408  		{
   409  			desc: "shardID is 0",
   410  			cluster: pb.ClusterInfo{
   411  				LogShards: []metadata.LogShardRecord{{
   412  					ShardID:          0,
   413  					NumberOfReplicas: 1,
   414  				}},
   415  			},
   416  			log: pb.LogState{
   417  				Shards: map[uint64]pb.LogShardInfo{0: {
   418  					ShardID:  0,
   419  					Replicas: map[uint64]string{1: "a"},
   420  				}},
   421  				Stores: map[string]pb.LogStoreInfo{"a": {
   422  					Tick: 0,
   423  					Replicas: []pb.LogReplicaInfo{{
   424  						LogShardInfo: pb.LogShardInfo{
   425  							ShardID:  0,
   426  							Replicas: map[uint64]string{1: "a"},
   427  						},
   428  						ReplicaID: 1,
   429  					}},
   430  				}},
   431  			},
   432  			expected: true,
   433  		},
   434  	}
   435  
   436  	for _, c := range cases {
   437  		bm := NewBootstrapManager(c.cluster)
   438  		output := bm.CheckBootstrap(c.log)
   439  		assert.Equal(t, c.expected, output)
   440  	}
   441  }