vitess.io/vitess@v0.16.2/go/vt/vtorc/inst/instance.go (about) 1 /* 2 Copyright 2014 Outbrain Inc. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package inst 18 19 import ( 20 "database/sql" 21 "encoding/json" 22 "strings" 23 "time" 24 25 "vitess.io/vitess/go/vt/vtctl/reparentutil/promotionrule" 26 ) 27 28 const ReasonableDiscoveryLatency = 500 * time.Millisecond 29 30 // Instance represents a database instance, including its current configuration & status. 31 // It presents important replication configuration and detailed replication status. 32 type Instance struct { 33 Key InstanceKey 34 InstanceAlias string 35 ServerID uint 36 ServerUUID string 37 Version string 38 VersionComment string 39 FlavorName string 40 ReadOnly bool 41 BinlogFormat string 42 BinlogRowImage string 43 LogBinEnabled bool 44 LogReplicationUpdatesEnabled bool 45 SelfBinlogCoordinates BinlogCoordinates 46 SourceKey InstanceKey 47 SourceUUID string 48 AncestryUUID string 49 IsDetachedPrimary bool 50 51 ReplicationSQLThreadRuning bool 52 ReplicationIOThreadRuning bool 53 ReplicationSQLThreadState ReplicationThreadState 54 ReplicationIOThreadState ReplicationThreadState 55 56 HasReplicationFilters bool 57 GTIDMode string 58 SupportsOracleGTID bool 59 UsingOracleGTID bool 60 UsingMariaDBGTID bool 61 UsingPseudoGTID bool // Legacy. Always 'false' 62 ReadBinlogCoordinates BinlogCoordinates 63 ExecBinlogCoordinates BinlogCoordinates 64 IsDetached bool 65 RelaylogCoordinates BinlogCoordinates 66 LastSQLError string 67 LastIOError string 68 SecondsBehindPrimary sql.NullInt64 69 SQLDelay uint 70 ExecutedGtidSet string 71 GtidPurged string 72 GtidErrant string 73 74 primaryExecutedGtidSet string // Not exported 75 76 ReplicationLagSeconds sql.NullInt64 77 DataCenter string 78 Region string 79 PhysicalEnvironment string 80 ReplicationDepth uint 81 IsCoPrimary bool 82 HasReplicationCredentials bool 83 SemiSyncEnforced bool 84 SemiSyncPrimaryEnabled bool 85 SemiSyncReplicaEnabled bool 86 SemiSyncPrimaryTimeout uint64 87 SemiSyncPrimaryWaitForReplicaCount uint 88 SemiSyncPrimaryStatus bool 89 SemiSyncPrimaryClients uint 90 SemiSyncReplicaStatus bool 91 92 LastSeenTimestamp string 93 IsLastCheckValid bool 94 IsUpToDate bool 95 IsRecentlyChecked bool 96 SecondsSinceLastSeen sql.NullInt64 97 98 // Careful. IsCandidate and PromotionRule are used together 99 // and probably need to be merged. IsCandidate's value may 100 // be picked up from daabase_candidate_instance's value when 101 // reading an instance from the db. 102 IsCandidate bool 103 PromotionRule promotionrule.CandidatePromotionRule 104 IsDowntimed bool 105 DowntimeReason string 106 DowntimeOwner string 107 DowntimeEndTimestamp string 108 ElapsedDowntime time.Duration 109 UnresolvedHostname string 110 AllowTLS bool 111 112 Problems []string 113 114 LastDiscoveryLatency time.Duration 115 116 seed bool // Means we force this instance to be written to backend, even if it's invalid, empty or forgotten 117 118 /* All things Group Replication below */ 119 120 // Group replication global variables 121 ReplicationGroupName string 122 ReplicationGroupIsSinglePrimary bool 123 124 // Replication group members information. See 125 // https://dev.mysql.com/doc/refman/8.0/en/replication-group-members-table.html for details. 126 ReplicationGroupMemberState string 127 ReplicationGroupMemberRole string 128 129 // List of all known members of the same group 130 ReplicationGroupMembers InstanceKeyMap 131 132 // Primary of the replication group 133 ReplicationGroupPrimaryInstanceKey InstanceKey 134 } 135 136 // NewInstance creates a new, empty instance 137 138 func NewInstance() *Instance { 139 return &Instance{ 140 ReplicationGroupMembers: make(map[InstanceKey]bool), 141 Problems: []string{}, 142 } 143 } 144 145 func (instance *Instance) MarshalJSON() ([]byte, error) { 146 i := struct { 147 Instance 148 }{} 149 i.Instance = *instance 150 151 return json.Marshal(i) 152 } 153 154 // Equals tests that this instance is the same instance as other. The function does not test 155 // configuration or status. 156 func (instance *Instance) Equals(other *Instance) bool { 157 return instance.Key == other.Key 158 } 159 160 // MajorVersion returns this instance's major version number (e.g. for 5.5.36 it returns "5.5") 161 func (instance *Instance) MajorVersion() []string { 162 return MajorVersion(instance.Version) 163 } 164 165 // MajorVersion returns this instance's major version number (e.g. for 5.5.36 it returns "5.5") 166 func (instance *Instance) MajorVersionString() string { 167 return strings.Join(instance.MajorVersion(), ".") 168 } 169 170 func (instance *Instance) IsMySQL51() bool { 171 return instance.MajorVersionString() == "5.1" 172 } 173 174 func (instance *Instance) IsMySQL55() bool { 175 return instance.MajorVersionString() == "5.5" 176 } 177 178 func (instance *Instance) IsMySQL56() bool { 179 return instance.MajorVersionString() == "5.6" 180 } 181 182 func (instance *Instance) IsMySQL57() bool { 183 return instance.MajorVersionString() == "5.7" 184 } 185 186 func (instance *Instance) IsMySQL80() bool { 187 return instance.MajorVersionString() == "8.0" 188 } 189 190 // IsSmallerBinlogFormat returns true when this instance's binlgo format is 191 // "smaller" than the other's, i.e. binary logs cannot flow from the other instance to this one 192 func (instance *Instance) IsSmallerBinlogFormat(other *Instance) bool { 193 return IsSmallerBinlogFormat(instance.BinlogFormat, other.BinlogFormat) 194 } 195 196 // IsSmallerMajorVersion tests this instance against another and returns true if this instance is of a smaller "major" varsion. 197 // e.g. 5.5.36 is NOT a smaller major version as comapred to 5.5.36, but IS as compared to 5.6.9 198 func (instance *Instance) IsSmallerMajorVersion(other *Instance) bool { 199 return IsSmallerMajorVersion(instance.Version, other.Version) 200 } 201 202 // IsSmallerMajorVersionByString checks if this instance has a smaller major version number than given one 203 func (instance *Instance) IsSmallerMajorVersionByString(otherVersion string) bool { 204 return IsSmallerMajorVersion(instance.Version, otherVersion) 205 } 206 207 // IsMariaDB checks whether this is any version of MariaDB 208 func (instance *Instance) IsMariaDB() bool { 209 return strings.Contains(instance.Version, "MariaDB") 210 } 211 212 // IsPercona checks whether this is any version of Percona Server 213 func (instance *Instance) IsPercona() bool { 214 return strings.Contains(instance.VersionComment, "Percona") 215 } 216 217 // isNDB check whether this is NDB Cluster (aka MySQL Cluster) 218 func (instance *Instance) IsNDB() bool { 219 return strings.Contains(instance.Version, "-ndb-") 220 } 221 222 // IsReplicationGroup checks whether the host thinks it is part of a known replication group. Notice that this might 223 // return True even if the group has decided to expel the member represented by this instance, as the instance might not 224 // know that under certain circumstances 225 func (instance *Instance) IsReplicationGroupMember() bool { 226 return instance.ReplicationGroupName != "" 227 } 228 229 func (instance *Instance) IsReplicationGroupPrimary() bool { 230 return instance.IsReplicationGroupMember() && instance.ReplicationGroupPrimaryInstanceKey.Equals(&instance.Key) 231 } 232 233 func (instance *Instance) IsReplicationGroupSecondary() bool { 234 return instance.IsReplicationGroupMember() && !instance.ReplicationGroupPrimaryInstanceKey.Equals(&instance.Key) 235 } 236 237 // IsBinlogServer checks whether this is any type of a binlog server 238 func (instance *Instance) IsBinlogServer() bool { 239 return false 240 } 241 242 // IsOracleMySQL checks whether this is an Oracle MySQL distribution 243 func (instance *Instance) IsOracleMySQL() bool { 244 if instance.IsMariaDB() { 245 return false 246 } 247 if instance.IsPercona() { 248 return false 249 } 250 if instance.IsBinlogServer() { 251 return false 252 } 253 return true 254 } 255 256 func (instance *Instance) SetSeed() { 257 instance.seed = true 258 } 259 func (instance *Instance) IsSeed() bool { 260 return instance.seed 261 } 262 263 // applyFlavorName 264 func (instance *Instance) applyFlavorName() { 265 if instance == nil { 266 return 267 } 268 if instance.IsOracleMySQL() { 269 instance.FlavorName = "MySQL" 270 } else if instance.IsMariaDB() { 271 instance.FlavorName = "MariaDB" 272 } else if instance.IsPercona() { 273 instance.FlavorName = "Percona" 274 } else { 275 instance.FlavorName = "unknown" 276 } 277 } 278 279 // FlavorNameAndMajorVersion returns a string of the combined 280 // flavor and major version which is useful in some checks. 281 func (instance *Instance) FlavorNameAndMajorVersion() string { 282 if instance.FlavorName == "" { 283 instance.applyFlavorName() 284 } 285 286 return instance.FlavorName + "-" + instance.MajorVersionString() 287 } 288 289 // IsReplica makes simple heuristics to decide whether this instance is a replica of another instance 290 func (instance *Instance) IsReplica() bool { 291 return instance.SourceKey.Hostname != "" && instance.SourceKey.Hostname != "_" && instance.SourceKey.Port != 0 && (instance.ReadBinlogCoordinates.LogFile != "" || instance.UsingGTID()) 292 } 293 294 // IsPrimary makes simple heuristics to decide whether this instance is a primary (not replicating from any other server), 295 // either via traditional async/semisync replication or group replication 296 func (instance *Instance) IsPrimary() bool { 297 // If traditional replication is configured, it is for sure not a primary 298 if instance.IsReplica() { 299 return false 300 } 301 // If traditional replication is not configured, and it is also not part of a replication group, this host is 302 // a primary 303 if !instance.IsReplicationGroupMember() { 304 return true 305 } 306 // If traditional replication is not configured, and this host is part of a group, it is only considered a 307 // primary if it has the role of group Primary. Otherwise it is not a primary. 308 if instance.ReplicationGroupMemberRole == GroupReplicationMemberRolePrimary { 309 return true 310 } 311 return false 312 } 313 314 // ReplicaRunning returns true when this instance's status is of a replicating replica. 315 func (instance *Instance) ReplicaRunning() bool { 316 return instance.IsReplica() && instance.ReplicationSQLThreadState.IsRunning() && instance.ReplicationIOThreadState.IsRunning() 317 } 318 319 // NoReplicationThreadRunning returns true when neither SQL nor IO threads are running (including the case where isn't even a replica) 320 func (instance *Instance) ReplicationThreadsStopped() bool { 321 return instance.ReplicationSQLThreadState.IsStopped() && instance.ReplicationIOThreadState.IsStopped() 322 } 323 324 // NoReplicationThreadRunning returns true when neither SQL nor IO threads are running (including the case where isn't even a replica) 325 func (instance *Instance) ReplicationThreadsExist() bool { 326 return instance.ReplicationSQLThreadState.Exists() && instance.ReplicationIOThreadState.Exists() 327 } 328 329 // SQLThreadUpToDate returns true when the instance had consumed all relay logs. 330 func (instance *Instance) SQLThreadUpToDate() bool { 331 return instance.ReadBinlogCoordinates.Equals(&instance.ExecBinlogCoordinates) 332 } 333 334 // UsingGTID returns true when this replica is currently replicating via GTID (either Oracle or MariaDB) 335 func (instance *Instance) UsingGTID() bool { 336 return instance.UsingOracleGTID || instance.UsingMariaDBGTID 337 } 338 339 // AddGroupMemberKey adds a group member to the list of this instance's group members. 340 func (instance *Instance) AddGroupMemberKey(groupMemberKey *InstanceKey) { 341 instance.ReplicationGroupMembers.AddKey(*groupMemberKey) 342 }