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  }