diff --git a/command/agent/config.go b/command/agent/config.go
index 48b347c33..7b6fc1ba6 100644
--- a/command/agent/config.go
+++ b/command/agent/config.go
@@ -263,6 +263,12 @@ type Config struct {
CheckUpdateInterval time.Duration `mapstructure:"-"`
CheckUpdateIntervalRaw string `mapstructure:"check_update_interval" json:"-"`
+ // CheckUpdateStagger enables a randomization of the CheckUpdateInterval between
+ // .5 and 1.5 of that interval. This is useful if checks happen often to stagger
+ // writes and pervent them from all executing at the end of the same CheckUpdateInterval.
+ // Off by default.
+ CheckUpdateStagger bool `mapstructure:"check_update_stagger"`
+
// ACLToken is the default token used to make requests if a per-request
// token is not provided. If not configured the 'anonymous' token is used.
ACLToken string `mapstructure:"acl_token" json:"-"`
diff --git a/command/agent/config_test.go b/command/agent/config_test.go
index 945e2152d..c21070d4e 100644
--- a/command/agent/config_test.go
+++ b/command/agent/config_test.go
@@ -494,6 +494,17 @@ func TestDecodeConfig(t *testing.T) {
t.Fatalf("bad: %#v", config)
}
+ // CheckUpdateStagger
+ input = `{"check_update_stagger": true}`
+ config, err = DecodeConfig(bytes.NewReader([]byte(input)))
+ if err != nil {
+ t.Fatalf("err: %s", err)
+ }
+
+ if !config.CheckUpdateStagger {
+ t.Fatalf("bad: %#v", config)
+ }
+
// ACLs
input = `{"acl_token": "1234", "acl_datacenter": "dc2",
"acl_ttl": "60s", "acl_down_policy": "deny",
diff --git a/command/agent/local.go b/command/agent/local.go
index f85d6671a..3726b53c7 100644
--- a/command/agent/local.go
+++ b/command/agent/local.go
@@ -3,6 +3,7 @@ package agent
import (
"fmt"
"log"
+ "math/rand"
"reflect"
"strings"
"sync"
@@ -208,7 +209,13 @@ func (l *localState) UpdateCheck(checkID, status, output string) {
if l.config.CheckUpdateInterval > 0 && check.Status == status {
check.Output = output
if _, ok := l.deferCheck[checkID]; !ok {
- deferSync := time.AfterFunc(l.config.CheckUpdateInterval, func() {
+ var intv time.Duration
+ if l.config.CheckUpdateStagger {
+ intv = time.Duration(uint64(l.config.CheckUpdateInterval)/2 + uint64(rand.Int63())%uint64(l.config.CheckUpdateInterval))
+ } else {
+ intv = l.config.CheckUpdateInterval
+ }
+ deferSync := time.AfterFunc(intv, func() {
l.Lock()
if _, ok := l.checkStatus[checkID]; ok {
l.checkStatus[checkID] = syncStatus{inSync: false}
diff --git a/command/agent/local_test.go b/command/agent/local_test.go
index 480da00cd..f95219098 100644
--- a/command/agent/local_test.go
+++ b/command/agent/local_test.go
@@ -601,7 +601,7 @@ func TestAgentAntiEntropy_Check_DeferSync(t *testing.T) {
})
}
-func TestAgentAntiEntory_deleteService_fails(t *testing.T) {
+func TestAgentAntiEntropy_deleteService_fails(t *testing.T) {
l := new(localState)
if err := l.deleteService(""); err == nil {
t.Fatalf("should have failed")
diff --git a/website/source/docs/agent/options.html.markdown b/website/source/docs/agent/options.html.markdown
index 8c36fec31..51dd8f397 100644
--- a/website/source/docs/agent/options.html.markdown
+++ b/website/source/docs/agent/options.html.markdown
@@ -353,6 +353,12 @@ definitions support being updated during a reload.
reduce write pressure. If a check ever changes state, the new state and associated
output is synchronized immediately. To disable this behavior, set the value to "0s".
+* `check_update_stagger`
+ CheckUpdateStagger enables a randomization of the CheckUpdateInterval between
+ .5 and 1.5 of that interval. This is useful if checks happen often to stagger
+ writes and pervent them from all executing at the end of the same CheckUpdateInterval.
+ Off by default.
+
* `client_addr` Equivalent to the
[`-client` command-line flag](#_client).