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 }