github.com/matrixorigin/matrixone@v0.7.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 "fmt" 20 "math" 21 "math/rand" 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/matrixorigin/matrixone/pkg/testutil" 35 "github.com/stretchr/testify/assert" 36 "github.com/stretchr/testify/require" 37 ) 38 39 var testLogger = logutil.GetGlobalLogger().Named("logservice-test") 40 41 var getClientConfig = func(readOnly bool) ClientConfig { 42 return ClientConfig{ 43 ReadOnly: readOnly, 44 LogShardID: 1, 45 DNReplicaID: 2, 46 ServiceAddresses: []string{testServiceAddress}, 47 MaxMessageSize: defaultMaxMessageSize, 48 } 49 } 50 51 func runClientTest( 52 t *testing.T, 53 readOnly bool, 54 cCfgFn func(bool) ClientConfig, 55 fn func(*testing.T, *Service, ClientConfig, Client)) { 56 runtime.SetupProcessLevelRuntime(runtime.DefaultRuntime()) 57 58 defer leaktest.AfterTest(t)() 59 cfg := getServiceTestConfig() 60 defer vfs.ReportLeakedFD(cfg.FS, t) 61 service, err := NewService(cfg, 62 testutil.NewFS(), 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 testutil.NewFS(), 148 WithBackendFilter(func(msg morpc.Message, backendAddr string) bool { 149 return true 150 }), 151 ) 152 require.NoError(t, err) 153 defer func() { 154 assert.NoError(t, service.Close()) 155 }() 156 157 init := make(map[uint64]string) 158 init[2] = service.ID() 159 assert.NoError(t, service.store.startReplica(1, 2, init, false)) 160 161 scfg := ClientConfig{ 162 LogShardID: 1, 163 DNReplicaID: 2, 164 ServiceAddresses: []string{"localhost:53032"}, // unreachable 165 DiscoveryAddress: testServiceAddress, 166 } 167 168 done := false 169 for i := 0; i < 1000; i++ { 170 si, ok, err := GetShardInfo(testServiceAddress, 1) 171 if err != nil || !ok { 172 time.Sleep(10 * time.Millisecond) 173 continue 174 } 175 done = true 176 require.NoError(t, err) 177 assert.True(t, ok) 178 assert.Equal(t, uint64(2), si.ReplicaID) 179 addr, ok := si.Replicas[si.ReplicaID] 180 assert.True(t, ok) 181 assert.Equal(t, testServiceAddress, addr) 182 break 183 } 184 if !done { 185 t.Fatalf("failed to get shard info") 186 } 187 188 ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) 189 defer cancel() 190 c, err := NewClient(ctx, scfg) 191 require.NoError(t, err) 192 defer func() { 193 assert.NoError(t, c.Close()) 194 }() 195 } 196 197 func TestClientGetTSOTimestamp(t *testing.T) { 198 fn := func(t *testing.T, s *Service, cfg ClientConfig, c Client) { 199 ctx, cancel := context.WithTimeout(context.Background(), time.Second) 200 defer cancel() 201 v, err := c.GetTSOTimestamp(ctx, 100) 202 require.NoError(t, err) 203 assert.Equal(t, uint64(1), v) 204 205 v, err = c.GetTSOTimestamp(ctx, 1000) 206 require.NoError(t, err) 207 assert.Equal(t, uint64(101), v) 208 209 v, err = c.GetTSOTimestamp(ctx, 100) 210 require.NoError(t, err) 211 assert.Equal(t, uint64(1101), v) 212 } 213 runClientTest(t, false, nil, fn) 214 } 215 216 func TestClientAppend(t *testing.T) { 217 fn := func(t *testing.T, s *Service, cfg ClientConfig, c Client) { 218 rec := c.GetLogRecord(16) 219 rand.Read(rec.Payload()) 220 ctx, cancel := context.WithTimeout(context.Background(), time.Second) 221 defer cancel() 222 lsn, err := c.Append(ctx, rec) 223 require.NoError(t, err) 224 assert.Equal(t, uint64(4), lsn) 225 226 lsn, err = c.Append(ctx, rec) 227 require.NoError(t, err) 228 assert.Equal(t, uint64(5), lsn) 229 230 cmd := make([]byte, 16+headerSize+8) 231 cmd = getAppendCmd(cmd, cfg.DNReplicaID+1) 232 _, err = c.Append(ctx, pb.LogRecord{Data: cmd}) 233 assert.True(t, moerr.IsMoErrCode(err, moerr.ErrNotLeaseHolder)) 234 } 235 runClientTest(t, false, nil, fn) 236 } 237 238 // FIXME: actually enforce allowed allocation 239 func TestClientAppendAlloc(t *testing.T) { 240 fn := func(t *testing.T, s *Service, cfg ClientConfig, c Client) { 241 rec := c.GetLogRecord(16) 242 rand.Read(rec.Payload()) 243 ac := testing.AllocsPerRun(1000, func() { 244 ctx, cancel := context.WithTimeout(context.Background(), time.Second) 245 defer cancel() 246 _, err := c.Append(ctx, rec) 247 require.NoError(t, err) 248 }) 249 testLogger.Info(fmt.Sprintf("ac: %f", ac)) 250 } 251 runClientTest(t, false, nil, fn) 252 } 253 254 func TestClientRead(t *testing.T) { 255 fn := func(t *testing.T, s *Service, cfg ClientConfig, c Client) { 256 rec := c.GetLogRecord(16) 257 rand.Read(rec.Payload()) 258 ctx, cancel := context.WithTimeout(context.Background(), time.Second) 259 defer cancel() 260 lsn, err := c.Append(ctx, rec) 261 require.NoError(t, err) 262 assert.Equal(t, uint64(4), lsn) 263 264 rec2 := c.GetLogRecord(16) 265 rand.Read(rec2.Payload()) 266 lsn, err = c.Append(ctx, rec2) 267 require.NoError(t, err) 268 assert.Equal(t, uint64(5), lsn) 269 270 // FIXME: returned records should contain correct Index value 271 recs, lsn, err := c.Read(ctx, 4, math.MaxUint64) 272 require.NoError(t, err) 273 assert.Equal(t, uint64(4), lsn) 274 require.Equal(t, 2, len(recs)) 275 assert.Equal(t, rec.Data, recs[0].Data) 276 assert.Equal(t, rec2.Data, recs[1].Data) 277 278 _, _, err = c.Read(ctx, 6, math.MaxUint64) 279 assert.True(t, errors.Is(err, dragonboat.ErrInvalidRange)) 280 } 281 runClientTest(t, false, nil, fn) 282 } 283 284 func TestClientTruncate(t *testing.T) { 285 fn := func(t *testing.T, s *Service, cfg ClientConfig, c Client) { 286 rec := c.GetLogRecord(16) 287 rand.Read(rec.Payload()) 288 ctx, cancel := context.WithTimeout(context.Background(), time.Second) 289 defer cancel() 290 lsn, err := c.Append(ctx, rec) 291 require.NoError(t, err) 292 assert.Equal(t, uint64(4), lsn) 293 294 require.NoError(t, c.Truncate(ctx, 4)) 295 lsn, err = c.GetTruncatedLsn(ctx) 296 assert.NoError(t, err) 297 assert.Equal(t, Lsn(4), lsn) 298 299 err = c.Truncate(ctx, 3) 300 assert.True(t, moerr.IsMoErrCode(err, moerr.ErrInvalidTruncateLsn)) 301 } 302 runClientTest(t, false, nil, fn) 303 } 304 305 func TestReadOnlyClientRejectWriteRequests(t *testing.T) { 306 fn := func(t *testing.T, s *Service, cfg ClientConfig, c Client) { 307 rec := c.GetLogRecord(16) 308 rand.Read(rec.Payload()) 309 ctx, cancel := context.WithTimeout(context.Background(), time.Second) 310 defer cancel() 311 _, err := c.Append(ctx, rec) 312 require.True(t, moerr.IsMoErrCode(err, moerr.ErrInvalidInput)) 313 err = c.Truncate(ctx, 4) 314 require.True(t, moerr.IsMoErrCode(err, moerr.ErrInvalidInput)) 315 } 316 runClientTest(t, true, nil, fn) 317 } 318 319 func TestClientSendWithMsgSize(t *testing.T) { 320 cFn := func(readOnly bool) ClientConfig { 321 return ClientConfig{ 322 ReadOnly: readOnly, 323 LogShardID: 1, 324 DNReplicaID: 2, 325 ServiceAddresses: []string{testServiceAddress}, 326 MaxMessageSize: testServerMaxMsgSize, 327 } 328 } 329 330 fn := func(t *testing.T, s *Service, cfg ClientConfig, c Client) { 331 rec := c.GetLogRecord(testServerMaxMsgSize + 80) 332 rand.Read(rec.Payload()) 333 ctx, cancel := context.WithTimeout(context.Background(), time.Second) 334 defer cancel() 335 // client only writes message whose size less than 190 336 _, err := c.Append(ctx, rec) 337 require.Error(t, err) 338 } 339 runClientTest(t, false, cFn, fn) 340 } 341 342 func TestServerReceiveWithMsgSize(t *testing.T) { 343 fn := func(t *testing.T, s *Service, cfg ClientConfig, c Client) { 344 rec := c.GetLogRecord(16) 345 rand.Read(rec.Payload()) 346 ctx, cancel := context.WithTimeout(context.Background(), time.Second) 347 defer cancel() 348 _, err := c.Append(ctx, rec) 349 require.NoError(t, err) 350 351 rec = c.GetLogRecord(defaultMaxMessageSize + 20) 352 rand.Read(rec.Payload()) 353 ctx, cancel = context.WithTimeout(context.Background(), time.Second) 354 defer cancel() 355 _, err = c.Append(ctx, rec) 356 require.Error(t, err) 357 } 358 runClientTest(t, false, nil, fn) 359 }