Browse Source

Merge pull request #6888 from fanminshi/use_monotonic_time_for_lease

Use monotonic time in lease
Xiang Li 9 năm trước cách đây
mục cha
commit
8eec86f7fb

+ 7 - 7
lease/lessor.go

@@ -24,6 +24,7 @@ import (
 
 	"github.com/coreos/etcd/lease/leasepb"
 	"github.com/coreos/etcd/mvcc/backend"
+	"github.com/coreos/etcd/pkg/monotime"
 )
 
 const (
@@ -33,9 +34,8 @@ const (
 
 var (
 	leaseBucketName = []byte("lease")
-	// do not use maxInt64 since it can overflow time which will add
-	// the offset of unix time (1970yr to seconds).
-	forever = time.Unix(math.MaxInt64>>1, 0)
+
+	forever = monotime.Time(math.MaxInt64)
 
 	ErrNotPrimary    = errors.New("not a primary lessor")
 	ErrLeaseNotFound = errors.New("lease not found")
@@ -504,8 +504,8 @@ type Lease struct {
 	ttl int64 // time to live in seconds
 
 	itemSet map[LeaseItem]struct{}
-	// expiry time in unixnano
-	expiry  time.Time
+	// expiry is time when lease should expire
+	expiry  monotime.Time
 	revokec chan struct{}
 }
 
@@ -534,7 +534,7 @@ func (l *Lease) TTL() int64 {
 
 // refresh refreshes the expiry of the lease.
 func (l *Lease) refresh(extend time.Duration) {
-	l.expiry = time.Now().Add(extend + time.Second*time.Duration(l.ttl))
+	l.expiry = monotime.Now().Add(extend + time.Duration(l.ttl)*time.Second)
 }
 
 // forever sets the expiry of lease to be forever.
@@ -551,7 +551,7 @@ func (l *Lease) Keys() []string {
 
 // Remaining returns the remaining time of the lease.
 func (l *Lease) Remaining() time.Duration {
-	return l.expiry.Sub(time.Now())
+	return time.Duration(l.expiry - monotime.Now())
 }
 
 type LeaseItem struct {

+ 7 - 4
lease/lessor_test.go

@@ -26,7 +26,10 @@ import (
 	"github.com/coreos/etcd/mvcc/backend"
 )
 
-const minLeaseTTL = int64(5)
+const (
+	minLeaseTTL         = int64(5)
+	minLeaseTTLDuration = time.Duration(minLeaseTTL) * time.Second
+)
 
 // TestLessorGrant ensures Lessor can grant wanted lease.
 // The granted lease should have a unique ID with a term
@@ -48,8 +51,8 @@ func TestLessorGrant(t *testing.T) {
 	if !reflect.DeepEqual(gl, l) {
 		t.Errorf("lease = %v, want %v", gl, l)
 	}
-	if l.expiry.Sub(time.Now()) < time.Duration(minLeaseTTL)*time.Second-time.Second {
-		t.Errorf("term = %v, want at least %v", l.expiry.Sub(time.Now()), time.Duration(minLeaseTTL)*time.Second-time.Second)
+	if l.Remaining() < minLeaseTTLDuration-time.Second {
+		t.Errorf("term = %v, want at least %v", l.Remaining(), minLeaseTTLDuration-time.Second)
 	}
 
 	nl, err := le.Grant(1, 1)
@@ -152,7 +155,7 @@ func TestLessorRenew(t *testing.T) {
 	}
 
 	l = le.Lookup(l.ID)
-	if l.expiry.Sub(time.Now()) < 9*time.Second {
+	if l.Remaining() < 9*time.Second {
 		t.Errorf("failed to renew the lease")
 	}
 }

+ 6 - 0
pkg/monotime/issue15006.s

@@ -0,0 +1,6 @@
+// Copyright (C) 2016  Arista Networks, Inc.
+// Use of this source code is governed by the Apache License 2.0
+// that can be found in the COPYING file.
+
+// This file is intentionally empty.
+// It's a workaround for https://github.com/golang/go/issues/15006

+ 26 - 0
pkg/monotime/monotime.go

@@ -0,0 +1,26 @@
+// Copyright 2016 The etcd Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package monotime
+
+import (
+	"time"
+)
+
+// Time represents a point in monotonic time
+type Time uint64
+
+func (t Time) Add(d time.Duration) Time {
+	return Time(uint64(t) + uint64(d.Nanoseconds()))
+}

+ 24 - 0
pkg/monotime/nanotime.go

@@ -0,0 +1,24 @@
+// Copyright (C) 2016  Arista Networks, Inc.
+// Use of this source code is governed by the Apache License 2.0
+// that can be found in the COPYING file.
+
+// Package monotime provides a fast monotonic clock source.
+package monotime
+
+import (
+	_ "unsafe" // required to use //go:linkname
+)
+
+//go:noescape
+//go:linkname nanotime runtime.nanotime
+func nanotime() int64
+
+// Now returns the current time in nanoseconds from a monotonic clock.
+// The time returned is based on some arbitrary platform-specific point in the
+// past.  The time returned is guaranteed to increase monotonically at a
+// constant rate, unlike time.Now() from the Go standard library, which may
+// slow down, speed up, jump forward or backward, due to NTP activity or leap
+// seconds.
+func Now() Time {
+	return Time(nanotime())
+}

+ 22 - 0
pkg/monotime/nanotime_test.go

@@ -0,0 +1,22 @@
+// Copyright (C) 2016  Arista Networks, Inc.
+// Use of this source code is governed by the Apache License 2.0
+
+// Package monotime provides a fast monotonic clock source.
+
+package monotime
+
+import (
+	"testing"
+)
+
+func TestNow(t *testing.T) {
+	for i := 0; i < 100; i++ {
+		t1 := Now()
+		t2 := Now()
+		// I honestly thought that we needed >= here, but in some environments
+		// two consecutive calls can return the same value!
+		if t1 > t2 {
+			t.Fatalf("t1=%d should have been less than or equal to t2=%d", t1, t2)
+		}
+	}
+}