Skip to content

Commit

Permalink
lease: randomize expiry on initial refresh call
Browse files Browse the repository at this point in the history
Randomize the very first expiry on lease recovery
to prevent recovered leases from expiring all at
the same time.

Address etcd-io#8096.

Signed-off-by: Gyu-Ho Lee <gyuhox@gmail.com>
  • Loading branch information
gyuho committed Jun 15, 2017
1 parent 037e33e commit 5bba057
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 1 deletion.
15 changes: 14 additions & 1 deletion lease/lessor.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"encoding/binary"
"errors"
"math"
"math/rand"
"sort"
"sync"
"sync/atomic"
Expand Down Expand Up @@ -326,10 +327,22 @@ func (le *lessor) Promote(extend time.Duration) {

// refresh the expiries of all leases.
for _, l := range le.leaseMap {
l.refresh(extend)
// randomize expiry with 士10%, otherwise leases of same TTL
// will expire all at the same time,
l.refresh(extend + computeRandomDelta(l.ttl))
}
}

func computeRandomDelta(seconds int64) time.Duration {
var delta int64
if seconds > 10 {
delta = int64(float64(seconds) * 0.1 * rand.Float64())
} else {
delta = rand.Int63n(10)
}
return time.Duration(delta) * time.Second
}

func (le *lessor) Demote() {
le.mu.Lock()
defer le.mu.Unlock()
Expand Down
36 changes: 36 additions & 0 deletions lease/lessor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"time"

"github.com/coreos/etcd/mvcc/backend"
"github.com/coreos/etcd/pkg/monotime"
)

const (
Expand Down Expand Up @@ -210,6 +211,41 @@ func TestLessorRenew(t *testing.T) {
}
}

// TestLessorRenewRandomize ensures Lessor renews with randomized expiry.
func TestLessorRenewRandomize(t *testing.T) {
dir, be := NewTestBackend(t)
defer os.RemoveAll(dir)

le := newLessor(be, minLeaseTTL)
for i := LeaseID(1); i <= 10; i++ {
if _, err := le.Grant(i, 3600); err != nil {
t.Fatal(err)
}
}

// simulate stop and recovery
le.Stop()
be.Close()
bcfg := backend.DefaultBackendConfig()
bcfg.Path = filepath.Join(dir, "be")
be = backend.New(bcfg)
defer be.Close()
le = newLessor(be, minLeaseTTL)

now := monotime.Now()

// extend after recovery should randomize expiries
le.Promote(0)

for _, l := range le.leaseMap {
leftSeconds := uint64(float64(l.expiry-now) * float64(1e-9))
pc := (float64(leftSeconds-3600) / float64(3600)) * 100
if pc > 10.0 || pc < -10.0 || pc == 0 { // should be within 士10%
t.Fatalf("expected randomized expiry, got %d seconds (ttl: 3600)", leftSeconds)
}
}
}

func TestLessorDetach(t *testing.T) {
dir, be := NewTestBackend(t)
defer os.RemoveAll(dir)
Expand Down

0 comments on commit 5bba057

Please sign in to comment.