예제 #1
0
// 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
}
예제 #2
0
// 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
}
예제 #3
0
// 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
}
예제 #4
0
// 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
}