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  }