github.com/pingcap/ticdc@v0.0.0-20220526033649-485a10ef2652/cdc/owner/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 owner
    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 mockSink 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 *mockSink) Initialize(ctx context.Context, tableInfo []*model.SimpleTableInfo) error {
    48  	m.initTableInfo = tableInfo
    49  	return nil
    50  }
    51  
    52  func (m *mockSink) EmitCheckpointTs(ctx context.Context, ts uint64) error {
    53  	atomic.StoreUint64(&m.checkpointTs, ts)
    54  	return nil
    55  }
    56  
    57  func (m *mockSink) 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 *mockSink) Close(ctx context.Context) error {
    66  	return nil
    67  }
    68  
    69  func (m *mockSink) Barrier(ctx context.Context) error {
    70  	return nil
    71  }
    72  
    73  func (m *mockSink) 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, *mockSink) {
    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  	mockSink := &mockSink{}
    87  	sink.(*asyncSinkImpl).sink = mockSink
    88  	return ctx, sink, mockSink
    89  }
    90  
    91  func (s *asyncSinkSuite) TestInitialize(c *check.C) {
    92  	defer testleak.AfterTest(c)()
    93  	ctx := cdcContext.NewBackendContext4Test(false)
    94  	ctx, sink, mockSink := 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, mockSink.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 *mockSink, targetTs model.Ts) error {
   109  		return retry.Do(context.Background(), func() error {
   110  			if targetTs != atomic.LoadUint64(&m.checkpointTs) {
   111  				return errors.New("targetTs!=checkpointTs")
   112  			}
   113  			return nil
   114  		}, retry.WithBackoffBaseDelay(100), retry.WithMaxTries(30))
   115  	}
   116  	sink.EmitCheckpointTs(ctx, 1)
   117  	c.Assert(waitCheckpointGrowingUp(mSink, 1), check.IsNil)
   118  	sink.EmitCheckpointTs(ctx, 10)
   119  	c.Assert(waitCheckpointGrowingUp(mSink, 10), check.IsNil)
   120  }
   121  
   122  func (s *asyncSinkSuite) TestExecDDL(c *check.C) {
   123  	defer testleak.AfterTest(c)()
   124  	ctx := cdcContext.NewBackendContext4Test(false)
   125  	ctx, sink, mSink := newAsyncSink4Test(ctx, c)
   126  	defer sink.Close(ctx)
   127  	ddl1 := &model.DDLEvent{CommitTs: 1}
   128  	for {
   129  		done, err := sink.EmitDDLEvent(ctx, ddl1)
   130  		c.Assert(err, check.IsNil)
   131  		if done {
   132  			c.Assert(mSink.GetDDL(), check.DeepEquals, ddl1)
   133  			break
   134  		}
   135  	}
   136  	ddl2 := &model.DDLEvent{CommitTs: 2}
   137  	ddl3 := &model.DDLEvent{CommitTs: 3}
   138  	_, err := sink.EmitDDLEvent(ctx, ddl2)
   139  	c.Assert(err, check.IsNil)
   140  	for {
   141  		done, err := sink.EmitDDLEvent(ctx, ddl2)
   142  		c.Assert(err, check.IsNil)
   143  		if done {
   144  			c.Assert(mSink.GetDDL(), check.DeepEquals, ddl2)
   145  			break
   146  		}
   147  	}
   148  	_, err = sink.EmitDDLEvent(ctx, ddl3)
   149  	c.Assert(err, check.IsNil)
   150  	for {
   151  		done, err := sink.EmitDDLEvent(ctx, ddl3)
   152  		c.Assert(err, check.IsNil)
   153  		if done {
   154  			c.Assert(mSink.GetDDL(), check.DeepEquals, ddl3)
   155  			break
   156  		}
   157  	}
   158  }
   159  
   160  func (s *asyncSinkSuite) TestExecDDLError(c *check.C) {
   161  	defer testleak.AfterTest(c)()
   162  	ctx := cdcContext.NewBackendContext4Test(false)
   163  	var resultErr error
   164  	var resultErrMu sync.Mutex
   165  	getResultErr := func() error {
   166  		resultErrMu.Lock()
   167  		defer resultErrMu.Unlock()
   168  		return resultErr
   169  	}
   170  	ctx = cdcContext.WithErrorHandler(ctx, func(err error) error {
   171  		resultErrMu.Lock()
   172  		defer resultErrMu.Unlock()
   173  		resultErr = err
   174  		return nil
   175  	})
   176  	ctx, sink, mSink := newAsyncSink4Test(ctx, c)
   177  	defer sink.Close(ctx)
   178  	mSink.ddlError = cerror.ErrDDLEventIgnored.GenWithStackByArgs()
   179  	ddl1 := &model.DDLEvent{CommitTs: 1}
   180  	for {
   181  		done, err := sink.EmitDDLEvent(ctx, ddl1)
   182  		c.Assert(err, check.IsNil)
   183  		if done {
   184  			c.Assert(mSink.GetDDL(), check.DeepEquals, ddl1)
   185  			break
   186  		}
   187  	}
   188  	c.Assert(getResultErr(), check.IsNil)
   189  	mSink.ddlError = cerror.ErrExecDDLFailed.GenWithStackByArgs()
   190  	ddl2 := &model.DDLEvent{CommitTs: 2}
   191  	for {
   192  		done, err := sink.EmitDDLEvent(ctx, ddl2)
   193  		c.Assert(err, check.IsNil)
   194  		if done || getResultErr() != nil {
   195  			c.Assert(mSink.GetDDL(), check.DeepEquals, ddl2)
   196  			break
   197  		}
   198  	}
   199  	c.Assert(cerror.ErrExecDDLFailed.Equal(errors.Cause(getResultErr())), check.IsTrue)
   200  }