// ReadClusterNameByMaster will return the cluster name for a given instance by looking at its master // and getting it from there. // It is a non-recursive function and so-called-recursion is performed upon periodic reading of // instances. func ReadClusterNameByMaster(instanceKey *InstanceKey, masterKey *InstanceKey) (string, error) { db, err := db.OpenOrchestrator() if err != nil { return "", log.Errore(err) } var clusterName string err = db.QueryRow(` select if ( cluster_name != '', cluster_name, ifnull(concat(max(hostname), ':', max(port)), '') ) as cluster_name from database_instance where hostname=? and port=?`, masterKey.Hostname, masterKey.Port).Scan( &clusterName, ) if err != nil { return "", log.Errore(err) } if clusterName == "" { return fmt.Sprintf("%s:%d", instanceKey.Hostname, instanceKey.Port), nil } return clusterName, err }
// ScanInstanceRow executes a read-a-single-row query on a given MySQL topology instance func ScanInstanceRow(instanceKey *InstanceKey, query string, dest ...interface{}) error { db, err := db.OpenTopology(instanceKey.Hostname, instanceKey.Port) if err != nil { return err } err = db.QueryRow(query).Scan(dest...) return err }
// ReadTopologyInstance connects to a topology MySQL instance and reads its configuration and // replication status. It writes read info into orchestrator's backend. func ReadTopologyInstance(instanceKey *InstanceKey) (*Instance, error) { defer func() { if err := recover(); err != nil { log.Errorf("Unexpected error: %+v", err) } }() instance := NewInstance() instanceFound := false foundBySlaveHosts := false db, err := db.OpenTopology(instanceKey.Hostname, instanceKey.Port) if err != nil { goto Cleanup } instance.Key = *instanceKey err = db.QueryRow("select @@global.server_id, @@global.version, @@global.binlog_format, @@global.log_bin, @@global.log_slave_updates").Scan( &instance.ServerID, &instance.Version, &instance.Binlog_format, &instance.LogBinEnabled, &instance.LogSlaveUpdatesEnabled) if err != nil { goto Cleanup } instanceFound = true err = sqlutils.QueryRowsMap(db, "show slave status", func(m sqlutils.RowMap) error { instance.Slave_IO_Running = (m.GetString("Slave_IO_Running") == "Yes") instance.Slave_SQL_Running = (m.GetString("Slave_SQL_Running") == "Yes") instance.ReadBinlogCoordinates.LogFile = m.GetString("Master_Log_File") instance.ReadBinlogCoordinates.LogPos = m.GetInt64("Read_Master_Log_Pos") instance.ExecBinlogCoordinates.LogFile = m.GetString("Relay_Master_Log_File") instance.ExecBinlogCoordinates.LogPos = m.GetInt64("Exec_Master_Log_Pos") masterKey, err := NewInstanceKeyFromStrings(m.GetString("Master_Host"), m.GetString("Master_Port")) if err != nil { log.Errore(err) } instance.MasterKey = *masterKey instance.SecondsBehindMaster = m.GetNullInt64("Seconds_Behind_Master") if config.Config.SlaveLagQuery == "" { instance.SlaveLagSeconds = instance.SecondsBehindMaster } // Not breaking the flow even on error return nil }) if err != nil { goto Cleanup } if config.Config.SlaveLagQuery != "" { err = db.QueryRow(config.Config.SlaveLagQuery).Scan(&instance.SlaveLagSeconds) if err != nil { goto Cleanup } } err = sqlutils.QueryRowsMap(db, "show master status", func(m sqlutils.RowMap) error { var err error instance.SelfBinlogCoordinates.LogFile = m.GetString("File") instance.SelfBinlogCoordinates.LogPos = m.GetInt64("Position") return err }) if err != nil { goto Cleanup } // Get slaves, either by SHOW SLAVE HOSTS or via PROCESSLIST if config.Config.DiscoverByShowSlaveHosts { err = sqlutils.QueryRowsMap(db, `show slave hosts`, func(m sqlutils.RowMap) error { slaveKey, err := NewInstanceKeyFromStrings(m.GetString("Host"), m.GetString("Port")) if err == nil { instance.AddSlaveKey(slaveKey) foundBySlaveHosts = true } return err }) if err != nil { goto Cleanup } } if !foundBySlaveHosts { // Either not configured to read SHOW SLAVE HOSTS or nothing was there. // Discover by processlist err = sqlutils.QueryRowsMap(db, ` select substring_index(host, ':', 1) as slave_hostname from information_schema.processlist where command='Binlog Dump'`, func(m sqlutils.RowMap) error { cname, err := GetCNAME(m.GetString("slave_hostname")) if err != nil { return err } slaveKey := InstanceKey{Hostname: cname, Port: instance.Key.Port} instance.AddSlaveKey(&slaveKey) return err }) if err != nil { goto Cleanup } } if err != nil { goto Cleanup } instance.ClusterName, err = ReadClusterNameByMaster(&instance.Key, &instance.MasterKey) if err != nil { goto Cleanup } Cleanup: if instanceFound { _ = WriteInstance(instance, err) } else { _ = UpdateInstanceLastChecked(instanceKey) } if err != nil { log.Errore(err) } return instance, err }
// ReadInstance reads an instance from the orchestrator backend database func ReadInstance(instanceKey *InstanceKey) (*Instance, bool, error) { db, err := db.OpenOrchestrator() if err != nil { return nil, false, log.Errore(err) } instance := NewInstance() instance.Key = *instanceKey var slaveHostsJson string var secondsSinceLastChecked uint err = db.QueryRow(` select server_id, version, binlog_format, log_bin, log_slave_updates, binary_log_file, binary_log_pos, master_host, master_port, slave_sql_running, slave_io_running, master_log_file, read_master_log_pos, relay_master_log_file, exec_master_log_pos, seconds_behind_master, slave_lag_seconds, slave_hosts, cluster_name, timestampdiff(second, last_checked, now()) as seconds_since_last_checked, (last_checked <= last_seen) is true as is_last_check_valid, timestampdiff(second, last_seen, now()) as seconds_since_last_seen from database_instance where hostname=? and port=?`, instanceKey.Hostname, instanceKey.Port).Scan( &instance.ServerID, &instance.Version, &instance.Binlog_format, &instance.LogBinEnabled, &instance.LogSlaveUpdatesEnabled, &instance.SelfBinlogCoordinates.LogFile, &instance.SelfBinlogCoordinates.LogPos, &instance.MasterKey.Hostname, &instance.MasterKey.Port, &instance.Slave_SQL_Running, &instance.Slave_IO_Running, &instance.ReadBinlogCoordinates.LogFile, &instance.ReadBinlogCoordinates.LogPos, &instance.ExecBinlogCoordinates.LogFile, &instance.ExecBinlogCoordinates.LogPos, &instance.SecondsBehindMaster, &instance.SlaveLagSeconds, &slaveHostsJson, &instance.ClusterName, &secondsSinceLastChecked, &instance.IsLastCheckValid, &instance.SecondsSinceLastSeen, ) if err == sql.ErrNoRows { log.Infof("No entry for %+v", instanceKey) return instance, false, err } if err != nil { log.Error("error on", instanceKey, err) return instance, false, err } instance.IsUpToDate = (secondsSinceLastChecked <= config.Config.InstancePollSeconds) instance.IsRecentlyChecked = (secondsSinceLastChecked <= config.Config.InstancePollSeconds*5) instance.ReadSlaveHostsFromJson(slaveHostsJson) return instance, true, err }