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 }