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  }