vendor: update all dependencies to latest versions
This commit is contained in:
+190
-32
@@ -201,6 +201,7 @@ func (s *session) refreshIdle() bool {
|
||||
if s.valid && s.idleList != nil {
|
||||
// session is in idle list, refresh its session id.
|
||||
sid, s.id = s.id, sid
|
||||
s.createTime = time.Now()
|
||||
if s.tx != nil {
|
||||
s.tx = nil
|
||||
s.pool.idleWriteList.Remove(s.idleList)
|
||||
@@ -351,17 +352,23 @@ type SessionPoolConfig struct {
|
||||
// to be broken, it will still be evicted from session pool, therefore it is
|
||||
// posssible that the number of opened sessions drops below MinOpened.
|
||||
MinOpened uint64
|
||||
// MaxSessionAge is the maximum duration that a session can be reused, zero
|
||||
// maxSessionAge is the maximum duration that a session can be reused, zero
|
||||
// means session pool will never expire sessions.
|
||||
MaxSessionAge time.Duration
|
||||
maxSessionAge time.Duration
|
||||
// MaxIdle is the maximum number of idle sessions, pool is allowed to keep. Defaults to 0.
|
||||
MaxIdle uint64
|
||||
// MaxBurst is the maximum number of concurrent session creation requests. Defaults to 10.
|
||||
MaxBurst uint64
|
||||
// WriteSessions is the fraction of sessions we try to keep prepared for write.
|
||||
WriteSessions float64
|
||||
// HealthCheckWorkers is number of workers used by health checker for this pool.
|
||||
HealthCheckWorkers int
|
||||
// HealthCheckInterval is how often the health checker pings a session.
|
||||
// HealthCheckInterval is how often the health checker pings a session. Defaults to 5 min.
|
||||
HealthCheckInterval time.Duration
|
||||
// healthCheckMaintainerEnabled enables the session pool maintainer.
|
||||
healthCheckMaintainerEnabled bool
|
||||
// healthCheckSampleInterval is how often the health checker samples live session (for use in maintaining session pool size). Defaults to 1 min.
|
||||
healthCheckSampleInterval time.Duration
|
||||
}
|
||||
|
||||
// errNoRPCGetter returns error for SessionPoolConfig missing getRPCClient method.
|
||||
@@ -436,9 +443,13 @@ func newSessionPool(db string, config SessionPoolConfig, md metadata.MD) (*sessi
|
||||
if config.HealthCheckInterval == 0 {
|
||||
config.HealthCheckInterval = 5 * time.Minute
|
||||
}
|
||||
if config.healthCheckSampleInterval == 0 {
|
||||
config.healthCheckSampleInterval = time.Minute
|
||||
}
|
||||
// On GCE VM, within the same region an healthcheck ping takes on average 10ms to finish, given a 5 minutes interval and
|
||||
// 10 healthcheck workers, a healthChecker can effectively mantain 100 checks_per_worker/sec * 10 workers * 300 seconds = 300K sessions.
|
||||
pool.hc = newHealthChecker(config.HealthCheckInterval, config.HealthCheckWorkers, pool)
|
||||
pool.hc = newHealthChecker(config.HealthCheckInterval, config.HealthCheckWorkers, config.healthCheckSampleInterval, pool)
|
||||
close(pool.hc.ready)
|
||||
return pool, nil
|
||||
}
|
||||
|
||||
@@ -666,15 +677,15 @@ func (p *sessionPool) recycle(s *session) bool {
|
||||
// Reject the session if session is invalid or pool itself is invalid.
|
||||
return false
|
||||
}
|
||||
if p.MaxSessionAge != 0 && s.createTime.Add(p.MaxSessionAge).Before(time.Now()) && p.numOpened > p.MinOpened {
|
||||
// session expires and number of opened sessions exceeds MinOpened, let the session destroy itself.
|
||||
if p.maxSessionAge != 0 && s.createTime.Add(p.maxSessionAge).Before(time.Now()) && p.numOpened > p.MinOpened {
|
||||
// session expires and number of opened sessions exceeds MinOpened, let the session itself.
|
||||
return false
|
||||
}
|
||||
// Hot sessions will be converging at the front of the list, cold sessions will be evicted by healthcheck workers.
|
||||
// Put session at the back of the list to round robin for load balancing across channels.
|
||||
if s.isWritePrepared() {
|
||||
s.setIdleList(p.idleWriteList.PushFront(s))
|
||||
s.setIdleList(p.idleWriteList.PushBack(s))
|
||||
} else {
|
||||
s.setIdleList(p.idleList.PushFront(s))
|
||||
s.setIdleList(p.idleList.PushBack(s))
|
||||
}
|
||||
// Broadcast that a session has been returned to idle list.
|
||||
close(p.mayGetSession)
|
||||
@@ -762,21 +773,34 @@ type healthChecker struct {
|
||||
waitWorkers sync.WaitGroup
|
||||
// pool is the underlying session pool.
|
||||
pool *sessionPool
|
||||
// closed marks if a healthChecker has been closed.
|
||||
closed bool
|
||||
// sampleInterval is the interval of sampling by the maintainer.
|
||||
sampleInterval time.Duration
|
||||
// ready is used to signal that maintainer can start running.
|
||||
ready chan struct{}
|
||||
// done is used to signal that health checker should be closed.
|
||||
done chan struct{}
|
||||
// once is used for closing channel done only once.
|
||||
once sync.Once
|
||||
}
|
||||
|
||||
// newHealthChecker initializes new instance of healthChecker.
|
||||
func newHealthChecker(interval time.Duration, workers int, pool *sessionPool) *healthChecker {
|
||||
func newHealthChecker(interval time.Duration, workers int, sampleInterval time.Duration, pool *sessionPool) *healthChecker {
|
||||
if workers <= 0 {
|
||||
workers = 1
|
||||
}
|
||||
hc := &healthChecker{
|
||||
interval: interval,
|
||||
workers: workers,
|
||||
pool: pool,
|
||||
interval: interval,
|
||||
workers: workers,
|
||||
pool: pool,
|
||||
sampleInterval: sampleInterval,
|
||||
ready: make(chan struct{}),
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
for i := 0; i < hc.workers; i++ {
|
||||
if hc.pool.healthCheckMaintainerEnabled {
|
||||
hc.waitWorkers.Add(1)
|
||||
go hc.maintainer()
|
||||
}
|
||||
for i := 1; i <= hc.workers; i++ {
|
||||
hc.waitWorkers.Add(1)
|
||||
go hc.worker(i)
|
||||
}
|
||||
@@ -785,17 +809,18 @@ func newHealthChecker(interval time.Duration, workers int, pool *sessionPool) *h
|
||||
|
||||
// close closes the healthChecker and waits for all healthcheck workers to exit.
|
||||
func (hc *healthChecker) close() {
|
||||
hc.mu.Lock()
|
||||
hc.closed = true
|
||||
hc.mu.Unlock()
|
||||
hc.once.Do(func() { close(hc.done) })
|
||||
hc.waitWorkers.Wait()
|
||||
}
|
||||
|
||||
// isClosing checks if a healthChecker is already closing.
|
||||
func (hc *healthChecker) isClosing() bool {
|
||||
hc.mu.Lock()
|
||||
defer hc.mu.Unlock()
|
||||
return hc.closed
|
||||
select {
|
||||
case <-hc.done:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// getInterval gets the healthcheck interval.
|
||||
@@ -856,7 +881,7 @@ func (hc *healthChecker) healthCheck(s *session) {
|
||||
s.destroy(false)
|
||||
return
|
||||
}
|
||||
if s.pool.MaxSessionAge != 0 && s.createTime.Add(s.pool.MaxSessionAge).Before(time.Now()) {
|
||||
if s.pool.maxSessionAge != 0 && s.createTime.Add(s.pool.maxSessionAge).Before(time.Now()) {
|
||||
// Session reaches its maximum age, retire it. Failing that try to refresh it.
|
||||
if s.destroy(true) || !s.refreshIdle() {
|
||||
return
|
||||
@@ -870,9 +895,6 @@ func (hc *healthChecker) healthCheck(s *session) {
|
||||
|
||||
// worker performs the healthcheck on sessions in healthChecker's priority queue.
|
||||
func (hc *healthChecker) worker(i int) {
|
||||
if log.V(2) {
|
||||
log.Infof("Starting health check worker %v", i)
|
||||
}
|
||||
// Returns a session which we should ping to keep it alive.
|
||||
getNextForPing := func() *session {
|
||||
hc.pool.mu.Lock()
|
||||
@@ -918,9 +940,6 @@ func (hc *healthChecker) worker(i int) {
|
||||
|
||||
for {
|
||||
if hc.isClosing() {
|
||||
if log.V(2) {
|
||||
log.Infof("Closing health check worker %v", i)
|
||||
}
|
||||
// Exit when the pool has been closed and all sessions have been destroyed
|
||||
// or when health checker has been closed.
|
||||
hc.waitWorkers.Done()
|
||||
@@ -929,8 +948,12 @@ func (hc *healthChecker) worker(i int) {
|
||||
ws := getNextForTx()
|
||||
if ws != nil {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||
defer cancel()
|
||||
ws.prepareForWrite(contextWithOutgoingMetadata(ctx, hc.pool.md))
|
||||
err := ws.prepareForWrite(contextWithOutgoingMetadata(ctx, hc.pool.md))
|
||||
cancel()
|
||||
if err != nil {
|
||||
// TODO(dixiao): handle error properly
|
||||
log.Errorf("prepareForWrite failed: %v", err)
|
||||
}
|
||||
hc.pool.recycle(ws)
|
||||
hc.pool.mu.Lock()
|
||||
hc.pool.prepareReqs--
|
||||
@@ -945,7 +968,13 @@ func (hc *healthChecker) worker(i int) {
|
||||
if pause > int64(hc.interval) {
|
||||
pause = int64(hc.interval)
|
||||
}
|
||||
<-time.After(time.Duration(rand.Int63n(pause) + pause/2))
|
||||
select {
|
||||
case <-time.After(time.Duration(rand.Int63n(pause) + pause/2)):
|
||||
break
|
||||
case <-hc.done:
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
continue
|
||||
}
|
||||
@@ -953,6 +982,135 @@ func (hc *healthChecker) worker(i int) {
|
||||
}
|
||||
}
|
||||
|
||||
// maintainer maintains the maxSessionsInUse by a window of kWindowSize * sampleInterval.
|
||||
// Based on this information, health checker will try to maintain the number of sessions by hc..
|
||||
func (hc *healthChecker) maintainer() {
|
||||
// Wait so that pool is ready.
|
||||
<-hc.ready
|
||||
|
||||
var (
|
||||
windowSize uint64 = 10
|
||||
iteration uint64
|
||||
timeout <-chan time.Time
|
||||
)
|
||||
|
||||
// replenishPool is run if numOpened is less than sessionsToKeep, timeouts on sampleInterval.
|
||||
replenishPool := func(sessionsToKeep uint64) {
|
||||
ctx, _ := context.WithTimeout(context.Background(), hc.sampleInterval)
|
||||
for {
|
||||
select {
|
||||
case <-timeout:
|
||||
return
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
p := hc.pool
|
||||
p.mu.Lock()
|
||||
// Take budget before the actual session creation.
|
||||
if sessionsToKeep <= p.numOpened {
|
||||
p.mu.Unlock()
|
||||
break
|
||||
}
|
||||
p.numOpened++
|
||||
p.createReqs++
|
||||
shouldPrepareWrite := p.shouldPrepareWrite()
|
||||
p.mu.Unlock()
|
||||
var (
|
||||
s *session
|
||||
err error
|
||||
)
|
||||
if s, err = p.createSession(ctx); err != nil {
|
||||
log.Warningf("Failed to create session, error: %v", toSpannerError(err))
|
||||
continue
|
||||
}
|
||||
if shouldPrepareWrite {
|
||||
if err = s.prepareForWrite(ctx); err != nil {
|
||||
log.Warningf("Failed to prepare session, error: %v", toSpannerError(err))
|
||||
continue
|
||||
}
|
||||
}
|
||||
p.recycle(s)
|
||||
}
|
||||
}
|
||||
|
||||
// shrinkPool, scales down the session pool.
|
||||
shrinkPool := func(sessionsToKeep uint64) {
|
||||
for {
|
||||
select {
|
||||
case <-timeout:
|
||||
return
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
p := hc.pool
|
||||
p.mu.Lock()
|
||||
|
||||
if sessionsToKeep >= p.numOpened {
|
||||
p.mu.Unlock()
|
||||
break
|
||||
}
|
||||
|
||||
var s *session
|
||||
if p.idleList.Len() > 0 {
|
||||
s = p.idleList.Front().Value.(*session)
|
||||
} else if p.idleWriteList.Len() > 0 {
|
||||
s = p.idleWriteList.Front().Value.(*session)
|
||||
}
|
||||
p.mu.Unlock()
|
||||
if s != nil {
|
||||
// destroy session as expire.
|
||||
s.destroy(true)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
if hc.isClosing() {
|
||||
hc.waitWorkers.Done()
|
||||
return
|
||||
}
|
||||
|
||||
// maxSessionsInUse is the maximum number of sessions in use concurrently over a period of time.
|
||||
var maxSessionsInUse uint64
|
||||
|
||||
// Updates metrics.
|
||||
hc.pool.mu.Lock()
|
||||
currSessionsInUse := hc.pool.numOpened - uint64(hc.pool.idleList.Len()) - uint64(hc.pool.idleWriteList.Len())
|
||||
currSessionsOpened := hc.pool.numOpened
|
||||
hc.pool.mu.Unlock()
|
||||
|
||||
hc.mu.Lock()
|
||||
if iteration%windowSize == 0 || maxSessionsInUse < currSessionsInUse {
|
||||
maxSessionsInUse = currSessionsInUse
|
||||
}
|
||||
sessionsToKeep := maxUint64(hc.pool.MinOpened,
|
||||
minUint64(currSessionsOpened, hc.pool.MaxIdle+maxSessionsInUse))
|
||||
hc.mu.Unlock()
|
||||
|
||||
timeout = time.After(hc.sampleInterval)
|
||||
// Replenish or Shrink pool if needed.
|
||||
// Note: we don't need to worry about pending create session requests, we only need to sample the current sessions in use.
|
||||
// the routines will not try to create extra / delete creating sessions.
|
||||
if sessionsToKeep > currSessionsOpened {
|
||||
replenishPool(sessionsToKeep)
|
||||
} else {
|
||||
shrinkPool(sessionsToKeep)
|
||||
}
|
||||
|
||||
select {
|
||||
case <-timeout:
|
||||
break
|
||||
case <-hc.done:
|
||||
break
|
||||
}
|
||||
iteration++
|
||||
}
|
||||
}
|
||||
|
||||
// shouldDropSession returns true if a particular error leads to the removal of a session
|
||||
func shouldDropSession(err error) bool {
|
||||
if err == nil {
|
||||
|
||||
Reference in New Issue
Block a user