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 }