in internal/cloudsql/instance.go [358:463]
func (i *RefreshAheadCache) scheduleRefresh(d time.Duration) *refreshOperation {
r := &refreshOperation{}
r.ready = make(chan struct{})
r.timer = time.AfterFunc(d, func() {
// instance has been closed, don't schedule anything
if err := i.ctx.Err(); err != nil {
i.logger.Debugf(
context.Background(),
"[%v] Instance is closed, stopping refresh operations",
i.connName.String(),
)
r.err = err
close(r.ready)
return
}
i.logger.Debugf(
context.Background(),
"[%v] Connection info refresh operation started",
i.connName.String(),
)
ctx, cancel := context.WithTimeout(i.ctx, i.refreshTimeout)
defer cancel()
// avoid refreshing too often to try not to tax the SQL Admin
// API quotas
err := i.l.Wait(ctx)
if err != nil {
r.err = errtype.NewDialError(
"context was canceled or expired before refresh completed",
i.connName.String(),
nil,
)
} else {
var useIAMAuthN bool
i.mu.Lock()
useIAMAuthN = i.useIAMAuthNDial
i.mu.Unlock()
r.result, r.err = i.r.ConnectionInfo(
ctx, i.connName, useIAMAuthN,
)
}
switch r.err {
case nil:
i.logger.Debugf(
ctx,
"[%v] Connection info refresh operation complete",
i.connName.String(),
)
i.logger.Debugf(
ctx,
"[%v] Current certificate expiration = %v",
i.connName.String(),
r.result.Expiration.UTC().Format(time.RFC3339),
)
default:
i.logger.Debugf(
ctx,
"[%v] Connection info refresh operation failed, err = %v",
i.connName.String(),
r.err,
)
}
close(r.ready)
// Once the refresh is complete, update "current" with working
// refreshOperation and schedule a new refresh
i.mu.Lock()
defer i.mu.Unlock()
// if failed, scheduled the next refresh immediately
if r.err != nil {
i.logger.Debugf(
ctx,
"[%v] Connection info refresh operation scheduled immediately",
i.connName.String(),
)
i.next = i.scheduleRefresh(0)
// If the latest refreshOperation is bad, avoid replacing the
// used refreshOperation while it's still valid and potentially
// able to provide successful connections. TODO: This
// means that errors while the current refreshOperation is still
// valid are suppressed. We should try to surface
// errors in a more meaningful way.
if !i.cur.isValid() {
i.cur = r
}
return
}
// Update the current results, and schedule the next refresh in
// the future
i.cur = r
t := refreshDuration(time.Now(), i.cur.result.Expiration)
i.logger.Debugf(
ctx,
"[%v] Connection info refresh operation scheduled at %v (now + %v)",
i.connName.String(),
time.Now().Add(t).UTC().Format(time.RFC3339),
t.Round(time.Minute),
)
i.next = i.scheduleRefresh(t)
})
return r
}