Browse Source

Pass by HostInfo instead of the peer address

Pass the full HostInfo to the connection pool after getting new
hosts, with the caveat that seed hosts wont have valid host info.
Chris Bannister 11 years ago
parent
commit
358e2503dd
3 changed files with 86 additions and 57 deletions
  1. 0 1
      cluster.go
  2. 45 17
      connectionpool.go
  3. 41 39
      host_source.go

+ 0 - 1
cluster.go

@@ -111,7 +111,6 @@ func (cfg *ClusterConfig) CreateSession() (*Session, error) {
 				session:    s,
 				session:    s,
 				dcFilter:   cfg.Discovery.DcFilter,
 				dcFilter:   cfg.Discovery.DcFilter,
 				rackFilter: cfg.Discovery.RackFilter,
 				rackFilter: cfg.Discovery.RackFilter,
-				previous:   cfg.Hosts,
 			}
 			}
 
 
 			go hostSource.run(cfg.Discovery.Sleep)
 			go hostSource.run(cfg.Discovery.Sleep)

+ 45 - 17
connectionpool.go

@@ -91,8 +91,7 @@ type ConnectionPool interface {
 	Size() int
 	Size() int
 	HandleError(*Conn, error, bool)
 	HandleError(*Conn, error, bool)
 	Close()
 	Close()
-	AddHost(addr string)
-	RemoveHost(addr string)
+	SetHosts(host []HostInfo)
 }
 }
 
 
 //NewPoolFunc is the type used by ClusterConfig to create a pool of a specific type.
 //NewPoolFunc is the type used by ClusterConfig to create a pool of a specific type.
@@ -107,9 +106,10 @@ type SimplePool struct {
 	connPool map[string]*RoundRobin
 	connPool map[string]*RoundRobin
 	conns    map[*Conn]struct{}
 	conns    map[*Conn]struct{}
 	keyspace string
 	keyspace string
-	// current hosts
+
 	hostMu sync.RWMutex
 	hostMu sync.RWMutex
-	hosts  map[string]struct{}
+	// this is the set of current hosts which the pool will attempt to connect to
+	hosts map[string]*HostInfo
 
 
 	// protects hostpool, connPoll, conns, quit
 	// protects hostpool, connPoll, conns, quit
 	mu sync.Mutex
 	mu sync.Mutex
@@ -132,11 +132,13 @@ func NewSimplePool(cfg *ClusterConfig) ConnectionPool {
 		quitWait:     make(chan bool),
 		quitWait:     make(chan bool),
 		cFillingPool: make(chan int, 1),
 		cFillingPool: make(chan int, 1),
 		keyspace:     cfg.Keyspace,
 		keyspace:     cfg.Keyspace,
-		hosts:        make(map[string]struct{}),
+		hosts:        make(map[string]*HostInfo),
 	}
 	}
 
 
 	for _, host := range cfg.Hosts {
 	for _, host := range cfg.Hosts {
-		pool.hosts[host] = struct{}{}
+		// seed hosts have unknown topology
+		// TODO: Handle populating this during SetHosts
+		pool.hosts[host] = &HostInfo{Peer: host}
 	}
 	}
 
 
 	//Walk through connecting to hosts. As soon as one host connects
 	//Walk through connecting to hosts. As soon as one host connects
@@ -157,7 +159,7 @@ func NewSimplePool(cfg *ClusterConfig) ConnectionPool {
 	return pool
 	return pool
 }
 }
 
 
