Quellcode durchsuchen

conn selection: use copy on write for conn list

The connection pool will always give the conn selector a new slice of
conns when the list is updated, it also holds a mutex so SetConns will
not be called concurrently. Replace the mutex protected slice of conns
with an atomic.Value.
Chris Bannister vor 9 Jahren
Ursprung
Commit
1b7cc11fed
1 geänderte Dateien mit 15 neuen und 14 gelöschten Zeilen
  1. 15 14
      policies.go

+ 15 - 14
policies.go

@@ -512,41 +512,42 @@ type ConnSelectionPolicy interface {
 }
 
 type roundRobinConnPolicy struct {
+	// pos is still used to evenly distribute queries amongst connections.
 	pos   uint32
-	mu    sync.RWMutex
-	conns []*Conn
+	conns atomic.Value // *[]*Conn
 }
 
 func RoundRobinConnPolicy() func() ConnSelectionPolicy {
 	return func() ConnSelectionPolicy {
-		return &roundRobinConnPolicy{}
+		p := &roundRobinConnPolicy{}
+		var conns []*Conn
+		p.conns.Store(&conns)
+		return p
 	}
 }
 
 func (r *roundRobinConnPolicy) SetConns(conns []*Conn) {
-	r.mu.Lock()
-	r.conns = conns
-	r.mu.Unlock()
+	// NOTE: we do not need to lock here due to the conneciton pool is already
+	// holding its own mutex over the conn seleciton policy
+	r.conns.Store(&conns)
 }
 
 func (r *roundRobinConnPolicy) Pick(qry *Query) *Conn {
-	pos := int(atomic.AddUint32(&r.pos, 1) - 1)
-
-	r.mu.RLock()
-	defer r.mu.RUnlock()
-
-	if len(r.conns) == 0 {
+	conns := *(r.conns.Load().(*[]*Conn))
+	if len(conns) == 0 {
 		return nil
 	}
 
+	pos := int(atomic.AddUint32(&r.pos, 1) - 1)
+
 	var (
 		leastBusyConn    *Conn
 		streamsAvailable int
 	)
 
 	// find the conn which has the most available streams, this is racy
-	for i := 0; i < len(r.conns); i++ {
-		conn := r.conns[(pos+i)%len(r.conns)]
+	for i := 0; i < len(conns); i++ {
+		conn := conns[(pos+i)%len(conns)]
 		if streams := conn.AvailableStreams(); streams > streamsAvailable {
 			leastBusyConn = conn
 			streamsAvailable = streams