github.com/pingcap/br@v5.3.0-alpha.0.20220125034240-ec59c7b6ce30+incompatible/pkg/lightning/backend/backend_test.go (about) 1 package backend_test 2 3 import ( 4 "context" 5 "testing" 6 "time" 7 8 "github.com/golang/mock/gomock" 9 "github.com/google/uuid" 10 . "github.com/pingcap/check" 11 "github.com/pingcap/errors" 12 "github.com/pingcap/parser/mysql" 13 "github.com/tikv/client-go/v2/oracle" 14 15 "github.com/pingcap/br/pkg/lightning/backend" 16 "github.com/pingcap/br/pkg/lightning/backend/kv" 17 "github.com/pingcap/br/pkg/mock" 18 ) 19 20 type backendSuite struct { 21 controller *gomock.Controller 22 mockBackend *mock.MockBackend 23 backend backend.Backend 24 ts uint64 25 } 26 27 var _ = Suite(&backendSuite{}) 28 29 func Test(t *testing.T) { 30 TestingT(t) 31 } 32 33 // FIXME: Cannot use the real SetUpTest/TearDownTest to set up the mock 34 // otherwise the mock error will be ignored. 35 36 func (s *backendSuite) setUpTest(c gomock.TestReporter) { 37 s.controller = gomock.NewController(c) 38 s.mockBackend = mock.NewMockBackend(s.controller) 39 s.backend = backend.MakeBackend(s.mockBackend) 40 s.ts = oracle.ComposeTS(time.Now().Unix()*1000, 0) 41 } 42 43 func (s *backendSuite) tearDownTest() { 44 s.controller.Finish() 45 } 46 47 func (s *backendSuite) TestOpenCloseImportCleanUpEngine(c *C) { 48 s.setUpTest(c) 49 defer s.tearDownTest() 50 51 ctx := context.Background() 52 engineUUID := uuid.MustParse("902efee3-a3f9-53d4-8c82-f12fb1900cd1") 53 54 openCall := s.mockBackend.EXPECT(). 55 OpenEngine(ctx, &backend.EngineConfig{}, engineUUID). 56 Return(nil) 57 closeCall := s.mockBackend.EXPECT(). 58 CloseEngine(ctx, nil, engineUUID). 59 Return(nil). 60 After(openCall) 61 importCall := s.mockBackend.EXPECT(). 62 ImportEngine(ctx, engineUUID). 63 Return(nil). 64 After(closeCall) 65 s.mockBackend.EXPECT(). 66 CleanupEngine(ctx, engineUUID). 67 Return(nil). 68 After(importCall) 69 70 engine, err := s.backend.OpenEngine(ctx, &backend.EngineConfig{}, "`db`.`table`", 1) 71 c.Assert(err, IsNil) 72 closedEngine, err := engine.Close(ctx, nil) 73 c.Assert(err, IsNil) 74 err = closedEngine.Import(ctx) 75 c.Assert(err, IsNil) 76 err = closedEngine.Cleanup(ctx) 77 c.Assert(err, IsNil) 78 } 79 80 func (s *backendSuite) TestUnsafeCloseEngine(c *C) { 81 s.setUpTest(c) 82 defer s.tearDownTest() 83 84 ctx := context.Background() 85 engineUUID := uuid.MustParse("7e3f3a3c-67ce-506d-af34-417ec138fbcb") 86 87 closeCall := s.mockBackend.EXPECT(). 88 CloseEngine(ctx, nil, engineUUID). 89 Return(nil) 90 s.mockBackend.EXPECT(). 91 CleanupEngine(ctx, engineUUID). 92 Return(nil). 93 After(closeCall) 94 95 closedEngine, err := s.backend.UnsafeCloseEngine(ctx, nil, "`db`.`table`", -1) 96 c.Assert(err, IsNil) 97 err = closedEngine.Cleanup(ctx) 98 c.Assert(err, IsNil) 99 } 100 101 func (s *backendSuite) TestUnsafeCloseEngineWithUUID(c *C) { 102 s.setUpTest(c) 103 defer s.tearDownTest() 104 105 ctx := context.Background() 106 engineUUID := uuid.MustParse("f1240229-79e0-4d8d-bda0-a211bf493796") 107 108 closeCall := s.mockBackend.EXPECT(). 109 CloseEngine(ctx, nil, engineUUID). 110 Return(nil) 111 s.mockBackend.EXPECT(). 112 CleanupEngine(ctx, engineUUID). 113 Return(nil). 114 After(closeCall) 115 116 closedEngine, err := s.backend.UnsafeCloseEngineWithUUID(ctx, nil, "some_tag", engineUUID) 117 c.Assert(err, IsNil) 118 err = closedEngine.Cleanup(ctx) 119 c.Assert(err, IsNil) 120 } 121 122 func (s *backendSuite) TestWriteEngine(c *C) { 123 s.setUpTest(c) 124 defer s.tearDownTest() 125 126 ctx := context.Background() 127 engineUUID := uuid.MustParse("902efee3-a3f9-53d4-8c82-f12fb1900cd1") 128 129 rows1 := mock.NewMockRows(s.controller) 130 rows2 := mock.NewMockRows(s.controller) 131 132 s.mockBackend.EXPECT(). 133 OpenEngine(ctx, &backend.EngineConfig{}, engineUUID). 134 Return(nil) 135 136 mockWriter := mock.NewMockEngineWriter(s.controller) 137 138 s.mockBackend.EXPECT().LocalWriter(ctx, gomock.Any(), gomock.Any()). 139 Return(mockWriter, nil).AnyTimes() 140 mockWriter.EXPECT(). 141 AppendRows(ctx, "`db`.`table`", []string{"c1", "c2"}, rows1). 142 Return(nil) 143 mockWriter.EXPECT().Close(ctx).Return(nil, nil).AnyTimes() 144 mockWriter.EXPECT(). 145 AppendRows(ctx, "`db`.`table`", []string{"c1", "c2"}, rows2). 146 Return(nil) 147 148 engine, err := s.backend.OpenEngine(ctx, &backend.EngineConfig{}, "`db`.`table`", 1) 149 c.Assert(err, IsNil) 150 writer, err := engine.LocalWriter(ctx, &backend.LocalWriterConfig{}) 151 c.Assert(err, IsNil) 152 err = writer.WriteRows(ctx, []string{"c1", "c2"}, rows1) 153 c.Assert(err, IsNil) 154 err = writer.WriteRows(ctx, []string{"c1", "c2"}, rows2) 155 c.Assert(err, IsNil) 156 _, err = writer.Close(ctx) 157 c.Assert(err, IsNil) 158 } 159 160 func (s *backendSuite) TestWriteToEngineWithNothing(c *C) { 161 s.setUpTest(c) 162 defer s.tearDownTest() 163 164 ctx := context.Background() 165 emptyRows := mock.NewMockRows(s.controller) 166 mockWriter := mock.NewMockEngineWriter(s.controller) 167 168 s.mockBackend.EXPECT().OpenEngine(ctx, &backend.EngineConfig{}, gomock.Any()).Return(nil) 169 mockWriter.EXPECT().AppendRows(ctx, gomock.Any(), gomock.Any(), emptyRows).Return(nil) 170 mockWriter.EXPECT().Close(ctx).Return(nil, nil) 171 s.mockBackend.EXPECT().LocalWriter(ctx, &backend.LocalWriterConfig{}, gomock.Any()).Return(mockWriter, nil) 172 173 engine, err := s.backend.OpenEngine(ctx, &backend.EngineConfig{}, "`db`.`table`", 1) 174 c.Assert(err, IsNil) 175 writer, err := engine.LocalWriter(ctx, &backend.LocalWriterConfig{}) 176 c.Assert(err, IsNil) 177 err = writer.WriteRows(ctx, nil, emptyRows) 178 c.Assert(err, IsNil) 179 _, err = writer.Close(ctx) 180 c.Assert(err, IsNil) 181 } 182 183 func (s *backendSuite) TestOpenEngineFailed(c *C) { 184 s.setUpTest(c) 185 defer s.tearDownTest() 186 187 ctx := context.Background() 188 189 s.mockBackend.EXPECT().OpenEngine(ctx, &backend.EngineConfig{}, gomock.Any()). 190 Return(errors.New("fake unrecoverable open error")) 191 192 _, err := s.backend.OpenEngine(ctx, &backend.EngineConfig{}, "`db`.`table`", 1) 193 c.Assert(err, ErrorMatches, "fake unrecoverable open error") 194 } 195 196 func (s *backendSuite) TestWriteEngineFailed(c *C) { 197 s.setUpTest(c) 198 defer s.tearDownTest() 199 200 ctx := context.Background() 201 rows := mock.NewMockRows(s.controller) 202 203 s.mockBackend.EXPECT().OpenEngine(ctx, &backend.EngineConfig{}, gomock.Any()).Return(nil) 204 mockWriter := mock.NewMockEngineWriter(s.controller) 205 206 s.mockBackend.EXPECT().LocalWriter(ctx, gomock.Any(), gomock.Any()).Return(mockWriter, nil).AnyTimes() 207 mockWriter.EXPECT(). 208 AppendRows(ctx, gomock.Any(), gomock.Any(), rows). 209 Return(errors.Annotate(context.Canceled, "fake unrecoverable write error")) 210 mockWriter.EXPECT().Close(ctx).Return(nil, nil) 211 212 engine, err := s.backend.OpenEngine(ctx, &backend.EngineConfig{}, "`db`.`table`", 1) 213 c.Assert(err, IsNil) 214 writer, err := engine.LocalWriter(ctx, &backend.LocalWriterConfig{}) 215 c.Assert(err, IsNil) 216 err = writer.WriteRows(ctx, nil, rows) 217 c.Assert(err, ErrorMatches, "fake unrecoverable write error.*") 218 _, err = writer.Close(ctx) 219 c.Assert(err, IsNil) 220 } 221 222 func (s *backendSuite) TestWriteBatchSendFailedWithRetry(c *C) { 223 s.setUpTest(c) 224 defer s.tearDownTest() 225 226 ctx := context.Background() 227 rows := mock.NewMockRows(s.controller) 228 229 s.mockBackend.EXPECT().OpenEngine(ctx, &backend.EngineConfig{}, gomock.Any()).Return(nil) 230 mockWriter := mock.NewMockEngineWriter(s.controller) 231 232 s.mockBackend.EXPECT().LocalWriter(ctx, gomock.Any(), gomock.Any()).Return(mockWriter, nil).AnyTimes() 233 mockWriter.EXPECT().AppendRows(ctx, gomock.Any(), gomock.Any(), rows). 234 Return(errors.New("fake recoverable write batch error")). 235 MinTimes(1) 236 mockWriter.EXPECT().Close(ctx).Return(nil, nil).MinTimes(1) 237 238 engine, err := s.backend.OpenEngine(ctx, &backend.EngineConfig{}, "`db`.`table`", 1) 239 c.Assert(err, IsNil) 240 writer, err := engine.LocalWriter(ctx, &backend.LocalWriterConfig{}) 241 c.Assert(err, IsNil) 242 err = writer.WriteRows(ctx, nil, rows) 243 c.Assert(err, ErrorMatches, ".*fake recoverable write batch error") 244 _, err = writer.Close(ctx) 245 c.Assert(err, IsNil) 246 } 247 248 func (s *backendSuite) TestImportFailedNoRetry(c *C) { 249 s.setUpTest(c) 250 defer s.tearDownTest() 251 252 ctx := context.Background() 253 254 s.mockBackend.EXPECT().CloseEngine(ctx, nil, gomock.Any()).Return(nil) 255 s.mockBackend.EXPECT(). 256 ImportEngine(ctx, gomock.Any()). 257 Return(errors.Annotate(context.Canceled, "fake unrecoverable import error")) 258 259 closedEngine, err := s.backend.UnsafeCloseEngine(ctx, nil, "`db`.`table`", 1) 260 c.Assert(err, IsNil) 261 err = closedEngine.Import(ctx) 262 c.Assert(err, ErrorMatches, "fake unrecoverable import error.*") 263 } 264 265 func (s *backendSuite) TestImportFailedWithRetry(c *C) { 266 s.setUpTest(c) 267 defer s.tearDownTest() 268 269 ctx := context.Background() 270 271 s.mockBackend.EXPECT().CloseEngine(ctx, nil, gomock.Any()).Return(nil) 272 s.mockBackend.EXPECT(). 273 ImportEngine(ctx, gomock.Any()). 274 Return(errors.New("fake recoverable import error")). 275 MinTimes(2) 276 s.mockBackend.EXPECT().RetryImportDelay().Return(time.Duration(0)).AnyTimes() 277 278 closedEngine, err := s.backend.UnsafeCloseEngine(ctx, nil, "`db`.`table`", 1) 279 c.Assert(err, IsNil) 280 err = closedEngine.Import(ctx) 281 c.Assert(err, ErrorMatches, ".*fake recoverable import error") 282 } 283 284 func (s *backendSuite) TestImportFailedRecovered(c *C) { 285 s.setUpTest(c) 286 defer s.tearDownTest() 287 288 ctx := context.Background() 289 290 s.mockBackend.EXPECT().CloseEngine(ctx, nil, gomock.Any()).Return(nil) 291 s.mockBackend.EXPECT(). 292 ImportEngine(ctx, gomock.Any()). 293 Return(errors.New("fake recoverable import error")) 294 s.mockBackend.EXPECT(). 295 ImportEngine(ctx, gomock.Any()). 296 Return(nil) 297 s.mockBackend.EXPECT().RetryImportDelay().Return(time.Duration(0)).AnyTimes() 298 299 closedEngine, err := s.backend.UnsafeCloseEngine(ctx, nil, "`db`.`table`", 1) 300 c.Assert(err, IsNil) 301 err = closedEngine.Import(ctx) 302 c.Assert(err, IsNil) 303 } 304 305 //nolint:interfacer // change test case signature causes check panicking. 306 func (s *backendSuite) TestClose(c *C) { 307 s.setUpTest(c) 308 defer s.tearDownTest() 309 310 s.mockBackend.EXPECT().Close().Return() 311 312 s.backend.Close() 313 } 314 315 func (s *backendSuite) TestMakeEmptyRows(c *C) { 316 s.setUpTest(c) 317 defer s.tearDownTest() 318 319 rows := mock.NewMockRows(s.controller) 320 s.mockBackend.EXPECT().MakeEmptyRows().Return(rows) 321 322 c.Assert(s.mockBackend.MakeEmptyRows(), Equals, rows) 323 } 324 325 func (s *backendSuite) TestNewEncoder(c *C) { 326 s.setUpTest(c) 327 defer s.tearDownTest() 328 329 encoder := mock.NewMockEncoder(s.controller) 330 options := &kv.SessionOptions{SQLMode: mysql.ModeANSIQuotes, Timestamp: 1234567890} 331 s.mockBackend.EXPECT().NewEncoder(nil, options).Return(encoder, nil) 332 333 realEncoder, err := s.mockBackend.NewEncoder(nil, options) 334 c.Assert(realEncoder, Equals, encoder) 335 c.Assert(err, IsNil) 336 } 337 338 func (s *backendSuite) TestCheckDiskQuota(c *C) { 339 s.setUpTest(c) 340 defer s.tearDownTest() 341 342 uuid1 := uuid.MustParse("11111111-1111-1111-1111-111111111111") 343 uuid3 := uuid.MustParse("33333333-3333-3333-3333-333333333333") 344 uuid5 := uuid.MustParse("55555555-5555-5555-5555-555555555555") 345 uuid7 := uuid.MustParse("77777777-7777-7777-7777-777777777777") 346 uuid9 := uuid.MustParse("99999999-9999-9999-9999-999999999999") 347 348 fileSizes := []backend.EngineFileSize{ 349 { 350 UUID: uuid1, 351 DiskSize: 1000, 352 MemSize: 0, 353 IsImporting: false, 354 }, 355 { 356 UUID: uuid3, 357 DiskSize: 2000, 358 MemSize: 1000, 359 IsImporting: true, 360 }, 361 { 362 UUID: uuid5, 363 DiskSize: 1500, 364 MemSize: 3500, 365 IsImporting: false, 366 }, 367 { 368 UUID: uuid7, 369 DiskSize: 0, 370 MemSize: 7000, 371 IsImporting: true, 372 }, 373 { 374 UUID: uuid9, 375 DiskSize: 4500, 376 MemSize: 4500, 377 IsImporting: false, 378 }, 379 } 380 381 s.mockBackend.EXPECT().EngineFileSizes().Return(fileSizes).Times(4) 382 383 // No quota exceeded 384 le, iple, ds, ms := s.backend.CheckDiskQuota(30000) 385 c.Assert(le, HasLen, 0) 386 c.Assert(iple, Equals, 0) 387 c.Assert(ds, Equals, int64(9000)) 388 c.Assert(ms, Equals, int64(16000)) 389 390 // Quota exceeded, the largest one is out 391 le, iple, ds, ms = s.backend.CheckDiskQuota(20000) 392 c.Assert(le, DeepEquals, []uuid.UUID{uuid9}) 393 c.Assert(iple, Equals, 0) 394 c.Assert(ds, Equals, int64(9000)) 395 c.Assert(ms, Equals, int64(16000)) 396 397 // Quota exceeded, the importing one should be ranked least priority 398 le, iple, ds, ms = s.backend.CheckDiskQuota(12000) 399 c.Assert(le, DeepEquals, []uuid.UUID{uuid5, uuid9}) 400 c.Assert(iple, Equals, 0) 401 c.Assert(ds, Equals, int64(9000)) 402 c.Assert(ms, Equals, int64(16000)) 403 404 // Quota exceeded, the importing ones should not be visible 405 le, iple, ds, ms = s.backend.CheckDiskQuota(5000) 406 c.Assert(le, DeepEquals, []uuid.UUID{uuid1, uuid5, uuid9}) 407 c.Assert(iple, Equals, 1) 408 c.Assert(ds, Equals, int64(9000)) 409 c.Assert(ms, Equals, int64(16000)) 410 }