فهرست منبع

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 10 سال پیش
والد
کامیت
1b7cc11fed
1فایلهای تغییر یافته به همراه15 افزوده شده و 14 حذف شده
  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