github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/engine/framework/model/master.go (about)

     1  // Copyright 2022 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  	"database/sql/driver"
    18  	"encoding/json"
    19  	"reflect"
    20  
    21  	ormModel "github.com/pingcap/tiflow/engine/pkg/orm/model"
    22  	"github.com/pingcap/tiflow/engine/pkg/p2p"
    23  	"github.com/pingcap/tiflow/engine/pkg/tenant"
    24  	"github.com/pingcap/tiflow/pkg/errors"
    25  	"github.com/pingcap/tiflow/pkg/label"
    26  	"gorm.io/gorm"
    27  )
    28  
    29  type (
    30  	// MasterState is used in framework to manage job status
    31  	MasterState int8
    32  )
    33  
    34  // Job master statuses
    35  // NOTICE: DO NOT CHANGE the previous status code
    36  // Modify the MasterMeta.State comment IF you add some new status code
    37  const (
    38  	MasterStateUninit   = MasterState(1)
    39  	MasterStateInit     = MasterState(2)
    40  	MasterStateFinished = MasterState(3)
    41  	MasterStateStopped  = MasterState(4)
    42  	MasterStateFailed   = MasterState(5)
    43  	// extend the status code here
    44  )
    45  
    46  // IsTerminatedState checks whether master state is terminated
    47  func (code MasterState) IsTerminatedState() bool {
    48  	switch code {
    49  	case MasterStateFinished, MasterStateStopped, MasterStateFailed:
    50  		return true
    51  	default:
    52  		return false
    53  	}
    54  }
    55  
    56  // MasterMetaExt stores some attributes of job masters that do not need
    57  // to be indexed.
    58  type MasterMetaExt struct {
    59  	Selectors []*label.Selector `json:"selectors"`
    60  }
    61  
    62  // Value implements driver.Valuer.
    63  func (e MasterMetaExt) Value() (driver.Value, error) {
    64  	b, err := json.Marshal(e)
    65  	if err != nil {
    66  		return nil, errors.Annotate(err, "failed to marshal MasterMetaExt")
    67  	}
    68  	return string(b), nil
    69  }
    70  
    71  // Scan implements sql.Scanner.
    72  func (e *MasterMetaExt) Scan(rawInput interface{}) error {
    73  	// Zero the fields.
    74  	*e = MasterMetaExt{}
    75  
    76  	if rawInput == nil {
    77  		return nil
    78  	}
    79  
    80  	// As different SQL drivers might treat the JSON value differently,
    81  	// we need to handle two cases where the JSON value is passed as a string
    82  	// and a byte slice respectively.
    83  	var bytes []byte
    84  	switch input := rawInput.(type) {
    85  	case string:
    86  		// SQLite is this case.
    87  		if len(input) == 0 {
    88  			return nil
    89  		}
    90  		bytes = []byte(input)
    91  	case []byte:
    92  		// MySQL is this case.
    93  		if len(input) == 0 {
    94  			return nil
    95  		}
    96  		bytes = input
    97  	default:
    98  		return errors.Errorf("failed to scan MasterMetaExt. "+
    99  			"Expected string or []byte, got %s", reflect.TypeOf(rawInput))
   100  	}
   101  
   102  	if err := json.Unmarshal(bytes, e); err != nil {
   103  		return errors.Annotate(err, "failed to unmarshal MasterMetaExt")
   104  	}
   105  
   106  	return nil
   107  }
   108  
   109  // MasterMeta defines the metadata of job master
   110  type MasterMeta struct {
   111  	ormModel.Model
   112  	ProjectID tenant.ProjectID `json:"project-id" gorm:"column:project_id;type:varchar(128) not null;index:idx_mst,priority:1"`
   113  	ID        MasterID         `json:"id" gorm:"column:id;type:varchar(128) not null;uniqueIndex:uidx_mid"`
   114  	Type      WorkerType       `json:"type" gorm:"column:type;type:smallint not null;comment:JobManager(1),CvsJobMaster(2),FakeJobMaster(3),DMJobMaster(4),CDCJobMaster(5)"`
   115  	State     MasterState      `json:"state" gorm:"column:state;type:tinyint not null;index:idx_mst,priority:2;comment:Uninit(1),Init(2),Finished(3),Stopped(4),Failed(5)"`
   116  	NodeID    p2p.NodeID       `json:"node-id" gorm:"column:node_id;type:varchar(128) not null"`
   117  	Addr      string           `json:"addr" gorm:"column:address;type:varchar(256) not null"`
   118  	Epoch     Epoch            `json:"epoch" gorm:"column:epoch;type:bigint not null"`
   119  
   120  	// Config holds business-specific data
   121  	Config []byte `json:"config" gorm:"column:config;type:blob"`
   122  
   123  	// error message for the job
   124  	ErrorMsg string `json:"error-message" gorm:"column:error_message;type:text"`
   125  
   126  	// if job is finished or canceled, business logic can set self-defined job info to `Detail`
   127  	Detail []byte `json:"detail" gorm:"column:detail;type:blob"`
   128  
   129  	Ext MasterMetaExt `json:"ext" gorm:"column:ext;type:JSON"`
   130  
   131  	// Deleted is a nullable timestamp. Then master is deleted
   132  	// if Deleted is not null.
   133  	Deleted gorm.DeletedAt
   134  }
   135  
   136  // Marshal returns the JSON encoding of MasterMeta.
   137  func (m *MasterMeta) Marshal() ([]byte, error) {
   138  	return json.Marshal(m)
   139  }
   140  
   141  // Unmarshal parses the JSON-encoded data and stores the result to MasterMeta
   142  func (m *MasterMeta) Unmarshal(data []byte) error {
   143  	return json.Unmarshal(data, m)
   144  }
   145  
   146  // RefreshValues is used to generate orm value map when refreshing metadata.
   147  func (m *MasterMeta) RefreshValues() ormModel.KeyValueMap {
   148  	return ormModel.KeyValueMap{
   149  		"node_id": m.NodeID,
   150  		"address": m.Addr,
   151  		"epoch":   m.Epoch,
   152  	}
   153  }
   154  
   155  // UpdateStateValues is used to generate orm value map when updating state of master meta.
   156  func (m *MasterMeta) UpdateStateValues() ormModel.KeyValueMap {
   157  	return ormModel.KeyValueMap{
   158  		"state": m.State,
   159  	}
   160  }
   161  
   162  // UpdateErrorValues is used to generate orm value map when job master meets error and records it.
   163  func (m *MasterMeta) UpdateErrorValues() ormModel.KeyValueMap {
   164  	return ormModel.KeyValueMap{
   165  		"error_message": m.ErrorMsg,
   166  	}
   167  }
   168  
   169  // ExitValues is used to generate orm value map when job master exits.
   170  func (m *MasterMeta) ExitValues() ormModel.KeyValueMap {
   171  	return ormModel.KeyValueMap{
   172  		"state":         m.State,
   173  		"error_message": m.ErrorMsg,
   174  		"detail":        m.Detail,
   175  	}
   176  }
   177  
   178  // MasterUpdateColumns is used in gorm update
   179  // TODO: using reflect to generate it more generally
   180  // related to some implement of gorm
   181  var MasterUpdateColumns = []string{
   182  	"updated_at",
   183  	"project_id",
   184  	"id",
   185  	"type",
   186  	"state",
   187  	"node_id",
   188  	"address",
   189  	"epoch",
   190  	"config",
   191  	"error_message",
   192  	"detail",
   193  	"ext",
   194  }