github.com/matrixorigin/matrixone@v1.2.0/pkg/logservice/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  	"crypto/rand"
    20  	"fmt"
    21  	"math"
    22  	"testing"
    23  	"time"
    24  
    25  	"github.com/cockroachdb/errors"
    26  	"github.com/lni/dragonboat/v4"
    27  	"github.com/lni/goutils/leaktest"
    28  	"github.com/lni/vfs"
    29  	"github.com/matrixorigin/matrixone/pkg/common/moerr"
    30  	"github.com/matrixorigin/matrixone/pkg/common/morpc"
    31  	"github.com/matrixorigin/matrixone/pkg/common/runtime"
    32  	"github.com/matrixorigin/matrixone/pkg/logutil"
    33  	pb "github.com/matrixorigin/matrixone/pkg/pb/logservice"
    34  	"github.com/stretchr/testify/assert"
    35  	"github.com/stretchr/testify/require"
    36  )
    37  
    38  var testLogger = logutil.GetGlobalLogger().Named("logservice-test")
    39  
    40  var getClientConfig = func(readOnly bool) ClientConfig {
    41  	return ClientConfig{
    42  		ReadOnly:         readOnly,
    43  		LogShardID:       1,
    44  		TNReplicaID:      2,
    45  		ServiceAddresses: []string{testServiceAddress},
    46  		MaxMessageSize:   defaultMaxMessageSize,
    47  	}
    48  }
    49  
    50  func runClientTest(
    51  	t *testing.T,
    52  	readOnly bool,
    53  	cCfgFn func(bool) ClientConfig,
    54  	fn func(*testing.T, *Service, ClientConfig, Client)) {
    55  	runtime.SetupProcessLevelRuntime(runtime.DefaultRuntime())
    56  
    57  	defer leaktest.AfterTest(t)()
    58  	cfg := getServiceTestConfig()
    59  	defer vfs.ReportLeakedFD(cfg.FS, t)
    60  	service, err := NewService(cfg,
    61  		newFS(),
    62  		nil,
    63  		WithBackendFilter(func(msg morpc.Message, backendAddr string) bool {
    64  			return true
    65  		}),
    66  	)
    67  	require.NoError(t, err)
    68  	defer func() {
    69  		assert.NoError(t, service.Close())
    70  	}()
    71  
    72  	init := make(map[uint64]string)
    73  	init[2] = service.ID()
    74  	assert.NoError(t, service.store.startReplica(1, 2, init, false))
    75  
    76  	if cCfgFn == nil {
    77  		cCfgFn = getClientConfig
    78  	}
    79  	scfg := cCfgFn(readOnly)
    80  
    81  	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
    82  	defer cancel()
    83  	c, err := NewClient(ctx, scfg)
    84  	require.NoError(t, err)
    85  	defer func() {
    86  		assert.NoError(t, c.Close())
    87  	}()
    88  
    89  	fn(t, service, scfg, c)
    90  }
    91  
    92  func TestClientConfigIsValidated(t *testing.T) {
    93  	cfg := ClientConfig{}
    94  	cc, err := NewClient(context.TODO(), cfg)
    95  	assert.Nil(t, cc)
    96  	assert.True(t, moerr.IsMoErrCode(err, moerr.ErrBadConfig))
    97  }
    98  
    99  func TestClientCanBeReset(t *testing.T) {
   100  	fn := func(t *testing.T, s *Service, cfg ClientConfig, c Client) {
   101  		client := c.(*managedClient)
   102  		client.resetClient()
   103  		assert.Nil(t, client.client)
   104  	}
   105  	runClientTest(t, false, nil, fn)
   106  }
   107  
   108  func TestPrepareClient(t *testing.T) {
   109  	fn := func(t *testing.T, s *Service, cfg ClientConfig, c Client) {
   110  		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
   111  		defer cancel()
   112  		client := c.(*managedClient)
   113  		assert.NoError(t, client.prepareClient(ctx))
   114  		client.resetClient()
   115  		assert.Nil(t, client.client)
   116  		assert.NoError(t, client.prepareClient(ctx))
   117  		assert.NotNil(t, client.client)
   118  	}
   119  	runClientTest(t, false, nil, fn)
   120  }
   121  
   122  func TestLogShardNotFoundErrorIsConsideredAsTempError(t *testing.T) {
   123  	fn := func(t *testing.T, s *Service, cfg ClientConfig, c Client) {
   124  		require.NoError(t, s.store.stopReplica(1, 2))
   125  		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
   126  		defer cancel()
   127  		_, err := c.GetTSOTimestamp(ctx, 100)
   128  		assert.True(t, isTempError(err))
   129  		client := c.(*managedClient)
   130  		assert.True(t, client.isRetryableError(err))
   131  	}
   132  	runClientTest(t, false, nil, fn)
   133  }
   134  
   135  func TestClientCanBeCreated(t *testing.T) {
   136  	fn := func(t *testing.T, s *Service, cfg ClientConfig, c Client) {
   137  	}
   138  	runClientTest(t, false, nil, fn)
   139  	runClientTest(t, true, nil, fn)
   140  }
   141  
   142  func TestClientCanBeConnectedByReverseProxy(t *testing.T) {
   143  	defer leaktest.AfterTest(t)()
   144  	cfg := getServiceTestConfig()
   145  	defer vfs.ReportLeakedFD(cfg.FS, t)
   146  	service, err := NewService(cfg,
   147  		newFS(),
   148  		nil,
   149  		WithBackendFilter(func(msg morpc.Message, backendAddr string) bool {
   150  			return true
   151  		}),
   152  	)
   153  	require.NoError(t, err)
   154  	defer func() {
   155  		assert.NoError(t, service.Close())
   156  	}()
   157  
   158  	init := make(map[uint64]string)
   159  	init[2] = service.ID()
   160  	assert.NoError(t, service.store.startReplica(1, 2, init, false))
   161  
   162  	scfg := ClientConfig{
   163  		LogShardID:       1,
   164  		TNReplicaID:      2,
   165  		ServiceAddresses: []string{"localhost:53032"}, // unreachable
   166  		DiscoveryAddress: testServiceAddress,
   167  	}
   168  
   169  	done := false
   170  	for i := 0; i < 1000; i++ {
   171  		si, ok, err := GetShardInfo(testServiceAddress, 1)
   172  		if err != nil || !ok {
   173  			time.Sleep(10 * time.Millisecond)
   174  			continue
   175  		}
   176  		done = true
   177  		require.NoError(t, err)
   178  		assert.True(t, ok)
   179  		assert.Equal(t, uint64(2), si.ReplicaID)
   180  		addr, ok := si.Replicas[si.ReplicaID]
   181  		assert.True(t, ok)
   182  		assert.Equal(t, testServiceAddress, addr)
   183  		break
   184  	}
   185  	if !done {
   186  		t.Fatalf("failed to get shard info")
   187  	}
   188  
   189  	ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
   190  	defer cancel()
   191  	c, err := NewClient(ctx, scfg)
   192  	require.NoError(t, err)
   193  	defer func() {
   194  		assert.NoError(t, c.Close())
   195  	}()
   196  }
   197  
   198  func TestClientGetTSOTimestamp(t *testing.T) {
   199  	fn := func(t *testing.T, s *Service, cfg ClientConfig, c Client) {
   200  		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
   201  		defer cancel()
   202  		v, err := c.GetTSOTimestamp(ctx, 100)
   203  		require.NoError(t, err)
   204  		assert.Equal(t, uint64(1), v)
   205  
   206  		v, err = c.GetTSOTimestamp(ctx, 1000)
   207  		require.NoError(t, err)
   208  		assert.Equal(t, uint64(101), v)
   209  
   210  		v, err = c.GetTSOTimestamp(ctx, 100)
   211  		require.NoError(t, err)
   212  		assert.Equal(t, uint64(1101), v)
   213  	}
   214  	runClientTest(t, false, nil, fn)
   215  }
   216  
   217  func TestClientAppend(t *testing.T) {
   218  	fn := func(t *testing.T, s *Service, cfg ClientConfig, c Client) {
   219  		rec := c.GetLogRecord(16)
   220  		rand.Read(rec.Payload())
   221  		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
   222  		defer cancel()
   223  		lsn, err := c.Append(ctx, rec)
   224  		require.NoError(t, err)
   225  		assert.Equal(t, uint64(4), lsn)
   226  
   227  		lsn, err = c.Append(ctx, rec)
   228  		require.NoError(t, err)
   229  		assert.Equal(t, uint64(5), lsn)
   230  
   231  		cmd := make([]byte, 16+headerSize+8)
   232  		cmd = getAppendCmd(cmd, cfg.TNReplicaID+1)
   233  		_, err = c.Append(ctx, pb.LogRecord{Data: cmd})
   234  		assert.True(t, moerr.IsMoErrCode(err, moerr.ErrNotLeaseHolder))
   235  	}
   236  	runClientTest(t, false, nil, fn)
   237  }
   238  
   239  // FIXME: actually enforce allowed allocation
   240  func TestClientAppendAlloc(t *testing.T) {
   241  	fn := func(t *testing.T, s *Service, cfg ClientConfig, c Client) {
   242  		rec := c.GetLogRecord(16)
   243  		rand.Read(rec.Payload())
   244  		ac := testing.AllocsPerRun(1000, func() {
   245  			ctx, cancel := context.WithTimeout(context.Background(), time.Second)
   246  			defer cancel()
   247  			_, err := c.Append(ctx, rec)
   248  			require.NoError(t, err)
   249  		})
   250  		testLogger.Info(fmt.Sprintf("ac: %f", ac))
   251  	}
   252  	runClientTest(t, false, nil, fn)
   253  }
   254  
   255  func TestClientRead(t *testing.T) {
   256  	fn := func(t *testing.T, s *Service, cfg ClientConfig, c Client) {
   257  		rec := c.GetLogRecord(16)
   258  		rand.Read(rec.Payload())
   259  		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
   260  		defer cancel()
   261  		lsn, err := c.Append(ctx, rec)
   262  		require.NoError(t, err)
   263  		assert.Equal(t, uint64(4), lsn)
   264  
   265  		rec2 := c.GetLogRecord(16)
   266  		rand.Read(rec2.Payload())
   267  		lsn, err = c.Append(ctx, rec2)
   268  		require.NoError(t, err)
   269  		assert.Equal(t, uint64(5), lsn)
   270  
   271  		// FIXME: returned records should contain correct Index value
   272  		recs, lsn, err := c.Read(ctx, 4, math.MaxUint64)
   273  		require.NoError(t, err)
   274  		assert.Equal(t, uint64(4), lsn)
   275  		require.Equal(t, 2, len(recs))
   276  		assert.Equal(t, rec.Data, recs[0].Data)
   277  		assert.Equal(t, rec2.Data, recs[1].Data)
   278  
   279  		_, _, err = c.Read(ctx, 6, math.MaxUint64)
   280  		assert.True(t, errors.Is(err, dragonboat.ErrInvalidRange))
   281  	}
   282  	runClientTest(t, false, nil, fn)
   283  }
   284  
   285  func TestClientTruncate(t *testing.T) {
   286  	fn := func(t *testing.T, s *Service, cfg ClientConfig, c Client) {
   287  		rec := c.GetLogRecord(16)
   288  		rand.Read(rec.Payload())
   289  		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
   290  		defer cancel()
   291  		lsn, err := c.Append(ctx, rec)
   292  		require.NoError(t, err)
   293  		assert.Equal(t, uint64(4), lsn)
   294  
   295  		require.NoError(t, c.Truncate(ctx, 4))
   296  		lsn, err = c.GetTruncatedLsn(ctx)
   297  		assert.NoError(t, err)
   298  		assert.Equal(t, Lsn(4), lsn)
   299  
   300  		err = c.Truncate(ctx, 3)
   301  		assert.True(t, moerr.IsMoErrCode(err, moerr.ErrInvalidTruncateLsn))
   302  	}
   303  	runClientTest(t, false, nil, fn)
   304  }
   305  
   306  func TestReadOnlyClientRejectWriteRequests(t *testing.T) {
   307  	fn := func(t *testing.T, s *Service, cfg ClientConfig, c Client) {
   308  		rec := c.GetLogRecord(16)
   309  		rand.Read(rec.Payload())
   310  		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
   311  		defer cancel()
   312  		_, err := c.Append(ctx, rec)
   313  		require.True(t, moerr.IsMoErrCode(err, moerr.ErrInvalidInput))
   314  		err = c.Truncate(ctx, 4)
   315  		require.True(t, moerr.IsMoErrCode(err, moerr.ErrInvalidInput))
   316  	}
   317  	runClientTest(t, true, nil, fn)
   318  }
   319  
   320  func TestClientSendWithMsgSize(t *testing.T) {
   321  	cFn := func(readOnly bool) ClientConfig {
   322  		return ClientConfig{
   323  			ReadOnly:         readOnly,
   324  			LogShardID:       1,
   325  			TNReplicaID:      2,
   326  			ServiceAddresses: []string{testServiceAddress},
   327  			MaxMessageSize:   testServerMaxMsgSize,
   328  		}
   329  	}
   330  
   331  	fn := func(t *testing.T, s *Service, cfg ClientConfig, c Client) {
   332  		rec := c.GetLogRecord(testServerMaxMsgSize + 80)
   333  		rand.Read(rec.Payload())
   334  		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
   335  		defer cancel()
   336  		// client only writes message whose size less than 190
   337  		_, err := c.Append(ctx, rec)
   338  		require.Error(t, err)
   339  	}
   340  	runClientTest(t, false, cFn, fn)
   341  }
   342  
   343  func TestServerReceiveWithMsgSize(t *testing.T) {
   344  	fn := func(t *testing.T, s *Service, cfg ClientConfig, c Client) {
   345  		rec := c.GetLogRecord(16)
   346  		rand.Read(rec.Payload())
   347  		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
   348  		defer cancel()
   349  		_, err := c.Append(ctx, rec)
   350  		require.NoError(t, err)
   351  
   352  		rec = c.GetLogRecord(defaultMaxMessageSize + 20)
   353  		rand.Read(rec.Payload())
   354  		ctx, cancel = context.WithTimeout(context.Background(), time.Second)
   355  		defer cancel()
   356  		_, err = c.Append(ctx, rec)
   357  		require.Error(t, err)
   358  	}
   359  	runClientTest(t, false, nil, fn)
   360  }