-func (c *SimplePool) connect(addr string) error {
+func (c *SimplePool) newConn(addr string) (*Conn, error) {
 	cfg := ConnConfig{
 	cfg := ConnConfig{
 		ProtoVersion:  c.cfg.ProtoVersion,
 		ProtoVersion:  c.cfg.ProtoVersion,
 		CQLVersion:    c.cfg.CQLVersion,
 		CQLVersion:    c.cfg.CQLVersion,
@@ -170,10 +172,20 @@ func (c *SimplePool) connect(addr string) error {
 
 
 	conn, err := Connect(addr, cfg, c)
 	conn, err := Connect(addr, cfg, c)
 	if err != nil {
 	if err != nil {
-		log.Printf("failed to connect to %q: %v", addr, err)
+		log.Printf("newConn: failed to connect to %q: %v", addr, err)
+		return nil, err
+	}
+
+	return conn, nil
+}
+
+func (c *SimplePool) connect(addr string) error {
+	if conn, err := c.newConn(addr); err != nil {
+		log.Printf("connect: failed to connect to %q: %v", addr, err)
 		return err
 		return err
+	} else {
+		return c.addConn(conn)
 	}
 	}
-	return c.addConn(conn)
 }
 }
 
 
 func (c *SimplePool) addConn(conn *Conn) error {
 func (c *SimplePool) addConn(conn *Conn) error {
@@ -337,23 +349,39 @@ func (c *SimplePool) Close() {
 	})
 	})
 }
 }
 
 
-func (c *SimplePool) AddHost(addr string) {
+func (c *SimplePool) SetHosts(hosts []HostInfo) {
 	c.hostMu.Lock()
 	c.hostMu.Lock()
-	if _, ok := c.hosts[addr]; !ok {
-		c.hosts[addr] = struct{}{}
-		go c.fillPool()
+	toRemove := make(map[string]struct{})
+	for k := range c.hosts {
+		toRemove[k] = struct{}{}
+	}
+
+	for _, host := range hosts {
+		host := host
+
+		delete(toRemove, host.Peer)
+		// we already have it
+		if _, ok := c.hosts[host.Peer]; ok {
+			// TODO: Check rack, dc, token range is consistent, trigger topology change
+			// update stored host
+			continue
+		}
+
+		c.hosts[host.Peer] = &host
+	}
+
+	// can we hold c.mu whilst iterating this loop?
+	for addr := range toRemove {
+		c.removeHostLocked(addr)
 	}
 	}
 	c.hostMu.Unlock()
 	c.hostMu.Unlock()
 }
 }
 
 
