github.com/pingcap/ticdc@v0.0.0-20220526033649-485a10ef2652/cdc/async_sink_test.go (about)

     1  // Copyright 2021 PingCAP, Inc.
     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  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package cdc
    15  
    16  import (
    17  	"context"
    18  	"sync"
    19  	"sync/atomic"
    20  	"time"
    21  
    22  	"github.com/pingcap/check"
    23  	"github.com/pingcap/errors"
    24  	"github.com/pingcap/ticdc/cdc/model"
    25  	"github.com/pingcap/ticdc/cdc/sink"
    26  	"github.com/pingcap/ticdc/pkg/config"
    27  	cdcContext "github.com/pingcap/ticdc/pkg/context"
    28  	cerror "github.com/pingcap/ticdc/pkg/errors"
    29  	"github.com/pingcap/ticdc/pkg/retry"
    30  	"github.com/pingcap/ticdc/pkg/util/testleak"
    31  )
    32  
    33  var _ = check.Suite(&asyncSinkSuite{})
    34  
    35  type asyncSinkSuite struct {
    36  }
    37  
    38  type mockAsyncSink struct {
    39  	sink.Sink
    40  	initTableInfo []*model.SimpleTableInfo
    41  	checkpointTs  model.Ts
    42  	ddl           *model.DDLEvent
    43  	ddlMu         sync.Mutex
    44  	ddlError      error
    45  }
    46  
    47  func (m *mockAsyncSink) Initialize(ctx context.Context, tableInfo []*model.SimpleTableInfo) error {
    48  	m.initTableInfo = tableInfo
    49  	return nil
    50  }
    51  
    52  func (m *mockAsyncSink) EmitCheckpointTs(ctx context.Context, ts uint64) error {
    53  	atomic.StoreUint64(&m.checkpointTs, ts)
    54  	return nil
    55  }
    56  
    57  func (m *mockAsyncSink) EmitDDLEvent(ctx context.Context, ddl *model.DDLEvent) error {
    58  	m.ddlMu.Lock()
    59  	defer m.ddlMu.Unlock()
    60  	time.Sleep(1 * time.Second)
    61  	m.ddl = ddl
    62  	return m.ddlError
    63  }
    64  
    65  func (m *mockAsyncSink) Close(ctx context.Context) error {
    66  	return nil
    67  }
    68  
    69  func (m *mockAsyncSink) Barrier(ctx context.Context) error {
    70  	return nil
    71  }
    72  
    73  func (m *mockAsyncSink) GetDDL() *model.DDLEvent {
    74  	m.ddlMu.Lock()
    75  	defer m.ddlMu.Unlock()
    76  	return m.ddl
    77  }
    78  
    79  func newAsyncSink4Test(ctx cdcContext.Context, c *check.C) (cdcContext.Context, AsyncSink, *mockAsyncSink) {
    80  	ctx = cdcContext.WithChangefeedVars(ctx, &cdcContext.ChangefeedVars{
    81  		ID:   "test-changefeed",
    82  		Info: &model.ChangeFeedInfo{SinkURI: "blackhole://", Config: config.GetDefaultReplicaConfig()},
    83  	})
    84  	sink, err := newAsyncSink(ctx)
    85  	c.Assert(err, check.IsNil)
    86  	mockAsyncSink := &mockAsyncSink{}
    87  	sink.(*asyncSinkImpl).sink = mockAsyncSink
    88  	return ctx, sink, mockAsyncSink
    89  }
    90  
    91  func (s *asyncSinkSuite) TestInitialize(c *check.C) {
    92  	defer testleak.AfterTest(c)()
    93  	ctx := cdcContext.NewBackendContext4Test(false)
    94  	ctx, sink, mockAsyncSink := newAsyncSink4Test(ctx, c)
    95  	defer sink.Close(ctx)
    96  	tableInfos := []*model.SimpleTableInfo{{Schema: "test"}}
    97  	err := sink.Initialize(ctx, tableInfos)
    98  	c.Assert(err, check.IsNil)
    99  	c.Assert(tableInfos, check.DeepEquals, mockAsyncSink.initTableInfo)
   100  }
   101  
   102  func (s *asyncSinkSuite) TestCheckpoint(c *check.C) {
   103  	defer testleak.AfterTest(c)()
   104  	ctx := cdcContext.NewBackendContext4Test(false)
   105  	ctx, sink, mSink := newAsyncSink4Test(ctx, c)
   106  	defer sink.Close(ctx)
   107  
   108  	waitCheckpointGrowingUp := func(m *mockAsyncSink, targetTs model.Ts) error {
   109  		return retry.Run(100*time.Millisecond, 30, func() error {
   110  			if targetTs != atomic.LoadUint64(&m.checkpointTs) {
   111  				return errors.New("targetTs!=checkpointTs")
   112  			}
   113  			return nil
   114  		})
   115  	}
   116  	err := sink.EmitCheckpointTs(ctx, 1)
   117  	c.Assert(err, check.IsNil)
   118  	c.Assert(waitCheckpointGrowingUp(mSink, 1), check.IsNil)
   119  	err = sink.EmitCheckpointTs(ctx, 10)
   120  	c.Assert(err, check.IsNil)
   121  	c.Assert(waitCheckpointGrowingUp(mSink, 10), check.IsNil)
   122  }
   123  
   124  func (s *asyncSinkSuite) TestExecDDL(c *check.C) {
   125  	defer testleak.AfterTest(c)()
   126  	ctx := cdcContext.NewBackendContext4Test(false)
   127  	ctx, sink, mSink := newAsyncSink4Test(ctx, c)
   128  	defer sink.Close(ctx)
   129  	ddl1 := &model.DDLEvent{CommitTs: 1}
   130  	for {
   131  		done, err := sink.EmitDDLEvent(ctx, ddl1)
   132  		c.Assert(err, check.IsNil)
   133  		if done {
   134  			c.Assert(mSink.GetDDL(), check.DeepEquals, ddl1)
   135  			break
   136  		}
   137  	}
   138  	ddl2 := &model.DDLEvent{CommitTs: 2}
   139  	ddl3 := &model.DDLEvent{CommitTs: 3}
   140  	_, err := sink.EmitDDLEvent(ctx, ddl2)
   141  	c.Assert(err, check.IsNil)
   142  	for {
   143  		done, err := sink.EmitDDLEvent(ctx, ddl2)
   144  		c.Assert(err, check.IsNil)
   145  		if done {
   146  			c.Assert(mSink.GetDDL(), check.DeepEquals, ddl2)
   147  			break
   148  		}
   149  	}
   150  	_, err = sink.EmitDDLEvent(ctx, ddl3)
   151  	c.Assert(err, check.IsNil)
   152  	for {
   153  		done, err := sink.EmitDDLEvent(ctx, ddl3)
   154  		c.Assert(err, check.IsNil)
   155  		if done {
   156  			c.Assert(mSink.GetDDL(), check.DeepEquals, ddl3)
   157  			break
   158  		}
   159  	}
   160  }
   161  
   162  func (s *asyncSinkSuite) TestExecDDLError(c *check.C) {
   163  	defer testleak.AfterTest(c)()
   164  	ctx := cdcContext.NewBackendContext4Test(false)
   165  	var resultErr error
   166  	var resultErrMu sync.Mutex
   167  	getResultErr := func() error {
   168  		resultErrMu.Lock()
   169  		defer resultErrMu.Unlock()
   170  		return resultErr
   171  	}
   172  	ctx = cdcContext.WithErrorHandler(ctx, func(err error) error {
   173  		resultErrMu.Lock()
   174  		defer resultErrMu.Unlock()
   175  		resultErr = err
   176  		return nil
   177  	})
   178  	ctx, sink, mSink := newAsyncSink4Test(ctx, c)
   179  	defer sink.Close(ctx)
   180  	mSink.ddlError = cerror.ErrDDLEventIgnored.GenWithStackByArgs()
   181  	ddl1 := &model.DDLEvent{CommitTs: 1}
   182  	for {
   183  		done, err := sink.EmitDDLEvent(ctx, ddl1)
   184  		c.Assert(err, check.IsNil)
   185  		if done {
   186  			c.Assert(mSink.GetDDL(), check.DeepEquals, ddl1)
   187  			break
   188  		}
   189  	}
   190  	c.Assert(getResultErr(), check.IsNil)
   191  	mSink.ddlError = cerror.ErrExecDDLFailed.GenWithStackByArgs()
   192  	ddl2 := &model.DDLEvent{CommitTs: 2}
   193  	for {
   194  		done, err := sink.EmitDDLEvent(ctx, ddl2)
   195  		c.Assert(err, check.IsNil)
   196  		if done || getResultErr() != nil {
   197  			c.Assert(mSink.GetDDL(), check.DeepEquals, ddl2)
   198  			break
   199  		}
   200  	}
   201  	c.Assert(cerror.ErrExecDDLFailed.Equal(errors.Cause(getResultErr())), check.IsTrue)
   202  }