Skip to content

Commit

Permalink
HealthCheck: Set initial state
Browse files Browse the repository at this point in the history
When starting health checks, do a single check before starting the async health checker to set the initial state of the client.

Fixes #1840

Also minor bonus, that you cannot accidentally start two if status is offline.
  • Loading branch information
klauspost committed Jun 19, 2023
1 parent a73fe95 commit 8ddc7cb
Showing 1 changed file with 23 additions and 7 deletions.
30 changes: 23 additions & 7 deletions api.go
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,8 @@ const (
online = 1
)

// IsOnline returns true if healthcheck enabled and client is online
// IsOnline returns true if healthcheck enabled and client is online.
// If HealthCheck function has not been called this will always return true.
func (c *Client) IsOnline() bool {
return !c.IsOffline()
}
Expand All @@ -374,22 +375,37 @@ func (c *Client) markOffline() {
}

// IsOffline returns true if healthcheck enabled and client is offline
// If HealthCheck function has not been called this will always return false.
func (c *Client) IsOffline() bool {
return atomic.LoadInt32(&c.healthStatus) == offline
}

// HealthCheck starts a healthcheck to see if endpoint is up. Returns a context cancellation function
// and and error if health check is already started
// HealthCheck starts a healthcheck to see if endpoint is up.
// Returns a context cancellation function, to stop the health check,
// and an error if health check is already started.
func (c *Client) HealthCheck(hcDuration time.Duration) (context.CancelFunc, error) {
if atomic.LoadInt32(&c.healthStatus) == online {
if atomic.LoadInt32(&c.healthStatus) != unknown {
return nil, fmt.Errorf("health check is running")
}
if hcDuration < 1*time.Second {
return nil, fmt.Errorf("health check duration should be atleast 1 second")
return nil, fmt.Errorf("health check duration should be at least 1 second")
}
ctx, cancelFn := context.WithCancel(context.Background())
atomic.StoreInt32(&c.healthStatus, online)
probeBucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "probe-health-")
ctx, cancelFn := context.WithCancel(context.Background())
atomic.StoreInt32(&c.healthStatus, offline)
{
// Change to online, if we can connect.
gctx, gcancel := context.WithTimeout(ctx, 3*time.Second)
_, err := c.getBucketLocation(gctx, probeBucketName)
gcancel()
if !IsNetworkOrHostDown(err, false) {
switch ToErrorResponse(err).Code {
case "NoSuchBucket", "AccessDenied", "":
atomic.CompareAndSwapInt32(&c.healthStatus, offline, online)
}
}
}

go func(duration time.Duration) {
timer := time.NewTimer(duration)
defer timer.Stop()
Expand Down

0 comments on commit 8ddc7cb

Please sign in to comment.