|
|
@@ -366,7 +366,6 @@ type ringDescriber struct {
|
|
|
session *Session
|
|
|
mu sync.Mutex
|
|
|
prevHosts []*HostInfo
|
|
|
- localHost *HostInfo
|
|
|
prevPartitioner string
|
|
|
}
|
|
|
|
|
|
@@ -388,13 +387,13 @@ func checkSystemSchema(control *controlConn) (bool, error) {
|
|
|
|
|
|
// Given a map that represents a row from either system.local or system.peers
|
|
|
// return as much information as we can in *HostInfo
|
|
|
-func (r *ringDescriber) hostInfoFromMap(row map[string]interface{}) (*HostInfo, error) {
|
|
|
+func hostInfoFromMap(row map[string]interface{}, defaultPort int) (*HostInfo, error) {
|
|
|
const assertErrorMsg = "Assertion failed for %s"
|
|
|
var ok bool
|
|
|
|
|
|
// Default to our connected port if the cluster doesn't have port information
|
|
|
host := HostInfo{
|
|
|
- port: r.session.cfg.Port,
|
|
|
+ port: defaultPort,
|
|
|
}
|
|
|
|
|
|
for key, value := range row {
|
|
|
@@ -489,83 +488,44 @@ func (r *ringDescriber) hostInfoFromMap(row map[string]interface{}) (*HostInfo,
|
|
|
return &host, nil
|
|
|
}
|
|
|
|
|
|
-// Ask the control node for it's local host information
|
|
|
-func (r *ringDescriber) GetLocalHostInfo() (*HostInfo, error) {
|
|
|
- it := r.session.control.query("SELECT * FROM system.local WHERE key='local'")
|
|
|
- if it == nil {
|
|
|
- return nil, errors.New("Attempted to query 'system.local' on a closed control connection")
|
|
|
- }
|
|
|
- host, err := r.extractHostInfo(it)
|
|
|
- if err != nil {
|
|
|
- return nil, err
|
|
|
- }
|
|
|
-
|
|
|
- if host.invalidConnectAddr() {
|
|
|
- host.SetConnectAddress(r.session.control.GetHostInfo().ConnectAddress())
|
|
|
- }
|
|
|
-
|
|
|
- return host, nil
|
|
|
-}
|
|
|
-
|
|
|
-// Given an ip address and port, return a peer that matched the ip address
|
|
|
-func (r *ringDescriber) GetPeerHostInfo(ip net.IP, port int) (*HostInfo, error) {
|
|
|
- it := r.session.control.query("SELECT * FROM system.peers WHERE peer=?", ip)
|
|
|
- if it == nil {
|
|
|
- return nil, errors.New("Attempted to query 'system.peers' on a closed control connection")
|
|
|
- }
|
|
|
- return r.extractHostInfo(it)
|
|
|
-}
|
|
|
-
|
|
|
-func (r *ringDescriber) extractHostInfo(it *Iter) (*HostInfo, error) {
|
|
|
- row := make(map[string]interface{})
|
|
|
-
|
|
|
- // expect only 1 row
|
|
|
- it.MapScan(row)
|
|
|
- if err := it.Close(); err != nil {
|
|
|
- return nil, err
|
|
|
- }
|
|
|
-
|
|
|
- // extract all available info about the host
|
|
|
- return r.hostInfoFromMap(row)
|
|
|
-}
|
|
|
-
|
|
|
// Ask the control node for host info on all it's known peers
|
|
|
-func (r *ringDescriber) GetClusterPeerInfo() ([]*HostInfo, error) {
|
|
|
+func (r *ringDescriber) getClusterPeerInfo() ([]*HostInfo, error) {
|
|
|
var hosts []*HostInfo
|
|
|
+ iter := r.session.control.withConnHost(func(ch *connHost) *Iter {
|
|
|
+ hosts = append(hosts, ch.host)
|
|
|
+ return ch.conn.query("SELECT * FROM system.peers")
|
|
|
+ })
|
|
|
|
|
|
- // Ask the node for a list of it's peers
|
|
|
- it := r.session.control.query("SELECT * FROM system.peers")
|
|
|
- if it == nil {
|
|
|
- return nil, errors.New("Attempted to query 'system.peers' on a closed connection")
|
|
|
+ if iter == nil {
|
|
|
+ return nil, errNoControl
|
|
|
}
|
|
|
|
|
|
- for {
|
|
|
- row := make(map[string]interface{})
|
|
|
- if !it.MapScan(row) {
|
|
|
- break
|
|
|
- }
|
|
|
+ rows, err := iter.SliceMap()
|
|
|
+ if err != nil {
|
|
|
+ // TODO(zariel): make typed error
|
|
|
+ return nil, fmt.Errorf("unable to fetch peer host info: %s", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, row := range rows {
|
|
|
// extract all available info about the peer
|
|
|
- host, err := r.hostInfoFromMap(row)
|
|
|
+ host, err := hostInfoFromMap(row, r.session.cfg.Port)
|
|
|
if err != nil {
|
|
|
return nil, err
|
|
|
- }
|
|
|
-
|
|
|
- // If it's not a valid peer
|
|
|
- if !r.IsValidPeer(host) {
|
|
|
- Logger.Printf("Found invalid peer '%+v' "+
|
|
|
+ } else if !isValidPeer(host) {
|
|
|
+ // If it's not a valid peer
|
|
|
+ Logger.Printf("Found invalid peer '%s' "+
|
|
|
"Likely due to a gossip or snitch issue, this host will be ignored", host)
|
|
|
continue
|
|
|
}
|
|
|
+
|
|
|
hosts = append(hosts, host)
|
|
|
}
|
|
|
- if it.err != nil {
|
|
|
- return nil, fmt.Errorf("while scanning 'system.peers' table: %s", it.err)
|
|
|
- }
|
|
|
+
|
|
|
return hosts, nil
|
|
|
}
|
|
|
|
|
|
// Return true if the host is a valid peer
|
|
|
-func (r *ringDescriber) IsValidPeer(host *HostInfo) bool {
|
|
|
+func isValidPeer(host *HostInfo) bool {
|
|
|
return !(len(host.RPCAddress()) == 0 ||
|
|
|
host.hostId == "" ||
|
|
|
host.dataCenter == "" ||
|
|
|
@@ -578,84 +538,47 @@ func (r *ringDescriber) GetHosts() ([]*HostInfo, string, error) {
|
|
|
r.mu.Lock()
|
|
|
defer r.mu.Unlock()
|
|
|
|
|
|
- // Update the localHost info with data from the connected host
|
|
|
- localHost, err := r.GetLocalHostInfo()
|
|
|
+ hosts, err := r.getClusterPeerInfo()
|
|
|
if err != nil {
|
|
|
return r.prevHosts, r.prevPartitioner, err
|
|
|
- } else if localHost.invalidConnectAddr() {
|
|
|
- panic(fmt.Sprintf("unable to get localhost connect address: %v", localHost))
|
|
|
}
|
|
|
|
|
|
- // Update our list of hosts by querying the cluster
|
|
|
- hosts, err := r.GetClusterPeerInfo()
|
|
|
- if err != nil {
|
|
|
- return r.prevHosts, r.prevPartitioner, err
|
|
|
- }
|
|
|
-
|
|
|
- hosts = append(hosts, localHost)
|
|
|
-
|
|
|
- // Filter the hosts if filter is provided
|
|
|
- filteredHosts := hosts
|
|
|
- if r.session.cfg.HostFilter != nil {
|
|
|
- filteredHosts = filteredHosts[:0]
|
|
|
- for _, host := range hosts {
|
|
|
- if r.session.cfg.HostFilter.Accept(host) {
|
|
|
- filteredHosts = append(filteredHosts, host)
|
|
|
- }
|
|
|
- }
|
|
|
+ var partitioner string
|
|
|
+ if len(hosts) > 0 {
|
|
|
+ partitioner = hosts[0].Partitioner()
|
|
|
}
|
|
|
|
|
|
- r.prevHosts = filteredHosts
|
|
|
- r.prevPartitioner = localHost.partitioner
|
|
|
- r.localHost = localHost
|
|
|
-
|
|
|
- return filteredHosts, localHost.partitioner, nil
|
|
|
+ return hosts, partitioner, nil
|
|
|
}
|
|
|
|
|
|
// Given an ip/port return HostInfo for the specified ip/port
|
|
|
-func (r *ringDescriber) GetHostInfo(ip net.IP, port int) (*HostInfo, error) {
|
|
|
- // TODO(thrawn01): Is IgnorePeerAddr still useful now that we have DisableInitialHostLookup?
|
|
|
- // TODO(thrawn01): should we also check for DisableInitialHostLookup and return if true?
|
|
|
-
|
|
|
- // Ignore the port and connect address and use the address/port we already have
|
|
|
- if r.session.control == nil || r.session.cfg.IgnorePeerAddr {
|
|
|
- return &HostInfo{connectAddress: ip, port: port}, nil
|
|
|
- }
|
|
|
-
|
|
|
- // Attempt to get the host info for our control connection
|
|
|
- controlHost := r.session.control.GetHostInfo()
|
|
|
- if controlHost == nil {
|
|
|
- return nil, errors.New("invalid control connection")
|
|
|
- }
|
|
|
-
|
|
|
- var (
|
|
|
- host *HostInfo
|
|
|
- err error
|
|
|
- )
|
|
|
+func (r *ringDescriber) getHostInfo(ip net.IP, port int) (*HostInfo, error) {
|
|
|
+ var host *HostInfo
|
|
|
+ iter := r.session.control.withConnHost(func(ch *connHost) *Iter {
|
|
|
+ if ch.host.ConnectAddress().Equal(ip) {
|
|
|
+ host = ch.host
|
|
|
+ return nil
|
|
|
+ }
|
|
|
|
|
|
- // If we are asking about the same node our control connection has a connection too
|
|
|
- if controlHost.ConnectAddress().Equal(ip) {
|
|
|
- host, err = r.GetLocalHostInfo()
|
|
|
- } else {
|
|
|
- host, err = r.GetPeerHostInfo(ip, port)
|
|
|
- }
|
|
|
+ return ch.conn.query("SELECT * FROM system.peers WHERE peer=?", ip)
|
|
|
+ })
|
|
|
|
|
|
- // No host was found matching this ip/port
|
|
|
- if err != nil {
|
|
|
- return nil, err
|
|
|
- }
|
|
|
+ if iter != nil {
|
|
|
+ row, err := iter.rowMap()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
|
|
|
- if controlHost.ConnectAddress().Equal(ip) {
|
|
|
- // Always respect the provided control node address and disregard the ip address
|
|
|
- // the cassandra node provides. We do this as we are already connected and have a
|
|
|
- // known valid ip address. This insulates gocql from client connection issues stemming
|
|
|
- // from node misconfiguration. For instance when a node is run from a container, by
|
|
|
- // default the node will report its ip address as 127.0.0.1 which is typically invalid.
|
|
|
- host.SetConnectAddress(ip)
|
|
|
+ host, err = hostInfoFromMap(row, port)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ } else if host == nil {
|
|
|
+ return nil, errors.New("unable to fetch host info: invalid control connection")
|
|
|
}
|
|
|
|
|
|
if host.invalidConnectAddr() {
|
|
|
- return nil, fmt.Errorf("host ConnectAddress invalid: %v", host)
|
|
|
+ return nil, fmt.Errorf("host ConnectAddress invalid ip=%v: %v", ip, host)
|
|
|
}
|
|
|
|
|
|
return host, nil
|
|
|
@@ -675,6 +598,10 @@ func (r *ringDescriber) refreshRing() error {
|
|
|
|
|
|
// TODO: move this to session
|
|
|
for _, h := range hosts {
|
|
|
+ if filter := r.session.cfg.HostFilter; filter != nil && !filter.Accept(h) {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
if host, ok := r.session.ring.addHostIfMissing(h); !ok {
|
|
|
r.session.pool.addHost(h)
|
|
|
r.session.policy.AddHost(h)
|