func (c *Connection) markAsLive() {
	c.IsDead = false
}
markAsLive marks the connection as alive.
package opensearchtransport
import (
	"errors"
	"fmt"
	"math"
	"net/url"
	"sort"
	"sync"
	"time"
)
var (
	defaultResurrectTimeoutInitial      = 60 * time.Second
	defaultResurrectTimeoutFactorCutoff = 5
)
type Selector interface {
	Select([]*Connection) (*Connection, error)
}
type ConnectionPool interface {
	Next() (*Connection, error)   
	OnSuccess(*Connection) error  
	OnFailure(*Connection) error  
	URLs() []*url.URL             
}
type Connection struct {
	sync.Mutex
	URL       *url.URL
	IsDead    bool
	DeadSince time.Time
	Failures  int
	ID         string
	Name       string
	Roles      []string
	Attributes map[string]interface{}
}
type singleConnectionPool struct {
	connection *Connection
	metrics *metrics
}
type statusConnectionPool struct {
	sync.Mutex
	live     []*Connection  
	dead     []*Connection  
	selector Selector
	metrics *metrics
}
type roundRobinSelector struct {
	sync.Mutex
	curr int  
}
func NewConnectionPool(conns []*Connection, selector Selector) (ConnectionPool, error) {
	if len(conns) == 1 {
		return &singleConnectionPool{connection: conns[0]}, nil
	}
	if selector == nil {
		selector = &roundRobinSelector{curr: -1}
	}
	return &statusConnectionPool{live: conns, selector: selector}, nil
}
func (cp *singleConnectionPool) Next() (*Connection, error) {
	return cp.connection, nil
}
func (cp *singleConnectionPool) OnSuccess(c *Connection) error { return nil }
func (cp *singleConnectionPool) OnFailure(c *Connection) error { return nil }
func (cp *singleConnectionPool) URLs() []*url.URL { return []*url.URL{cp.connection.URL} }
func (cp *singleConnectionPool) connections() []*Connection { return []*Connection{cp.connection} }
func (cp *statusConnectionPool) Next() (*Connection, error) {
	cp.Lock()
	defer cp.Unlock()
	if len(cp.live) > 0 {
		return cp.selector.Select(cp.live)
	} else if len(cp.dead) > 0 {
		c := cp.dead[len(cp.dead)-1]
		cp.dead = cp.dead[:len(cp.dead)-1]
		c.Lock()
		defer c.Unlock()
		cp.resurrect(c, false)
		return c, nil
	}
	return nil, errors.New("no connection available")
}
func (cp *statusConnectionPool) OnSuccess(c *Connection) error {
	c.Lock()
	defer c.Unlock()
	if !c.IsDead {
		return nil
	}
	c.markAsHealthy()
	cp.Lock()
	defer cp.Unlock()
	return cp.resurrect(c, true)
}
func (cp *statusConnectionPool) OnFailure(c *Connection) error {
	cp.Lock()
	defer cp.Unlock()
	c.Lock()
	if c.IsDead {
		if debugLogger != nil {
			debugLogger.Logf("Already removed %s\n", c.URL)
		}
		c.Unlock()
		return nil
	}
	if debugLogger != nil {
		debugLogger.Logf("Removing %s...\n", c.URL)
	}
	c.markAsDead()
	cp.scheduleResurrect(c)
	c.Unlock()
	cp.dead = append(cp.dead, c)
	sort.Slice(cp.dead, func(i, j int) bool {
		c1 := cp.dead[i]
		c2 := cp.dead[j]
		c1.Lock()
		c2.Lock()
		defer c1.Unlock()
		defer c2.Unlock()
		res := c1.Failures > c2.Failures
		return res
	})
	index := -1
	for i, conn := range cp.live {
		if conn == c {
			index = i
		}
	}
	if index < 0 {
		return errors.New("connection not in live list")
	}
	copy(cp.live[index:], cp.live[index+1:])
	cp.live = cp.live[:len(cp.live)-1]
	return nil
}
func (cp *statusConnectionPool) URLs() []*url.URL {
	var urls []*url.URL
	cp.Lock()
	defer cp.Unlock()
	for _, c := range cp.live {
		urls = append(urls, c.URL)
	}
	return urls
}
func (cp *statusConnectionPool) connections() []*Connection {
	var conns []*Connection
	conns = append(conns, cp.live...)
	conns = append(conns, cp.dead...)
	return conns
}
func (cp *statusConnectionPool) resurrect(c *Connection, removeDead bool) error {
	if debugLogger != nil {
		debugLogger.Logf("Resurrecting %s\n", c.URL)
	}
	c.markAsLive()
	cp.live = append(cp.live, c)
	if removeDead {
		index := -1
		for i, conn := range cp.dead {
			if conn == c {
				index = i
			}
		}
		if index >= 0 {
			copy(cp.dead[index:], cp.dead[index+1:])
			cp.dead = cp.dead[:len(cp.dead)-1]
		}
	}
	return nil
}
func (cp *statusConnectionPool) scheduleResurrect(c *Connection) {
	factor := math.Min(float64(c.Failures-1), float64(defaultResurrectTimeoutFactorCutoff))
	timeout := time.Duration(defaultResurrectTimeoutInitial.Seconds() * math.Exp2(factor) * float64(time.Second))
	if debugLogger != nil {
		debugLogger.Logf("Resurrect %s (failures=%d, factor=%1.1f, timeout=%s) in %s\n", c.URL, c.Failures, factor, timeout, c.DeadSince.Add(timeout).Sub(time.Now().UTC()).Truncate(time.Second))
	}
	time.AfterFunc(timeout, func() {
		cp.Lock()
		defer cp.Unlock()
		c.Lock()
		defer c.Unlock()
		if !c.IsDead {
			if debugLogger != nil {
				debugLogger.Logf("Already resurrected %s\n", c.URL)
			}
			return
		}
		cp.resurrect(c, true)
	})
}
func (s *roundRobinSelector) Select(conns []*Connection) (*Connection, error) {
	s.Lock()
	defer s.Unlock()
	s.curr = (s.curr + 1) % len(conns)
	return conns[s.curr], nil
}
func (c *Connection) markAsDead() {
	c.IsDead = true
	if c.DeadSince.IsZero() {
		c.DeadSince = time.Now().UTC()
	}
	c.Failures++
}