github.com/pingcap/ticdc@v0.0.0-20220526033649-485a10ef2652/cdc/model/owner.go (about) 1 // Copyright 2020 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 "encoding/json" 18 "fmt" 19 "math" 20 21 "github.com/pingcap/errors" 22 "github.com/pingcap/log" 23 cerror "github.com/pingcap/ticdc/pkg/errors" 24 "go.uber.org/zap" 25 ) 26 27 // AdminJobType represents for admin job type, both used in owner and processor 28 type AdminJobType int 29 30 // AdminJobOption records addition options of an admin job 31 type AdminJobOption struct { 32 ForceRemove bool 33 } 34 35 // AdminJob holds an admin job 36 type AdminJob struct { 37 CfID string 38 Type AdminJobType 39 Opts *AdminJobOption 40 Error *RunningError 41 } 42 43 // All AdminJob types 44 const ( 45 AdminNone AdminJobType = iota 46 AdminStop 47 AdminResume 48 AdminRemove 49 AdminFinish 50 ) 51 52 // String implements fmt.Stringer interface. 53 func (t AdminJobType) String() string { 54 switch t { 55 case AdminNone: 56 return "noop" 57 case AdminStop: 58 return "stop changefeed" 59 case AdminResume: 60 return "resume changefeed" 61 case AdminRemove: 62 return "remove changefeed" 63 case AdminFinish: 64 return "finish changefeed" 65 } 66 return "unknown" 67 } 68 69 // IsStopState returns whether changefeed is in stop state with give admin job 70 func (t AdminJobType) IsStopState() bool { 71 switch t { 72 case AdminStop, AdminRemove, AdminFinish: 73 return true 74 } 75 return false 76 } 77 78 // TaskPosition records the process information of a capture 79 type TaskPosition struct { 80 // The maximum event CommitTs that has been synchronized. This is updated by corresponding processor. 81 CheckPointTs uint64 `json:"checkpoint-ts"` 82 // The event that satisfies CommitTs <= ResolvedTs can be synchronized. This is updated by corresponding processor. 83 ResolvedTs uint64 `json:"resolved-ts"` 84 // The count of events were synchronized. This is updated by corresponding processor. 85 Count uint64 `json:"count"` 86 // Error code when error happens 87 Error *RunningError `json:"error"` 88 } 89 90 // Marshal returns the json marshal format of a TaskStatus 91 func (tp *TaskPosition) Marshal() (string, error) { 92 data, err := json.Marshal(tp) 93 return string(data), cerror.WrapError(cerror.ErrMarshalFailed, err) 94 } 95 96 // Unmarshal unmarshals into *TaskStatus from json marshal byte slice 97 func (tp *TaskPosition) Unmarshal(data []byte) error { 98 err := json.Unmarshal(data, tp) 99 return errors.Annotatef( 100 cerror.WrapError(cerror.ErrUnmarshalFailed, err), "Unmarshal data: %v", data) 101 } 102 103 // String implements fmt.Stringer interface. 104 func (tp *TaskPosition) String() string { 105 data, _ := tp.Marshal() 106 return data 107 } 108 109 // MoveTableStatus represents for the status of a MoveTableJob 110 type MoveTableStatus int 111 112 // All MoveTable status 113 const ( 114 MoveTableStatusNone MoveTableStatus = iota 115 MoveTableStatusDeleted 116 MoveTableStatusFinished 117 ) 118 119 // MoveTableJob records a move operation of a table 120 type MoveTableJob struct { 121 From CaptureID 122 To CaptureID 123 TableID TableID 124 TableReplicaInfo *TableReplicaInfo 125 Status MoveTableStatus 126 } 127 128 // All TableOperation flags 129 const ( 130 // Move means after the delete operation, the table will be re added. 131 // This field is necessary since we must persist enough information to 132 // restore complete table operation in case of processor or owner crashes. 133 OperFlagMoveTable uint64 = 1 << iota 134 ) 135 136 // All TableOperation status 137 const ( 138 OperDispatched uint64 = iota 139 OperProcessed 140 OperFinished 141 ) 142 143 // TableOperation records the current information of a table migration 144 type TableOperation struct { 145 Done bool `json:"done"` // deprecated, will be removed in the next version 146 Delete bool `json:"delete"` 147 Flag uint64 `json:"flag,omitempty"` 148 // if the operation is a delete operation, BoundaryTs is checkpoint ts 149 // if the operation is a add operation, BoundaryTs is start ts 150 BoundaryTs uint64 `json:"boundary_ts"` 151 Status uint64 `json:"status,omitempty"` 152 } 153 154 // TableProcessed returns whether the table has been processed by processor 155 func (o *TableOperation) TableProcessed() bool { 156 // TODO: remove o.Done 157 return o.Status == OperProcessed || o.Status == OperFinished || o.Done 158 } 159 160 // TableApplied returns whether the table has finished the startup procedure. 161 // Returns true if table has been processed by processor and resolved ts reaches global resolved ts. 162 func (o *TableOperation) TableApplied() bool { 163 // TODO: remove o.Done 164 return o.Status == OperFinished || o.Done 165 } 166 167 // Clone returns a deep-clone of the struct 168 func (o *TableOperation) Clone() *TableOperation { 169 if o == nil { 170 return nil 171 } 172 clone := *o 173 return &clone 174 } 175 176 // TaskWorkload records the workloads of a task 177 // the value of the struct is the workload 178 type TaskWorkload map[TableID]WorkloadInfo 179 180 // WorkloadInfo records the workload info of a table 181 type WorkloadInfo struct { 182 Workload uint64 `json:"workload"` 183 } 184 185 // Unmarshal unmarshals into *TaskWorkload from json marshal byte slice 186 func (w *TaskWorkload) Unmarshal(data []byte) error { 187 err := json.Unmarshal(data, w) 188 return errors.Annotatef( 189 cerror.WrapError(cerror.ErrUnmarshalFailed, err), "Unmarshal data: %v", data) 190 } 191 192 // Marshal returns the json marshal format of a TaskWorkload 193 func (w *TaskWorkload) Marshal() (string, error) { 194 if w == nil { 195 return "{}", nil 196 } 197 data, err := json.Marshal(w) 198 return string(data), cerror.WrapError(cerror.ErrMarshalFailed, err) 199 } 200 201 // TableReplicaInfo records the table replica info 202 type TableReplicaInfo struct { 203 StartTs Ts `json:"start-ts"` 204 MarkTableID TableID `json:"mark-table-id"` 205 } 206 207 // Clone clones a TableReplicaInfo 208 func (i *TableReplicaInfo) Clone() *TableReplicaInfo { 209 if i == nil { 210 return nil 211 } 212 clone := *i 213 return &clone 214 } 215 216 // TaskStatus records the task information of a capture 217 type TaskStatus struct { 218 // Table information list, containing tables that processor should process, updated by ownrer, processor is read only. 219 Tables map[TableID]*TableReplicaInfo `json:"tables"` 220 Operation map[TableID]*TableOperation `json:"operation"` 221 AdminJobType AdminJobType `json:"admin-job-type"` 222 ModRevision int64 `json:"-"` 223 // true means Operation record has been changed 224 Dirty bool `json:"-"` 225 } 226 227 // String implements fmt.Stringer interface. 228 func (ts *TaskStatus) String() string { 229 data, _ := ts.Marshal() 230 return data 231 } 232 233 // RemoveTable remove the table in TableInfos and add a remove table operation. 234 func (ts *TaskStatus) RemoveTable(id TableID, boundaryTs Ts, isMoveTable bool) (*TableReplicaInfo, bool) { 235 if ts.Tables == nil { 236 return nil, false 237 } 238 table, exist := ts.Tables[id] 239 if !exist { 240 return nil, false 241 } 242 delete(ts.Tables, id) 243 log.Info("remove a table", zap.Int64("tableId", id), zap.Uint64("boundaryTs", boundaryTs), zap.Bool("isMoveTable", isMoveTable)) 244 if ts.Operation == nil { 245 ts.Operation = make(map[TableID]*TableOperation) 246 } 247 op := &TableOperation{ 248 Delete: true, 249 BoundaryTs: boundaryTs, 250 } 251 if isMoveTable { 252 op.Flag |= OperFlagMoveTable 253 } 254 ts.Operation[id] = op 255 return table, true 256 } 257 258 // AddTable add the table in TableInfos and add a add table operation. 259 func (ts *TaskStatus) AddTable(id TableID, table *TableReplicaInfo, boundaryTs Ts) { 260 if ts.Tables == nil { 261 ts.Tables = make(map[TableID]*TableReplicaInfo) 262 } 263 _, exist := ts.Tables[id] 264 if exist { 265 return 266 } 267 ts.Tables[id] = table 268 log.Info("add a table", zap.Int64("tableId", id), zap.Uint64("boundaryTs", boundaryTs)) 269 if ts.Operation == nil { 270 ts.Operation = make(map[TableID]*TableOperation) 271 } 272 ts.Operation[id] = &TableOperation{ 273 Delete: false, 274 BoundaryTs: boundaryTs, 275 Status: OperDispatched, 276 } 277 } 278 279 // SomeOperationsUnapplied returns true if there are some operations not applied 280 func (ts *TaskStatus) SomeOperationsUnapplied() bool { 281 for _, o := range ts.Operation { 282 if !o.TableApplied() { 283 return true 284 } 285 } 286 return false 287 } 288 289 // AppliedTs returns a Ts which less or equal to the ts boundary of any unapplied operation 290 func (ts *TaskStatus) AppliedTs() Ts { 291 appliedTs := uint64(math.MaxUint64) 292 for _, o := range ts.Operation { 293 if !o.TableApplied() { 294 if appliedTs > o.BoundaryTs { 295 appliedTs = o.BoundaryTs 296 } 297 } 298 } 299 return appliedTs 300 } 301 302 // Snapshot takes a snapshot of `*TaskStatus` and returns a new `*ProcInfoSnap` 303 func (ts *TaskStatus) Snapshot(cfID ChangeFeedID, captureID CaptureID, checkpointTs Ts) *ProcInfoSnap { 304 snap := &ProcInfoSnap{ 305 CfID: cfID, 306 CaptureID: captureID, 307 Tables: make(map[TableID]*TableReplicaInfo, len(ts.Tables)), 308 } 309 for tableID, table := range ts.Tables { 310 ts := checkpointTs 311 if ts < table.StartTs { 312 ts = table.StartTs 313 } 314 snap.Tables[tableID] = &TableReplicaInfo{ 315 StartTs: ts, 316 MarkTableID: table.MarkTableID, 317 } 318 } 319 return snap 320 } 321 322 // Marshal returns the json marshal format of a TaskStatus 323 func (ts *TaskStatus) Marshal() (string, error) { 324 data, err := json.Marshal(ts) 325 return string(data), cerror.WrapError(cerror.ErrMarshalFailed, err) 326 } 327 328 // Unmarshal unmarshals into *TaskStatus from json marshal byte slice 329 func (ts *TaskStatus) Unmarshal(data []byte) error { 330 err := json.Unmarshal(data, ts) 331 return errors.Annotatef( 332 cerror.WrapError(cerror.ErrUnmarshalFailed, err), "Unmarshal data: %v", data) 333 } 334 335 // Clone returns a deep-clone of the struct 336 func (ts *TaskStatus) Clone() *TaskStatus { 337 clone := *ts 338 tables := make(map[TableID]*TableReplicaInfo, len(ts.Tables)) 339 for tableID, table := range ts.Tables { 340 tables[tableID] = table.Clone() 341 } 342 clone.Tables = tables 343 operation := make(map[TableID]*TableOperation, len(ts.Operation)) 344 for tableID, opt := range ts.Operation { 345 operation[tableID] = opt.Clone() 346 } 347 clone.Operation = operation 348 return &clone 349 } 350 351 // CaptureID is the type for capture ID 352 type CaptureID = string 353 354 // ChangeFeedID is the type for change feed ID 355 type ChangeFeedID = string 356 357 // TableID is the ID of the table 358 type TableID = int64 359 360 // SchemaID is the ID of the schema 361 type SchemaID = int64 362 363 // Ts is the timestamp with a logical count 364 type Ts = uint64 365 366 // ProcessorsInfos maps from capture IDs to TaskStatus 367 type ProcessorsInfos map[CaptureID]*TaskStatus 368 369 // ChangeFeedDDLState is the type for change feed status 370 type ChangeFeedDDLState int 371 372 const ( 373 // ChangeFeedUnknown stands for all unknown status 374 ChangeFeedUnknown ChangeFeedDDLState = iota 375 // ChangeFeedSyncDML means DMLs are being processed 376 ChangeFeedSyncDML 377 // ChangeFeedWaitToExecDDL means we are waiting to execute a DDL 378 ChangeFeedWaitToExecDDL 379 // ChangeFeedExecDDL means a DDL is being executed 380 ChangeFeedExecDDL 381 // ChangeFeedDDLExecuteFailed means that an error occurred when executing a DDL 382 ChangeFeedDDLExecuteFailed 383 ) 384 385 // String implements fmt.Stringer interface. 386 func (p ProcessorsInfos) String() string { 387 s := "{" 388 for id, sinfo := range p { 389 s += fmt.Sprintf("%s: %+v,", id, *sinfo) 390 } 391 392 s += "}" 393 394 return s 395 } 396 397 // String implements fmt.Stringer interface. 398 func (s ChangeFeedDDLState) String() string { 399 switch s { 400 case ChangeFeedSyncDML: 401 return "SyncDML" 402 case ChangeFeedWaitToExecDDL: 403 return "WaitToExecDDL" 404 case ChangeFeedExecDDL: 405 return "ExecDDL" 406 case ChangeFeedDDLExecuteFailed: 407 return "DDLExecuteFailed" 408 } 409 return "Unknown" 410 } 411 412 // ChangeFeedStatus stores information about a ChangeFeed 413 type ChangeFeedStatus struct { 414 ResolvedTs uint64 `json:"resolved-ts"` 415 CheckpointTs uint64 `json:"checkpoint-ts"` 416 AdminJobType AdminJobType `json:"admin-job-type"` 417 } 418 419 // Marshal returns json encoded string of ChangeFeedStatus, only contains necessary fields stored in storage 420 func (status *ChangeFeedStatus) Marshal() (string, error) { 421 data, err := json.Marshal(status) 422 return string(data), cerror.WrapError(cerror.ErrMarshalFailed, err) 423 } 424 425 // Unmarshal unmarshals into *ChangeFeedStatus from json marshal byte slice 426 func (status *ChangeFeedStatus) Unmarshal(data []byte) error { 427 err := json.Unmarshal(data, status) 428 return errors.Annotatef( 429 cerror.WrapError(cerror.ErrUnmarshalFailed, err), "Unmarshal data: %v", data) 430 } 431 432 // ProcInfoSnap holds most important replication information of a processor 433 type ProcInfoSnap struct { 434 CfID string `json:"changefeed-id"` 435 CaptureID string `json:"capture-id"` 436 Tables map[TableID]*TableReplicaInfo `json:"-"` 437 }