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 }