github.com/pingcap/ticdc@v0.0.0-20220526033649-485a10ef2652/cdc/model/reactor_state_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 model 15 16 import ( 17 "time" 18 19 "github.com/google/go-cmp/cmp/cmpopts" 20 21 "github.com/google/go-cmp/cmp" 22 "github.com/pingcap/check" 23 "github.com/pingcap/ticdc/pkg/config" 24 "github.com/pingcap/ticdc/pkg/orchestrator" 25 "github.com/pingcap/ticdc/pkg/orchestrator/util" 26 "github.com/pingcap/ticdc/pkg/util/testleak" 27 ) 28 29 type stateSuite struct{} 30 31 var _ = check.Suite(&stateSuite{}) 32 33 func (s *stateSuite) TestCheckCaptureAlive(c *check.C) { 34 defer testleak.AfterTest(c)() 35 state := NewChangefeedReactorState("test") 36 stateTester := orchestrator.NewReactorStateTester(c, state, nil) 37 state.CheckCaptureAlive("6bbc01c8-0605-4f86-a0f9-b3119109b225") 38 c.Assert(stateTester.ApplyPatches(), check.ErrorMatches, ".*[CDC:ErrLeaseExpired].*") 39 err := stateTester.Update("/tidb/cdc/capture/6bbc01c8-0605-4f86-a0f9-b3119109b225", []byte(`{"id":"6bbc01c8-0605-4f86-a0f9-b3119109b225","address":"127.0.0.1:8300"}`)) 40 c.Assert(err, check.IsNil) 41 state.CheckCaptureAlive("6bbc01c8-0605-4f86-a0f9-b3119109b225") 42 stateTester.MustApplyPatches() 43 } 44 45 func (s *stateSuite) TestChangefeedStateUpdate(c *check.C) { 46 defer testleak.AfterTest(c)() 47 createTime, err := time.Parse("2006-01-02", "2020-02-02") 48 c.Assert(err, check.IsNil) 49 testCases := []struct { 50 changefeedID string 51 updateKey []string 52 updateValue []string 53 expected ChangefeedReactorState 54 }{ 55 { // common case 56 changefeedID: "test1", 57 updateKey: []string{ 58 "/tidb/cdc/changefeed/info/test1", 59 "/tidb/cdc/job/test1", 60 "/tidb/cdc/task/position/6bbc01c8-0605-4f86-a0f9-b3119109b225/test1", 61 "/tidb/cdc/task/status/6bbc01c8-0605-4f86-a0f9-b3119109b225/test1", 62 "/tidb/cdc/task/workload/6bbc01c8-0605-4f86-a0f9-b3119109b225/test1", 63 "/tidb/cdc/capture/6bbc01c8-0605-4f86-a0f9-b3119109b225", 64 }, 65 updateValue: []string{ 66 `{"sink-uri":"blackhole://","opts":{},"create-time":"2020-02-02T00:00:00.000000+00:00","start-ts":421980685886554116,"target-ts":0,"admin-job-type":0,"sort-engine":"memory","sort-dir":"","config":{"case-sensitive":true,"enable-old-value":false,"force-replicate":false,"check-gc-safe-point":true,"filter":{"rules":["*.*"],"ignore-txn-start-ts":null,"ddl-allow-list":null},"mounter":{"worker-num":16},"sink":{"dispatchers":null,"protocol":"default"},"cyclic-replication":{"enable":false,"replica-id":0,"filter-replica-ids":null,"id-buckets":0,"sync-ddl":false},"scheduler":{"type":"table-number","polling-time":-1}},"state":"normal","history":null,"error":null,"sync-point-enabled":false,"sync-point-interval":600000000000}`, 67 `{"resolved-ts":421980720003809281,"checkpoint-ts":421980719742451713,"admin-job-type":0}`, 68 `{"checkpoint-ts":421980720003809281,"resolved-ts":421980720003809281,"count":0,"error":null}`, 69 `{"tables":{"45":{"start-ts":421980685886554116,"mark-table-id":0}},"operation":null,"admin-job-type":0}`, 70 `{"45":{"workload":1}}`, 71 `{"id":"6bbc01c8-0605-4f86-a0f9-b3119109b225","address":"127.0.0.1:8300"}`, 72 }, 73 expected: ChangefeedReactorState{ 74 ID: "test1", 75 Info: &ChangeFeedInfo{ 76 SinkURI: "blackhole://", 77 Opts: map[string]string{}, 78 CreateTime: createTime, 79 StartTs: 421980685886554116, 80 Engine: SortInMemory, 81 State: "normal", 82 SyncPointInterval: time.Minute * 10, 83 Config: &config.ReplicaConfig{ 84 CaseSensitive: true, 85 CheckGCSafePoint: true, 86 Filter: &config.FilterConfig{Rules: []string{"*.*"}}, 87 Mounter: &config.MounterConfig{WorkerNum: 16}, 88 Sink: &config.SinkConfig{Protocol: "default"}, 89 Cyclic: &config.CyclicConfig{}, 90 Scheduler: &config.SchedulerConfig{Tp: "table-number", PollingTime: -1}, 91 }, 92 }, 93 Status: &ChangeFeedStatus{CheckpointTs: 421980719742451713, ResolvedTs: 421980720003809281}, 94 TaskStatuses: map[CaptureID]*TaskStatus{ 95 "6bbc01c8-0605-4f86-a0f9-b3119109b225": { 96 Tables: map[int64]*TableReplicaInfo{45: {StartTs: 421980685886554116}}, 97 }, 98 }, 99 TaskPositions: map[CaptureID]*TaskPosition{ 100 "6bbc01c8-0605-4f86-a0f9-b3119109b225": {CheckPointTs: 421980720003809281, ResolvedTs: 421980720003809281}, 101 }, 102 Workloads: map[CaptureID]TaskWorkload{ 103 "6bbc01c8-0605-4f86-a0f9-b3119109b225": {45: {Workload: 1}}, 104 }, 105 }, 106 }, 107 { // test multiple capture 108 changefeedID: "test1", 109 updateKey: []string{ 110 "/tidb/cdc/changefeed/info/test1", 111 "/tidb/cdc/job/test1", 112 "/tidb/cdc/task/position/6bbc01c8-0605-4f86-a0f9-b3119109b225/test1", 113 "/tidb/cdc/task/status/6bbc01c8-0605-4f86-a0f9-b3119109b225/test1", 114 "/tidb/cdc/task/workload/6bbc01c8-0605-4f86-a0f9-b3119109b225/test1", 115 "/tidb/cdc/capture/6bbc01c8-0605-4f86-a0f9-b3119109b225", 116 "/tidb/cdc/task/position/666777888/test1", 117 "/tidb/cdc/task/status/666777888/test1", 118 "/tidb/cdc/task/workload/666777888/test1", 119 "/tidb/cdc/capture/666777888", 120 }, 121 updateValue: []string{ 122 `{"sink-uri":"blackhole://","opts":{},"create-time":"2020-02-02T00:00:00.000000+00:00","start-ts":421980685886554116,"target-ts":0,"admin-job-type":0,"sort-engine":"memory","sort-dir":"","config":{"case-sensitive":true,"enable-old-value":false,"force-replicate":false,"check-gc-safe-point":true,"filter":{"rules":["*.*"],"ignore-txn-start-ts":null,"ddl-allow-list":null},"mounter":{"worker-num":16},"sink":{"dispatchers":null,"protocol":"default"},"cyclic-replication":{"enable":false,"replica-id":0,"filter-replica-ids":null,"id-buckets":0,"sync-ddl":false},"scheduler":{"type":"table-number","polling-time":-1}},"state":"normal","history":null,"error":null,"sync-point-enabled":false,"sync-point-interval":600000000000}`, 123 `{"resolved-ts":421980720003809281,"checkpoint-ts":421980719742451713,"admin-job-type":0}`, 124 `{"checkpoint-ts":421980720003809281,"resolved-ts":421980720003809281,"count":0,"error":null}`, 125 `{"tables":{"45":{"start-ts":421980685886554116,"mark-table-id":0}},"operation":null,"admin-job-type":0}`, 126 `{"45":{"workload":1}}`, 127 `{"id":"6bbc01c8-0605-4f86-a0f9-b3119109b225","address":"127.0.0.1:8300"}`, 128 `{"checkpoint-ts":11332244,"resolved-ts":312321,"count":8,"error":null}`, 129 `{"tables":{"46":{"start-ts":412341234,"mark-table-id":0}},"operation":null,"admin-job-type":0}`, 130 `{"46":{"workload":3}}`, 131 `{"id":"666777888","address":"127.0.0.1:8300"}`, 132 }, 133 expected: ChangefeedReactorState{ 134 ID: "test1", 135 Info: &ChangeFeedInfo{ 136 SinkURI: "blackhole://", 137 Opts: map[string]string{}, 138 CreateTime: createTime, 139 StartTs: 421980685886554116, 140 Engine: SortInMemory, 141 State: "normal", 142 SyncPointInterval: time.Minute * 10, 143 Config: &config.ReplicaConfig{ 144 CaseSensitive: true, 145 CheckGCSafePoint: true, 146 Filter: &config.FilterConfig{Rules: []string{"*.*"}}, 147 Mounter: &config.MounterConfig{WorkerNum: 16}, 148 Sink: &config.SinkConfig{Protocol: "default"}, 149 Cyclic: &config.CyclicConfig{}, 150 Scheduler: &config.SchedulerConfig{Tp: "table-number", PollingTime: -1}, 151 }, 152 }, 153 Status: &ChangeFeedStatus{CheckpointTs: 421980719742451713, ResolvedTs: 421980720003809281}, 154 TaskStatuses: map[CaptureID]*TaskStatus{ 155 "6bbc01c8-0605-4f86-a0f9-b3119109b225": { 156 Tables: map[int64]*TableReplicaInfo{45: {StartTs: 421980685886554116}}, 157 }, 158 "666777888": { 159 Tables: map[int64]*TableReplicaInfo{46: {StartTs: 412341234}}, 160 }, 161 }, 162 TaskPositions: map[CaptureID]*TaskPosition{ 163 "6bbc01c8-0605-4f86-a0f9-b3119109b225": {CheckPointTs: 421980720003809281, ResolvedTs: 421980720003809281}, 164 "666777888": {CheckPointTs: 11332244, ResolvedTs: 312321, Count: 8}, 165 }, 166 Workloads: map[CaptureID]TaskWorkload{ 167 "6bbc01c8-0605-4f86-a0f9-b3119109b225": {45: {Workload: 1}}, 168 "666777888": {46: {Workload: 3}}, 169 }, 170 }, 171 }, 172 { // testing changefeedID not match 173 changefeedID: "test1", 174 updateKey: []string{ 175 "/tidb/cdc/changefeed/info/test1", 176 "/tidb/cdc/job/test1", 177 "/tidb/cdc/task/position/6bbc01c8-0605-4f86-a0f9-b3119109b225/test1", 178 "/tidb/cdc/task/status/6bbc01c8-0605-4f86-a0f9-b3119109b225/test1", 179 "/tidb/cdc/task/workload/6bbc01c8-0605-4f86-a0f9-b3119109b225/test1", 180 "/tidb/cdc/capture/6bbc01c8-0605-4f86-a0f9-b3119109b225", 181 "/tidb/cdc/changefeed/info/test-fake", 182 "/tidb/cdc/job/test-fake", 183 "/tidb/cdc/task/position/6bbc01c8-0605-4f86-a0f9-b3119109b225/test-fake", 184 "/tidb/cdc/task/status/6bbc01c8-0605-4f86-a0f9-b3119109b225/test-fake", 185 "/tidb/cdc/task/workload/6bbc01c8-0605-4f86-a0f9-b3119109b225/test-fake", 186 }, 187 updateValue: []string{ 188 `{"sink-uri":"blackhole://","opts":{},"create-time":"2020-02-02T00:00:00.000000+00:00","start-ts":421980685886554116,"target-ts":0,"admin-job-type":0,"sort-engine":"memory","sort-dir":"","config":{"case-sensitive":true,"enable-old-value":false,"force-replicate":false,"check-gc-safe-point":true,"filter":{"rules":["*.*"],"ignore-txn-start-ts":null,"ddl-allow-list":null},"mounter":{"worker-num":16},"sink":{"dispatchers":null,"protocol":"default"},"cyclic-replication":{"enable":false,"replica-id":0,"filter-replica-ids":null,"id-buckets":0,"sync-ddl":false},"scheduler":{"type":"table-number","polling-time":-1}},"state":"normal","history":null,"error":null,"sync-point-enabled":false,"sync-point-interval":600000000000}`, 189 `{"resolved-ts":421980720003809281,"checkpoint-ts":421980719742451713,"admin-job-type":0}`, 190 `{"checkpoint-ts":421980720003809281,"resolved-ts":421980720003809281,"count":0,"error":null}`, 191 `{"tables":{"45":{"start-ts":421980685886554116,"mark-table-id":0}},"operation":null,"admin-job-type":0}`, 192 `{"45":{"workload":1}}`, 193 `{"id":"6bbc01c8-0605-4f86-a0f9-b3119109b225","address":"127.0.0.1:8300"}`, 194 `fake value`, 195 `fake value`, 196 `fake value`, 197 `fake value`, 198 `fake value`, 199 }, 200 expected: ChangefeedReactorState{ 201 ID: "test1", 202 Info: &ChangeFeedInfo{ 203 SinkURI: "blackhole://", 204 Opts: map[string]string{}, 205 CreateTime: createTime, 206 StartTs: 421980685886554116, 207 Engine: SortInMemory, 208 State: "normal", 209 SyncPointInterval: time.Minute * 10, 210 Config: &config.ReplicaConfig{ 211 CaseSensitive: true, 212 CheckGCSafePoint: true, 213 Filter: &config.FilterConfig{Rules: []string{"*.*"}}, 214 Mounter: &config.MounterConfig{WorkerNum: 16}, 215 Sink: &config.SinkConfig{Protocol: "default"}, 216 Cyclic: &config.CyclicConfig{}, 217 Scheduler: &config.SchedulerConfig{Tp: "table-number", PollingTime: -1}, 218 }, 219 }, 220 Status: &ChangeFeedStatus{CheckpointTs: 421980719742451713, ResolvedTs: 421980720003809281}, 221 TaskStatuses: map[CaptureID]*TaskStatus{ 222 "6bbc01c8-0605-4f86-a0f9-b3119109b225": { 223 Tables: map[int64]*TableReplicaInfo{45: {StartTs: 421980685886554116}}, 224 }, 225 }, 226 TaskPositions: map[CaptureID]*TaskPosition{ 227 "6bbc01c8-0605-4f86-a0f9-b3119109b225": {CheckPointTs: 421980720003809281, ResolvedTs: 421980720003809281}, 228 }, 229 Workloads: map[CaptureID]TaskWorkload{ 230 "6bbc01c8-0605-4f86-a0f9-b3119109b225": {45: {Workload: 1}}, 231 }, 232 }, 233 }, 234 { // testing value is nil 235 changefeedID: "test1", 236 updateKey: []string{ 237 "/tidb/cdc/changefeed/info/test1", 238 "/tidb/cdc/job/test1", 239 "/tidb/cdc/task/position/6bbc01c8-0605-4f86-a0f9-b3119109b225/test1", 240 "/tidb/cdc/task/status/6bbc01c8-0605-4f86-a0f9-b3119109b225/test1", 241 "/tidb/cdc/task/workload/6bbc01c8-0605-4f86-a0f9-b3119109b225/test1", 242 "/tidb/cdc/capture/6bbc01c8-0605-4f86-a0f9-b3119109b225", 243 "/tidb/cdc/task/position/666777888/test1", 244 "/tidb/cdc/task/status/666777888/test1", 245 "/tidb/cdc/task/workload/666777888/test1", 246 "/tidb/cdc/changefeed/info/test1", 247 "/tidb/cdc/job/test1", 248 "/tidb/cdc/task/position/6bbc01c8-0605-4f86-a0f9-b3119109b225/test1", 249 "/tidb/cdc/task/status/6bbc01c8-0605-4f86-a0f9-b3119109b225/test1", 250 "/tidb/cdc/task/workload/6bbc01c8-0605-4f86-a0f9-b3119109b225/test1", 251 "/tidb/cdc/capture/6bbc01c8-0605-4f86-a0f9-b3119109b225", 252 "/tidb/cdc/task/workload/666777888/test1", 253 "/tidb/cdc/task/status/666777888/test1", 254 }, 255 updateValue: []string{ 256 `{"sink-uri":"blackhole://","opts":{},"create-time":"2020-02-02T00:00:00.000000+00:00","start-ts":421980685886554116,"target-ts":0,"admin-job-type":0,"sort-engine":"memory","sort-dir":"","config":{"case-sensitive":true,"enable-old-value":false,"force-replicate":false,"check-gc-safe-point":true,"filter":{"rules":["*.*"],"ignore-txn-start-ts":null,"ddl-allow-list":null},"mounter":{"worker-num":16},"sink":{"dispatchers":null,"protocol":"default"},"cyclic-replication":{"enable":false,"replica-id":0,"filter-replica-ids":null,"id-buckets":0,"sync-ddl":false},"scheduler":{"type":"table-number","polling-time":-1}},"state":"normal","history":null,"error":null,"sync-point-enabled":false,"sync-point-interval":600000000000}`, 257 `{"resolved-ts":421980720003809281,"checkpoint-ts":421980719742451713,"admin-job-type":0}`, 258 `{"checkpoint-ts":421980720003809281,"resolved-ts":421980720003809281,"count":0,"error":null}`, 259 `{"tables":{"45":{"start-ts":421980685886554116,"mark-table-id":0}},"operation":null,"admin-job-type":0}`, 260 `{"45":{"workload":1}}`, 261 `{"id":"6bbc01c8-0605-4f86-a0f9-b3119109b225","address":"127.0.0.1:8300"}`, 262 `{"checkpoint-ts":11332244,"resolved-ts":312321,"count":8,"error":null}`, 263 `{"tables":{"46":{"start-ts":412341234,"mark-table-id":0}},"operation":null,"admin-job-type":0}`, 264 `{"46":{"workload":3}}`, 265 ``, 266 ``, 267 ``, 268 ``, 269 ``, 270 ``, 271 ``, 272 ``, 273 }, 274 expected: ChangefeedReactorState{ 275 ID: "test1", 276 Info: nil, 277 Status: nil, 278 TaskStatuses: map[CaptureID]*TaskStatus{}, 279 TaskPositions: map[CaptureID]*TaskPosition{ 280 "666777888": {CheckPointTs: 11332244, ResolvedTs: 312321, Count: 8}, 281 }, 282 Workloads: map[CaptureID]TaskWorkload{}, 283 }, 284 }, 285 { // testing the same key case 286 changefeedID: "test1", 287 updateKey: []string{ 288 "/tidb/cdc/task/status/6bbc01c8-0605-4f86-a0f9-b3119109b225/test1", 289 "/tidb/cdc/task/status/6bbc01c8-0605-4f86-a0f9-b3119109b225/test1", 290 "/tidb/cdc/task/status/6bbc01c8-0605-4f86-a0f9-b3119109b225/test1", 291 "/tidb/cdc/task/status/6bbc01c8-0605-4f86-a0f9-b3119109b225/test1", 292 }, 293 updateValue: []string{ 294 `{"tables":{"45":{"start-ts":421980685886554116,"mark-table-id":0}},"operation":null,"admin-job-type":0}`, 295 `{"tables":{"46":{"start-ts":421980685886554116,"mark-table-id":0}},"operation":null,"admin-job-type":0}`, 296 ``, 297 `{"tables":{"47":{"start-ts":421980685886554116,"mark-table-id":0}},"operation":null,"admin-job-type":0}`, 298 }, 299 expected: ChangefeedReactorState{ 300 ID: "test1", 301 TaskStatuses: map[CaptureID]*TaskStatus{ 302 "6bbc01c8-0605-4f86-a0f9-b3119109b225": { 303 Tables: map[int64]*TableReplicaInfo{47: {StartTs: 421980685886554116}}, 304 }, 305 }, 306 TaskPositions: map[CaptureID]*TaskPosition{}, 307 Workloads: map[CaptureID]TaskWorkload{}, 308 }, 309 }, 310 } 311 for i, tc := range testCases { 312 state := NewChangefeedReactorState(tc.changefeedID) 313 for i, k := range tc.updateKey { 314 value := []byte(tc.updateValue[i]) 315 if len(value) == 0 { 316 value = nil 317 } 318 err = state.Update(util.NewEtcdKey(k), value, false) 319 c.Assert(err, check.IsNil) 320 } 321 c.Assert(cmp.Equal(state, &tc.expected, cmpopts.IgnoreUnexported(ChangefeedReactorState{})), check.IsTrue, 322 check.Commentf("%d,%s", i, cmp.Diff(state, &tc.expected, cmpopts.IgnoreUnexported(ChangefeedReactorState{})))) 323 } 324 } 325 326 func (s *stateSuite) TestPatchInfo(c *check.C) { 327 defer testleak.AfterTest(c)() 328 state := NewChangefeedReactorState("test1") 329 stateTester := orchestrator.NewReactorStateTester(c, state, nil) 330 state.PatchInfo(func(info *ChangeFeedInfo) (*ChangeFeedInfo, bool, error) { 331 c.Assert(info, check.IsNil) 332 return &ChangeFeedInfo{SinkURI: "123", Config: &config.ReplicaConfig{}}, true, nil 333 }) 334 stateTester.MustApplyPatches() 335 defaultConfig := config.GetDefaultReplicaConfig() 336 c.Assert(state.Info, check.DeepEquals, &ChangeFeedInfo{ 337 SinkURI: "123", 338 Engine: SortUnified, 339 Config: &config.ReplicaConfig{ 340 Filter: defaultConfig.Filter, 341 Mounter: defaultConfig.Mounter, 342 Sink: defaultConfig.Sink, 343 Cyclic: defaultConfig.Cyclic, 344 Scheduler: defaultConfig.Scheduler, 345 }, 346 }) 347 state.PatchInfo(func(info *ChangeFeedInfo) (*ChangeFeedInfo, bool, error) { 348 info.StartTs = 6 349 return info, true, nil 350 }) 351 stateTester.MustApplyPatches() 352 c.Assert(state.Info, check.DeepEquals, &ChangeFeedInfo{ 353 SinkURI: "123", 354 StartTs: 6, 355 Engine: SortUnified, 356 Config: &config.ReplicaConfig{ 357 Filter: defaultConfig.Filter, 358 Mounter: defaultConfig.Mounter, 359 Sink: defaultConfig.Sink, 360 Cyclic: defaultConfig.Cyclic, 361 Scheduler: defaultConfig.Scheduler, 362 }, 363 }) 364 state.PatchInfo(func(info *ChangeFeedInfo) (*ChangeFeedInfo, bool, error) { 365 return nil, true, nil 366 }) 367 stateTester.MustApplyPatches() 368 c.Assert(state.Info, check.IsNil) 369 } 370 371 func (s *stateSuite) TestPatchStatus(c *check.C) { 372 defer testleak.AfterTest(c)() 373 state := NewChangefeedReactorState("test1") 374 stateTester := orchestrator.NewReactorStateTester(c, state, nil) 375 state.PatchStatus(func(status *ChangeFeedStatus) (*ChangeFeedStatus, bool, error) { 376 c.Assert(status, check.IsNil) 377 return &ChangeFeedStatus{CheckpointTs: 5}, true, nil 378 }) 379 stateTester.MustApplyPatches() 380 c.Assert(state.Status, check.DeepEquals, &ChangeFeedStatus{CheckpointTs: 5}) 381 state.PatchStatus(func(status *ChangeFeedStatus) (*ChangeFeedStatus, bool, error) { 382 status.ResolvedTs = 6 383 return status, true, nil 384 }) 385 stateTester.MustApplyPatches() 386 c.Assert(state.Status, check.DeepEquals, &ChangeFeedStatus{CheckpointTs: 5, ResolvedTs: 6}) 387 state.PatchStatus(func(status *ChangeFeedStatus) (*ChangeFeedStatus, bool, error) { 388 return nil, true, nil 389 }) 390 stateTester.MustApplyPatches() 391 c.Assert(state.Status, check.IsNil) 392 } 393 394 func (s *stateSuite) TestPatchTaskPosition(c *check.C) { 395 defer testleak.AfterTest(c)() 396 state := NewChangefeedReactorState("test1") 397 stateTester := orchestrator.NewReactorStateTester(c, state, nil) 398 captureID1 := "capture1" 399 captureID2 := "capture2" 400 state.PatchTaskPosition(captureID1, func(position *TaskPosition) (*TaskPosition, bool, error) { 401 c.Assert(position, check.IsNil) 402 return &TaskPosition{ 403 CheckPointTs: 1, 404 }, true, nil 405 }) 406 state.PatchTaskPosition(captureID2, func(position *TaskPosition) (*TaskPosition, bool, error) { 407 c.Assert(position, check.IsNil) 408 return &TaskPosition{ 409 CheckPointTs: 2, 410 }, true, nil 411 }) 412 stateTester.MustApplyPatches() 413 c.Assert(state.TaskPositions, check.DeepEquals, map[string]*TaskPosition{ 414 captureID1: { 415 CheckPointTs: 1, 416 }, 417 captureID2: { 418 CheckPointTs: 2, 419 }, 420 }) 421 state.PatchTaskPosition(captureID1, func(position *TaskPosition) (*TaskPosition, bool, error) { 422 position.CheckPointTs = 3 423 return position, true, nil 424 }) 425 state.PatchTaskPosition(captureID2, func(position *TaskPosition) (*TaskPosition, bool, error) { 426 position.ResolvedTs = 2 427 return position, true, nil 428 }) 429 stateTester.MustApplyPatches() 430 c.Assert(state.TaskPositions, check.DeepEquals, map[string]*TaskPosition{ 431 captureID1: { 432 CheckPointTs: 3, 433 }, 434 captureID2: { 435 CheckPointTs: 2, 436 ResolvedTs: 2, 437 }, 438 }) 439 state.PatchTaskPosition(captureID1, func(position *TaskPosition) (*TaskPosition, bool, error) { 440 return nil, false, nil 441 }) 442 state.PatchTaskPosition(captureID2, func(position *TaskPosition) (*TaskPosition, bool, error) { 443 return nil, true, nil 444 }) 445 state.PatchTaskPosition(captureID1, func(position *TaskPosition) (*TaskPosition, bool, error) { 446 position.Count = 6 447 return position, true, nil 448 }) 449 stateTester.MustApplyPatches() 450 c.Assert(state.TaskPositions, check.DeepEquals, map[string]*TaskPosition{ 451 captureID1: { 452 CheckPointTs: 3, 453 Count: 6, 454 }, 455 }) 456 } 457 458 func (s *stateSuite) TestPatchTaskStatus(c *check.C) { 459 defer testleak.AfterTest(c)() 460 state := NewChangefeedReactorState("test1") 461 stateTester := orchestrator.NewReactorStateTester(c, state, nil) 462 captureID1 := "capture1" 463 captureID2 := "capture2" 464 state.PatchTaskStatus(captureID1, func(status *TaskStatus) (*TaskStatus, bool, error) { 465 c.Assert(status, check.IsNil) 466 return &TaskStatus{ 467 Tables: map[TableID]*TableReplicaInfo{45: {StartTs: 1}}, 468 }, true, nil 469 }) 470 state.PatchTaskStatus(captureID2, func(status *TaskStatus) (*TaskStatus, bool, error) { 471 c.Assert(status, check.IsNil) 472 return &TaskStatus{ 473 Tables: map[TableID]*TableReplicaInfo{46: {StartTs: 1}}, 474 }, true, nil 475 }) 476 stateTester.MustApplyPatches() 477 c.Assert(state.TaskStatuses, check.DeepEquals, map[CaptureID]*TaskStatus{ 478 captureID1: {Tables: map[TableID]*TableReplicaInfo{45: {StartTs: 1}}}, 479 captureID2: {Tables: map[TableID]*TableReplicaInfo{46: {StartTs: 1}}}, 480 }) 481 state.PatchTaskStatus(captureID1, func(status *TaskStatus) (*TaskStatus, bool, error) { 482 status.Tables[46] = &TableReplicaInfo{StartTs: 2} 483 return status, true, nil 484 }) 485 state.PatchTaskStatus(captureID2, func(status *TaskStatus) (*TaskStatus, bool, error) { 486 status.Tables[46].StartTs++ 487 return status, true, nil 488 }) 489 stateTester.MustApplyPatches() 490 c.Assert(state.TaskStatuses, check.DeepEquals, map[CaptureID]*TaskStatus{ 491 captureID1: {Tables: map[TableID]*TableReplicaInfo{45: {StartTs: 1}, 46: {StartTs: 2}}}, 492 captureID2: {Tables: map[TableID]*TableReplicaInfo{46: {StartTs: 2}}}, 493 }) 494 state.PatchTaskStatus(captureID2, func(status *TaskStatus) (*TaskStatus, bool, error) { 495 return nil, true, nil 496 }) 497 stateTester.MustApplyPatches() 498 c.Assert(state.TaskStatuses, check.DeepEquals, map[CaptureID]*TaskStatus{ 499 captureID1: {Tables: map[TableID]*TableReplicaInfo{45: {StartTs: 1}, 46: {StartTs: 2}}}, 500 }) 501 } 502 503 func (s *stateSuite) TestPatchTaskWorkload(c *check.C) { 504 defer testleak.AfterTest(c)() 505 state := NewChangefeedReactorState("test1") 506 stateTester := orchestrator.NewReactorStateTester(c, state, nil) 507 captureID1 := "capture1" 508 captureID2 := "capture2" 509 state.PatchTaskWorkload(captureID1, func(workload TaskWorkload) (TaskWorkload, bool, error) { 510 c.Assert(workload, check.IsNil) 511 return TaskWorkload{45: {Workload: 1}}, true, nil 512 }) 513 state.PatchTaskWorkload(captureID2, func(workload TaskWorkload) (TaskWorkload, bool, error) { 514 c.Assert(workload, check.IsNil) 515 return TaskWorkload{46: {Workload: 1}}, true, nil 516 }) 517 stateTester.MustApplyPatches() 518 c.Assert(state.Workloads, check.DeepEquals, map[CaptureID]TaskWorkload{ 519 captureID1: {45: {Workload: 1}}, 520 captureID2: {46: {Workload: 1}}, 521 }) 522 state.PatchTaskWorkload(captureID1, func(workload TaskWorkload) (TaskWorkload, bool, error) { 523 workload[46] = WorkloadInfo{Workload: 2} 524 return workload, true, nil 525 }) 526 state.PatchTaskWorkload(captureID2, func(workload TaskWorkload) (TaskWorkload, bool, error) { 527 workload[45] = WorkloadInfo{Workload: 3} 528 return workload, true, nil 529 }) 530 stateTester.MustApplyPatches() 531 c.Assert(state.Workloads, check.DeepEquals, map[CaptureID]TaskWorkload{ 532 captureID1: {45: {Workload: 1}, 46: {Workload: 2}}, 533 captureID2: {45: {Workload: 3}, 46: {Workload: 1}}, 534 }) 535 state.PatchTaskWorkload(captureID2, func(workload TaskWorkload) (TaskWorkload, bool, error) { 536 return nil, true, nil 537 }) 538 stateTester.MustApplyPatches() 539 c.Assert(state.Workloads, check.DeepEquals, map[CaptureID]TaskWorkload{ 540 captureID1: {45: {Workload: 1}, 46: {Workload: 2}}, 541 }) 542 } 543 544 func (s *stateSuite) TestGlobalStateUpdate(c *check.C) { 545 defer testleak.AfterTest(c)() 546 testCases := []struct { 547 updateKey []string 548 updateValue []string 549 expected GlobalReactorState 550 }{ 551 { // common case 552 updateKey: []string{ 553 "/tidb/cdc/owner/22317526c4fc9a37", 554 "/tidb/cdc/owner/22317526c4fc9a38", 555 "/tidb/cdc/capture/6bbc01c8-0605-4f86-a0f9-b3119109b225", 556 "/tidb/cdc/task/position/6bbc01c8-0605-4f86-a0f9-b3119109b225/test1", 557 "/tidb/cdc/task/workload/6bbc01c8-0605-4f86-a0f9-b3119109b225/test2", 558 "/tidb/cdc/task/workload/55551111/test2", 559 }, 560 updateValue: []string{ 561 `6bbc01c8-0605-4f86-a0f9-b3119109b225`, 562 `55551111`, 563 `{"id":"6bbc01c8-0605-4f86-a0f9-b3119109b225","address":"127.0.0.1:8300"}`, 564 `{"resolved-ts":421980720003809281,"checkpoint-ts":421980719742451713,"admin-job-type":0}`, 565 `{"45":{"workload":1}}`, 566 `{"46":{"workload":1}}`, 567 }, 568 expected: GlobalReactorState{ 569 Owner: map[string]struct{}{"22317526c4fc9a37": {}, "22317526c4fc9a38": {}}, 570 Captures: map[CaptureID]*CaptureInfo{"6bbc01c8-0605-4f86-a0f9-b3119109b225": { 571 ID: "6bbc01c8-0605-4f86-a0f9-b3119109b225", 572 AdvertiseAddr: "127.0.0.1:8300", 573 }}, 574 Changefeeds: map[ChangeFeedID]*ChangefeedReactorState{ 575 "test1": { 576 ID: "test1", 577 TaskStatuses: map[string]*TaskStatus{}, 578 TaskPositions: map[CaptureID]*TaskPosition{ 579 "6bbc01c8-0605-4f86-a0f9-b3119109b225": {CheckPointTs: 421980719742451713, ResolvedTs: 421980720003809281}, 580 }, 581 Workloads: map[string]TaskWorkload{}, 582 }, 583 "test2": { 584 ID: "test2", 585 TaskStatuses: map[string]*TaskStatus{}, 586 TaskPositions: map[CaptureID]*TaskPosition{}, 587 Workloads: map[CaptureID]TaskWorkload{ 588 "6bbc01c8-0605-4f86-a0f9-b3119109b225": {45: {Workload: 1}}, 589 "55551111": {46: {Workload: 1}}, 590 }, 591 }, 592 }, 593 }, 594 }, 595 { // testing remove changefeed 596 updateKey: []string{ 597 "/tidb/cdc/owner/22317526c4fc9a37", 598 "/tidb/cdc/owner/22317526c4fc9a38", 599 "/tidb/cdc/capture/6bbc01c8-0605-4f86-a0f9-b3119109b225", 600 "/tidb/cdc/task/position/6bbc01c8-0605-4f86-a0f9-b3119109b225/test1", 601 "/tidb/cdc/task/workload/6bbc01c8-0605-4f86-a0f9-b3119109b225/test2", 602 "/tidb/cdc/task/workload/55551111/test2", 603 "/tidb/cdc/owner/22317526c4fc9a37", 604 "/tidb/cdc/task/position/6bbc01c8-0605-4f86-a0f9-b3119109b225/test1", 605 "/tidb/cdc/task/workload/6bbc01c8-0605-4f86-a0f9-b3119109b225/test2", 606 "/tidb/cdc/capture/6bbc01c8-0605-4f86-a0f9-b3119109b225", 607 }, 608 updateValue: []string{ 609 `6bbc01c8-0605-4f86-a0f9-b3119109b225`, 610 `55551111`, 611 `{"id":"6bbc01c8-0605-4f86-a0f9-b3119109b225","address":"127.0.0.1:8300"}`, 612 `{"resolved-ts":421980720003809281,"checkpoint-ts":421980719742451713,"admin-job-type":0}`, 613 `{"45":{"workload":1}}`, 614 `{"46":{"workload":1}}`, 615 ``, 616 ``, 617 ``, 618 ``, 619 }, 620 expected: GlobalReactorState{ 621 Owner: map[string]struct{}{"22317526c4fc9a38": {}}, 622 Captures: map[CaptureID]*CaptureInfo{}, 623 Changefeeds: map[ChangeFeedID]*ChangefeedReactorState{ 624 "test2": { 625 ID: "test2", 626 TaskStatuses: map[string]*TaskStatus{}, 627 TaskPositions: map[CaptureID]*TaskPosition{}, 628 Workloads: map[CaptureID]TaskWorkload{ 629 "55551111": {46: {Workload: 1}}, 630 }, 631 }, 632 }, 633 }, 634 }, 635 } 636 for _, tc := range testCases { 637 state := NewGlobalState() 638 for i, k := range tc.updateKey { 639 value := []byte(tc.updateValue[i]) 640 if len(value) == 0 { 641 value = nil 642 } 643 err := state.Update(util.NewEtcdKey(k), value, false) 644 c.Assert(err, check.IsNil) 645 } 646 c.Assert(cmp.Equal(state, &tc.expected, cmpopts.IgnoreUnexported(GlobalReactorState{}, ChangefeedReactorState{})), check.IsTrue, 647 check.Commentf("%s", cmp.Diff(state, &tc.expected, cmpopts.IgnoreUnexported(GlobalReactorState{}, ChangefeedReactorState{})))) 648 } 649 } 650 651 func (s *stateSuite) TestCheckChangefeedNormal(c *check.C) { 652 defer testleak.AfterTest(c)() 653 state := NewChangefeedReactorState("test1") 654 stateTester := orchestrator.NewReactorStateTester(c, state, nil) 655 state.CheckChangefeedNormal() 656 stateTester.MustApplyPatches() 657 state.PatchInfo(func(info *ChangeFeedInfo) (*ChangeFeedInfo, bool, error) { 658 return &ChangeFeedInfo{SinkURI: "123", AdminJobType: AdminNone, Config: &config.ReplicaConfig{}}, true, nil 659 }) 660 state.PatchStatus(func(status *ChangeFeedStatus) (*ChangeFeedStatus, bool, error) { 661 return &ChangeFeedStatus{ResolvedTs: 1, AdminJobType: AdminNone}, true, nil 662 }) 663 state.CheckChangefeedNormal() 664 stateTester.MustApplyPatches() 665 c.Assert(state.Status.ResolvedTs, check.Equals, uint64(1)) 666 667 state.PatchInfo(func(info *ChangeFeedInfo) (*ChangeFeedInfo, bool, error) { 668 info.AdminJobType = AdminStop 669 return info, true, nil 670 }) 671 state.PatchStatus(func(status *ChangeFeedStatus) (*ChangeFeedStatus, bool, error) { 672 status.ResolvedTs = 2 673 return status, true, nil 674 }) 675 state.CheckChangefeedNormal() 676 stateTester.MustApplyPatches() 677 c.Assert(state.Status.ResolvedTs, check.Equals, uint64(1)) 678 679 state.PatchStatus(func(status *ChangeFeedStatus) (*ChangeFeedStatus, bool, error) { 680 status.ResolvedTs = 2 681 return status, true, nil 682 }) 683 state.CheckChangefeedNormal() 684 stateTester.MustApplyPatches() 685 c.Assert(state.Status.ResolvedTs, check.Equals, uint64(2)) 686 }