-func (c *SimplePool) RemoveHost(addr string) {
-	c.hostMu.Lock()
+func (c *SimplePool) removeHostLocked(addr string) {
 	if _, ok := c.hosts[addr]; !ok {
 	if _, ok := c.hosts[addr]; !ok {
-		c.hostMu.Unlock()
 		return
 		return
 	}
 	}
 	delete(c.hosts, addr)
 	delete(c.hosts, addr)
-	c.hostMu.Unlock()
 
 
 	c.mu.Lock()
 	c.mu.Lock()
 	defer c.mu.Unlock()
 	defer c.mu.Unlock()

+ 41 - 39
host_source.go

@@ -1,24 +1,27 @@
 package gocql
 package gocql
 
 
-import "time"
-
-type hostInfo struct {
-	peer       string
-	dataCenter string
-	rack       string
-	hostId     string
-	tokens     []string
+import (
+	"net"
+	"time"
+)
+
+type HostInfo struct {
+	Peer       string
+	DataCenter string
+	Rack       string
+	HostId     string
+	Tokens     []string
 }
 }
 
 
 // Polls system.peers at a specific interval to find new hosts
 // Polls system.peers at a specific interval to find new hosts
 type ringDescriber struct {
 type ringDescriber struct {
 	dcFilter   string
 	dcFilter   string
 	rackFilter string
 	rackFilter string
-	previous   []string
+	previous   []HostInfo
 	session    *Session
 	session    *Session
 }
 }
 
 
-func (r *ringDescriber) GetHosts() []string {
+func (r *ringDescriber) GetHosts() []HostInfo {
 	// we need conn to be the same because we need to query system.peers and system.local
 	// we need conn to be the same because we need to query system.peers and system.local
 	// on the same node to get the whole cluster
 	// on the same node to get the whole cluster
 	conn := r.session.Pool.Pick(nil)
 	conn := r.session.Pool.Pick(nil)
@@ -26,17 +29,33 @@ func (r *ringDescriber) GetHosts() []string {
 		return r.previous
 		return r.previous
 	}
 	}
 
 
-	// TODO: Get conn's tokens form system.local
-	query := r.session.Query("SELECT peer, data_center, rack, host_id, tokens FROM system.peers")
+	query := r.session.Query("SELECT data_center, rack, host_id, tokens FROM system.local")
 	iter := conn.executeQuery(query)
 	iter := conn.executeQuery(query)
 
 
-	hosts := []string{conn.Address()}
-	host := hostInfo{}
+	host := &HostInfo{}
+	iter.Scan(&host.DataCenter, &host.Rack, &host.HostId, &host.Tokens)
 
 
-	for iter.Scan(&host.peer, &host.dataCenter, &host.rack, &host.hostId, &host.tokens) {
-		if r.matchFilter(&host) {
-			// TODO: Capture tokens
-			hosts = append(hosts, host.peer)
+	if err := iter.Close(); err != nil {
+		return r.previous
+	}
+
+	addr, _, err := net.SplitHostPort(conn.Address())
+	if err != nil {
+		// this should not happen, ever, as this is the address that was dialed by conn, here
+		// a panic makes sense, please report a bug if it occurs.
+		panic(err)
+	}
+
+	host.Peer = addr
+
+	hosts := []HostInfo{*host}
+
+	query = r.session.Query("SELECT peer, data_center, rack, host_id, tokens FROM system.peers")
+	iter = conn.executeQuery(query)
+
+	for iter.Scan(&host.Peer, &host.DataCenter, &host.Rack, &host.HostId, &host.Tokens) {
+		if r.matchFilter(host) {
+			hosts = append(hosts, *host)
 		}
 		}
 	}
 	}
 
 
@@ -49,16 +68,16 @@ func (r *ringDescriber) GetHosts() []string {
 	return hosts
 	return hosts
 }
 }
 
 
-func (r *ringDescriber) matchFilter(host *hostInfo) bool {
+func (r *ringDescriber) matchFilter(host *HostInfo) bool {
 	if r.dcFilter == "" && r.rackFilter == "" {
 	if r.dcFilter == "" && r.rackFilter == "" {
 		return true
 		return true
 	}
 	}
 
 
-	if r.dcFilter != "" && r.dcFilter != host.dataCenter {
+	if r.dcFilter != "" && r.dcFilter != host.DataCenter {
 		return false
 		return false
 	}
 	}
 
 
-	if r.rackFilter != "" && r.rackFilter != host.rack {
+	if r.rackFilter != "" && r.rackFilter != host.Rack {
 		return false
 		return false
 	}
 	}
 
 
@@ -70,29 +89,12 @@ func (h *ringDescriber) run(sleep time.Duration) {
 		sleep = 30 * time.Second
 		sleep = 30 * time.Second
 	}
 	}
 
 
-	prev := make(map[string]struct{})
 	for {
 	for {
 		// if we have 0 hosts this will return the previous list of hosts to
 		// if we have 0 hosts this will return the previous list of hosts to
 		// attempt to reconnect to the cluster otherwise we would never find
 		// attempt to reconnect to the cluster otherwise we would never find
 		// downed hosts again, could possibly have an optimisation to only
 		// downed hosts again, could possibly have an optimisation to only
 		// try to add new hosts if GetHosts didnt error and the hosts didnt change.
 		// try to add new hosts if GetHosts didnt error and the hosts didnt change.
-		hosts := h.GetHosts()
-		current := make(map[string]struct{})
-		for _, host := range hosts {
-			if _, ok := prev[host]; !ok {
-				h.session.Pool.AddHost(host)
-			} else {
-				delete(prev, host)
-			}
-
-			current[host] = struct{}{}
-		}
-
-		for host := range prev {
-			h.session.Pool.RemoveHost(host)
-		}
-
-		prev = current
+		h.session.Pool.SetHosts(h.GetHosts())
 
 
 		time.Sleep(sleep)
 		time.Sleep(sleep)
 	}
 	}