Skip to content

Commit b9a53db

Browse files
authored
Merge pull request #8101 from gyuho/randomize-renew
lease: randomize expiry on initial refresh call
2 parents 639687b + 95bc33f commit b9a53db

File tree

3 files changed

+51
-8
lines changed

3 files changed

+51
-8
lines changed

integration/v3_lease_test.go

+1-7
Original file line numberDiff line numberDiff line change
@@ -66,16 +66,10 @@ func TestV3LeasePrmote(t *testing.T) {
6666
// it was going to expire anyway.
6767
time.Sleep(3 * time.Second)
6868

69+
// expiring lease should be renewed with randomized delta
6970
if !leaseExist(t, clus, lresp.ID) {
7071
t.Error("unexpected lease not exists")
7172
}
72-
73-
// let lease expires. total lease = 5 seconds and we already
74-
// waits for 3 seconds, so 3 seconds more is enough.
75-
time.Sleep(3 * time.Second)
76-
if leaseExist(t, clus, lresp.ID) {
77-
t.Error("unexpected lease exists")
78-
}
7973
}
8074

8175
// TestV3LeaseRevoke ensures a key is deleted once its lease is revoked.

lease/lessor.go

+14-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"encoding/binary"
1919
"errors"
2020
"math"
21+
"math/rand"
2122
"sort"
2223
"sync"
2324
"sync/atomic"
@@ -326,10 +327,22 @@ func (le *lessor) Promote(extend time.Duration) {
326327

327328
// refresh the expiries of all leases.
328329
for _, l := range le.leaseMap {
329-
l.refresh(extend)
330+
// randomize expiry with 士10%, otherwise leases of same TTL
331+
// will expire all at the same time,
332+
l.refresh(extend + computeRandomDelta(l.ttl))
330333
}
331334
}
332335

336+
func computeRandomDelta(seconds int64) time.Duration {
337+
var delta int64
338+
if seconds > 10 {
339+
delta = int64(float64(seconds) * 0.1 * rand.Float64())
340+
} else {
341+
delta = rand.Int63n(10)
342+
}
343+
return time.Duration(delta) * time.Second
344+
}
345+
333346
func (le *lessor) Demote() {
334347
le.mu.Lock()
335348
defer le.mu.Unlock()

lease/lessor_test.go

+36
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"time"
2727

2828
"github.com/coreos/etcd/mvcc/backend"
29+
"github.com/coreos/etcd/pkg/monotime"
2930
)
3031

3132
const (
@@ -210,6 +211,41 @@ func TestLessorRenew(t *testing.T) {
210211
}
211212
}
212213

214+
// TestLessorRenewRandomize ensures Lessor renews with randomized expiry.
215+
func TestLessorRenewRandomize(t *testing.T) {
216+
dir, be := NewTestBackend(t)
217+
defer os.RemoveAll(dir)
218+
219+
le := newLessor(be, minLeaseTTL)
220+
for i := LeaseID(1); i <= 10; i++ {
221+
if _, err := le.Grant(i, 3600); err != nil {
222+
t.Fatal(err)
223+
}
224+
}
225+
226+
// simulate stop and recovery
227+
le.Stop()
228+
be.Close()
229+
bcfg := backend.DefaultBackendConfig()
230+
bcfg.Path = filepath.Join(dir, "be")
231+
be = backend.New(bcfg)
232+
defer be.Close()
233+
le = newLessor(be, minLeaseTTL)
234+
235+
now := monotime.Now()
236+
237+
// extend after recovery should randomize expiries
238+
le.Promote(0)
239+
240+
for _, l := range le.leaseMap {
241+
leftSeconds := uint64(float64(l.expiry-now) * float64(1e-9))
242+
pc := (float64(leftSeconds-3600) / float64(3600)) * 100
243+
if pc > 10.0 || pc < -10.0 || pc == 0 { // should be within 士10%
244+
t.Fatalf("expected randomized expiry, got %d seconds (ttl: 3600)", leftSeconds)
245+
}
246+
}
247+
}
248+
213249
func TestLessorDetach(t *testing.T) {
214250
dir, be := NewTestBackend(t)
215251
defer os.RemoveAll(dir)

0 commit comments

Comments
 (0)