diff --git a/embed/config.go b/embed/config.go index 5b361ab8622..2e1cabd505f 100644 --- a/embed/config.go +++ b/embed/config.go @@ -222,6 +222,8 @@ type Config struct { ExperimentalCorruptCheckTime time.Duration `json:"experimental-corrupt-check-time"` ExperimentalEnableV2V3 string `json:"experimental-enable-v2v3"` + PersistExpiry bool `json:"persist-expiry"` + // ForceNewCluster starts a new cluster even if previously started; unsafe. ForceNewCluster bool `json:"force-new-cluster"` @@ -327,6 +329,8 @@ func NewConfig() *Config { PreVote: false, // TODO: enable by default in v3.5 + PersistExpiry: false, + loggerMu: new(sync.RWMutex), logger: nil, Logger: "capnslog", diff --git a/embed/etcd.go b/embed/etcd.go index 74033e29aa5..fa53f4e5667 100644 --- a/embed/etcd.go +++ b/embed/etcd.go @@ -191,6 +191,7 @@ func StartEtcd(inCfg *Config) (e *Etcd, err error) { LoggerConfig: cfg.loggerConfig, Debug: cfg.Debug, ForceNewCluster: cfg.ForceNewCluster, + PersistExpiry: cfg.PersistExpiry, } if e.Server, err = etcdserver.NewServer(srvcfg); err != nil { return e, err diff --git a/etcdmain/config.go b/etcdmain/config.go index c4a6d0a481a..77b9b98b7cd 100644 --- a/etcdmain/config.go +++ b/etcdmain/config.go @@ -241,6 +241,8 @@ func newConfig() *config { fs.DurationVar(&cfg.ec.ExperimentalCorruptCheckTime, "experimental-corrupt-check-time", cfg.ec.ExperimentalCorruptCheckTime, "Duration of time between cluster corruption check passes.") fs.StringVar(&cfg.ec.ExperimentalEnableV2V3, "experimental-enable-v2v3", cfg.ec.ExperimentalEnableV2V3, "v3 prefix for serving emulated v2 state.") + fs.BoolVar(&cfg.ec.PersistExpiry, "persist-expiry", cfg.ec.PersistExpiry, "Persist expiry values for leases.") + // unsafe fs.BoolVar(&cfg.ec.ForceNewCluster, "force-new-cluster", false, "Force to create a new one member cluster.") diff --git a/etcdserver/config.go b/etcdserver/config.go index 19eef8aaaa1..21cc348272d 100644 --- a/etcdserver/config.go +++ b/etcdserver/config.go @@ -91,6 +91,8 @@ type ServerConfig struct { Debug bool ForceNewCluster bool + + PersistExpiry bool } // VerifyBootstrap sanity-checks the initial config for bootstrap case diff --git a/etcdserver/server.go b/etcdserver/server.go index 0b259363d4d..2e7c2778fc8 100644 --- a/etcdserver/server.go +++ b/etcdserver/server.go @@ -517,7 +517,7 @@ func NewServer(cfg ServerConfig) (srv *EtcdServer, err error) { // always recover lessor before kv. When we recover the mvcc.KV it will reattach keys to its leases. // If we recover mvcc.KV first, it will attach the keys to the wrong lessor before it recovers. - srv.lessor = lease.NewLessor(srv.be, int64(math.Ceil(minTTL.Seconds()))) + srv.lessor = lease.NewLessor(srv.be, int64(math.Ceil(minTTL.Seconds())), cfg.PersistExpiry) srv.kv = mvcc.New(srv.getLogger(), srv.be, srv.lessor, &srv.consistIndex) if beExist { kvindex := srv.kv.ConsistentIndex() diff --git a/lease/leasehttp/http_test.go b/lease/leasehttp/http_test.go index 367cd8e64a4..ad20b4a2f18 100644 --- a/lease/leasehttp/http_test.go +++ b/lease/leasehttp/http_test.go @@ -32,7 +32,7 @@ func TestRenewHTTP(t *testing.T) { defer os.Remove(tmpPath) defer be.Close() - le := lease.NewLessor(be, int64(5)) + le := lease.NewLessor(be, int64(5), false) le.Promote(time.Second) l, err := le.Grant(1, int64(5)) if err != nil { @@ -56,7 +56,7 @@ func TestTimeToLiveHTTP(t *testing.T) { defer os.Remove(tmpPath) defer be.Close() - le := lease.NewLessor(be, int64(5)) + le := lease.NewLessor(be, int64(5), false) le.Promote(time.Second) l, err := le.Grant(1, int64(5)) if err != nil { @@ -97,7 +97,7 @@ func testApplyTimeout(t *testing.T, f func(*lease.Lease, string) error) { defer os.Remove(tmpPath) defer be.Close() - le := lease.NewLessor(be, int64(5)) + le := lease.NewLessor(be, int64(5), false) le.Promote(time.Second) l, err := le.Grant(1, int64(5)) if err != nil { diff --git a/lease/leasepb/lease.pb.go b/lease/leasepb/lease.pb.go index 4ab93767277..cc55ff81152 100644 --- a/lease/leasepb/lease.pb.go +++ b/lease/leasepb/lease.pb.go @@ -40,8 +40,9 @@ var _ = math.Inf const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package type Lease struct { - ID int64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"` - TTL int64 `protobuf:"varint,2,opt,name=TTL,proto3" json:"TTL,omitempty"` + ID int64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"` + TTL int64 `protobuf:"varint,2,opt,name=TTL,proto3" json:"TTL,omitempty"` + Expiry int64 `protobuf:"varint,3,opt,name=Expiry,proto3" json:"Expiry,omitempty"` } func (m *Lease) Reset() { *m = Lease{} } @@ -97,6 +98,11 @@ func (m *Lease) MarshalTo(dAtA []byte) (int, error) { i++ i = encodeVarintLease(dAtA, i, uint64(m.TTL)) } + if m.Expiry != 0 { + dAtA[i] = 0x18 + i++ + i = encodeVarintLease(dAtA, i, uint64(m.Expiry)) + } return i, nil } @@ -174,6 +180,9 @@ func (m *Lease) Size() (n int) { if m.TTL != 0 { n += 1 + sovLease(uint64(m.TTL)) } + if m.Expiry != 0 { + n += 1 + sovLease(uint64(m.Expiry)) + } return n } @@ -277,6 +286,25 @@ func (m *Lease) Unmarshal(dAtA []byte) error { break } } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Expiry", wireType) + } + m.Expiry = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLease + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Expiry |= (int64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipLease(dAtA[iNdEx:]) @@ -572,20 +600,21 @@ var ( func init() { proto.RegisterFile("lease.proto", fileDescriptorLease) } var fileDescriptorLease = []byte{ - // 233 bytes of a gzipped FileDescriptorProto + // 249 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0xce, 0x49, 0x4d, 0x2c, 0x4e, 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x07, 0x73, 0x0a, 0x92, 0xa4, 0x44, 0xd2, 0xf3, 0xd3, 0xf3, 0xc1, 0x62, 0xfa, 0x20, 0x16, 0x44, 0x5a, 0x4a, 0x2d, 0xb5, 0x24, 0x39, 0x45, 0x1f, 0x44, 0x14, 0xa7, 0x16, 0x95, 0xa5, 0x16, 0x21, 0x31, 0x0b, 0x92, 0xf4, 0x8b, 0x0a, 0x92, - 0x21, 0xea, 0x94, 0x34, 0xb9, 0x58, 0x7d, 0x40, 0x06, 0x09, 0xf1, 0x71, 0x31, 0x79, 0xba, 0x48, + 0x21, 0xea, 0x94, 0x1c, 0xb9, 0x58, 0x7d, 0x40, 0x06, 0x09, 0xf1, 0x71, 0x31, 0x79, 0xba, 0x48, 0x30, 0x2a, 0x30, 0x6a, 0x30, 0x07, 0x31, 0x79, 0xba, 0x08, 0x09, 0x70, 0x31, 0x87, 0x84, 0xf8, - 0x48, 0x30, 0x81, 0x05, 0x40, 0x4c, 0xa5, 0x12, 0x2e, 0x11, 0xb0, 0x52, 0xcf, 0xbc, 0x92, 0xd4, - 0xa2, 0xbc, 0xc4, 0x9c, 0xa0, 0xd4, 0xc2, 0xd2, 0xd4, 0xe2, 0x12, 0xa1, 0x18, 0x2e, 0x31, 0xb0, - 0x78, 0x48, 0x66, 0x6e, 0x6a, 0x48, 0xbe, 0x4f, 0x66, 0x59, 0x2a, 0x54, 0x06, 0x6c, 0x1a, 0xb7, - 0x91, 0x8a, 0x1e, 0xb2, 0xdd, 0x7a, 0xd8, 0xd5, 0x06, 0xe1, 0x30, 0x43, 0xa9, 0x82, 0x4b, 0x14, - 0xcd, 0xd6, 0xe2, 0x82, 0xfc, 0xbc, 0xe2, 0x54, 0xa1, 0x78, 0x2e, 0x71, 0x0c, 0x2d, 0x10, 0x29, - 0xa8, 0xbd, 0xaa, 0x04, 0xec, 0x85, 0x28, 0x0e, 0xc2, 0x65, 0x8a, 0x93, 0xc4, 0x89, 0x87, 0x72, - 0x0c, 0x17, 0x1e, 0xca, 0x31, 0x9c, 0x78, 0x24, 0xc7, 0x78, 0xe1, 0x91, 0x1c, 0xe3, 0x83, 0x47, - 0x72, 0x8c, 0x33, 0x1e, 0xcb, 0x31, 0x24, 0xb1, 0x81, 0xc3, 0xce, 0x18, 0x10, 0x00, 0x00, 0xff, - 0xff, 0x9f, 0xf2, 0x42, 0xe0, 0x91, 0x01, 0x00, 0x00, + 0x48, 0x30, 0x81, 0x05, 0x40, 0x4c, 0x21, 0x31, 0x2e, 0x36, 0xd7, 0x8a, 0x82, 0xcc, 0xa2, 0x4a, + 0x09, 0x66, 0xb0, 0x20, 0x94, 0xa7, 0x54, 0xc2, 0x25, 0x02, 0x36, 0xc2, 0x33, 0xaf, 0x24, 0xb5, + 0x28, 0x2f, 0x31, 0x27, 0x28, 0xb5, 0xb0, 0x34, 0xb5, 0xb8, 0x44, 0x28, 0x86, 0x4b, 0x0c, 0x2c, + 0x1e, 0x92, 0x99, 0x9b, 0x1a, 0x92, 0xef, 0x93, 0x59, 0x96, 0x0a, 0x95, 0x01, 0xdb, 0xc2, 0x6d, + 0xa4, 0xa2, 0x87, 0xec, 0x26, 0x3d, 0xec, 0x6a, 0x83, 0x70, 0x98, 0xa1, 0x54, 0xc1, 0x25, 0x8a, + 0x66, 0x6b, 0x71, 0x41, 0x7e, 0x5e, 0x71, 0xaa, 0x50, 0x3c, 0x97, 0x38, 0x86, 0x16, 0x88, 0x14, + 0xd4, 0x5e, 0x55, 0x02, 0xf6, 0x42, 0x14, 0x07, 0xe1, 0x32, 0xc5, 0x49, 0xe2, 0xc4, 0x43, 0x39, + 0x86, 0x0b, 0x0f, 0xe5, 0x18, 0x4e, 0x3c, 0x92, 0x63, 0xbc, 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23, + 0x39, 0xc6, 0x19, 0x8f, 0xe5, 0x18, 0x92, 0xd8, 0xc0, 0x61, 0x6a, 0x0c, 0x08, 0x00, 0x00, 0xff, + 0xff, 0x0c, 0xce, 0xf7, 0x50, 0xa9, 0x01, 0x00, 0x00, } diff --git a/lease/leasepb/lease.proto b/lease/leasepb/lease.proto index be414b993ed..1b83c39daec 100644 --- a/lease/leasepb/lease.proto +++ b/lease/leasepb/lease.proto @@ -13,6 +13,7 @@ option (gogoproto.goproto_enum_prefix_all) = false; message Lease { int64 ID = 1; int64 TTL = 2; + int64 Expiry = 3; } message LeaseInternalRequest { diff --git a/lease/lessor.go b/lease/lessor.go index 90c568f9df6..168d0bf212f 100644 --- a/lease/lessor.go +++ b/lease/lessor.go @@ -150,13 +150,15 @@ type lessor struct { stopC chan struct{} // doneC is a channel whose closure indicates that the lessor is stopped. doneC chan struct{} + + persistExpiry bool } -func NewLessor(b backend.Backend, minLeaseTTL int64) Lessor { - return newLessor(b, minLeaseTTL) +func NewLessor(b backend.Backend, minLeaseTTL int64, persistExpiry bool) Lessor { + return newLessor(b, minLeaseTTL, persistExpiry) } -func newLessor(b backend.Backend, minLeaseTTL int64) *lessor { +func newLessor(b backend.Backend, minLeaseTTL int64, persistExpiry bool) *lessor { l := &lessor{ leaseMap: make(map[LeaseID]*Lease), itemMap: make(map[LeaseItem]LeaseID), @@ -167,7 +169,9 @@ func newLessor(b backend.Backend, minLeaseTTL int64) *lessor { expiredC: make(chan []*Lease, 16), stopC: make(chan struct{}), doneC: make(chan struct{}), + persistExpiry: persistExpiry, } + l.initAndRecover() go l.runLoop() @@ -228,7 +232,7 @@ func (le *lessor) Grant(id LeaseID, ttl int64) (*Lease, error) { l.ttl = le.minLeaseTTL } - if le.isPrimary() { + if le.isPrimary() || le.persistExpiry { l.refresh(0) } else { l.forever() @@ -237,7 +241,7 @@ func (le *lessor) Grant(id LeaseID, ttl int64) (*Lease, error) { le.leaseMap[id] = l item := &LeaseWithTime{id: l.ID, expiration: l.expiry.UnixNano()} heap.Push(&le.leaseHeap, item) - l.persistTo(le.b) + l.persistTo(le.b, le.persistExpiry) return l, nil } @@ -321,6 +325,9 @@ func (le *lessor) Renew(id LeaseID) (int64, error) { l.refresh(0) item := &LeaseWithTime{id: l.ID, expiration: l.expiry.UnixNano()} heap.Push(&le.leaseHeap, item) + if le.persistExpiry { + l.persistTo(le.b, le.persistExpiry) + } return l.ttl, nil } @@ -351,7 +358,9 @@ func (le *lessor) Promote(extend time.Duration) { defer le.mu.Unlock() le.demotec = make(chan struct{}) - + if le.persistExpiry { + return + } // refresh the expiries of all leases. for _, l := range le.leaseMap { l.refresh(extend) @@ -407,9 +416,11 @@ func (le *lessor) Demote() { le.mu.Lock() defer le.mu.Unlock() - // set the expiries of all leases to forever - for _, l := range le.leaseMap { - l.forever() + if !le.persistExpiry { + // set the expiries of all leases to forever + for _, l := range le.leaseMap { + l.forever() + } } if le.demotec != nil { @@ -597,13 +608,19 @@ func (le *lessor) initAndRecover() { if lpb.TTL < le.minLeaseTTL { lpb.TTL = le.minLeaseTTL } + var expiry time.Time + if le.persistExpiry && lpb.Expiry != 0{ + expiry = time.Unix(0, lpb.Expiry) + }else{ + expiry = forever + } le.leaseMap[ID] = &Lease{ ID: ID, ttl: lpb.TTL, // itemSet will be filled in when recover key-value pairs // set expiry to forever, refresh when promoted itemSet: make(map[LeaseItem]struct{}), - expiry: forever, + expiry: expiry, revokec: make(chan struct{}), } } @@ -631,10 +648,14 @@ func (l *Lease) expired() bool { return l.Remaining() <= 0 } -func (l *Lease) persistTo(b backend.Backend) { +func (l *Lease) persistTo(b backend.Backend, expiry bool) { key := int64ToBytes(int64(l.ID)) - - lpb := leasepb.Lease{ID: int64(l.ID), TTL: int64(l.ttl)} + var lpb leasepb.Lease + if expiry{ + lpb = leasepb.Lease{ID: int64(l.ID), TTL: int64(l.ttl), Expiry: l.expiry.UnixNano()} + }else{ + lpb = leasepb.Lease{ID: int64(l.ID), TTL: int64(l.ttl)} + } val, err := lpb.Marshal() if err != nil { panic("failed to marshal lease proto item") diff --git a/lease/lessor_bench_test.go b/lease/lessor_bench_test.go index a3be6aa95b2..ba365ef5922 100644 --- a/lease/lessor_bench_test.go +++ b/lease/lessor_bench_test.go @@ -55,7 +55,7 @@ func BenchmarkLessorRevoke1000000(b *testing.B) { benchmarkLessorRevoke(1000000, func benchmarkLessorFindExpired(size int, b *testing.B) { be, tmpPath := backend.NewDefaultTmpBackend() - le := newLessor(be, minLeaseTTL) + le := newLessor(be, minLeaseTTL, false) defer le.Stop() defer cleanup(be, tmpPath) le.Promote(0) @@ -72,7 +72,7 @@ func benchmarkLessorFindExpired(size int, b *testing.B) { func benchmarkLessorGrant(size int, b *testing.B) { be, tmpPath := backend.NewDefaultTmpBackend() - le := newLessor(be, minLeaseTTL) + le := newLessor(be, minLeaseTTL, false) defer le.Stop() defer cleanup(be, tmpPath) for i := 0; i < size; i++ { @@ -86,7 +86,7 @@ func benchmarkLessorGrant(size int, b *testing.B) { func benchmarkLessorRevoke(size int, b *testing.B) { be, tmpPath := backend.NewDefaultTmpBackend() - le := newLessor(be, minLeaseTTL) + le := newLessor(be, minLeaseTTL, false) defer le.Stop() defer cleanup(be, tmpPath) for i := 0; i < size; i++ { @@ -103,7 +103,7 @@ func benchmarkLessorRevoke(size int, b *testing.B) { func benchmarkLessorRenew(size int, b *testing.B) { be, tmpPath := backend.NewDefaultTmpBackend() - le := newLessor(be, minLeaseTTL) + le := newLessor(be, minLeaseTTL, false) defer le.Stop() defer cleanup(be, tmpPath) for i := 0; i < size; i++ { diff --git a/lease/lessor_test.go b/lease/lessor_test.go index 3a39e846f72..766fdf46df8 100644 --- a/lease/lessor_test.go +++ b/lease/lessor_test.go @@ -41,7 +41,7 @@ func TestLessorGrant(t *testing.T) { defer os.RemoveAll(dir) defer be.Close() - le := newLessor(be, minLeaseTTL) + le := newLessor(be, minLeaseTTL, false) defer le.Stop() le.Promote(0) @@ -95,6 +95,29 @@ func TestLessorGrant(t *testing.T) { be.BatchTx().Unlock() } +// TestLessorGrantPresistExpiry ensures Lessor can grant an existing lease persisting expiry. +func TestLessorGrantPersistExpiry(t *testing.T) { + dir, be := NewTestBackend(t) + defer be.Close() + defer os.RemoveAll(dir) + + le := newLessor(be, minLeaseTTL, true) + defer le.Stop() + le.Promote(0) + + l, err := le.Grant(1, minLeaseTTL) + if err != nil { + t.Fatalf("failed to grant lease (%v)", err) + } + + nle := newLessor(be, minLeaseTTL, true) + + nl := nle.Lookup(l.ID) + if !l.expiry.Equal(nl.expiry) { + t.Errorf("Time remaining was not persisted. Original: %v, New: %v", l.expiry.Round(0), nl.expiry.Round(0)) + } +} + // TestLeaseConcurrentKeys ensures Lease.Keys method calls are guarded // from concurrent map writes on 'itemSet'. func TestLeaseConcurrentKeys(t *testing.T) { @@ -102,7 +125,7 @@ func TestLeaseConcurrentKeys(t *testing.T) { defer os.RemoveAll(dir) defer be.Close() - le := newLessor(be, minLeaseTTL) + le := newLessor(be, minLeaseTTL, false) defer le.Stop() le.SetRangeDeleter(func() TxnDelete { return newFakeDeleter(be) }) @@ -150,7 +173,7 @@ func TestLessorRevoke(t *testing.T) { defer os.RemoveAll(dir) defer be.Close() - le := newLessor(be, minLeaseTTL) + le := newLessor(be, minLeaseTTL, false) defer le.Stop() var fd *fakeDeleter le.SetRangeDeleter(func() TxnDelete { @@ -202,7 +225,7 @@ func TestLessorRenew(t *testing.T) { defer be.Close() defer os.RemoveAll(dir) - le := newLessor(be, minLeaseTTL) + le := newLessor(be, minLeaseTTL, false) defer le.Stop() le.Promote(0) @@ -229,6 +252,37 @@ func TestLessorRenew(t *testing.T) { } } +// TestLessorRenewPersistExpiry ensures Lessor can renew an existing lease and persists the new expiration +func TestLessorRenewPersistExpiry(t *testing.T) { + dir, be := NewTestBackend(t) + defer be.Close() + defer os.RemoveAll(dir) + + le := newLessor(be, minLeaseTTL, true) + defer le.Stop() + le.Promote(0) + + l, err := le.Grant(1, minLeaseTTL) + if err != nil { + t.Fatalf("failed to grant lease (%v)", err) + } + + ttl, err := le.Renew(l.ID) + if err != nil { + t.Fatalf("failed to renew lease (%v)", err) + } + if ttl != l.ttl { + t.Errorf("ttl = %d, want %d", ttl, l.ttl) + } + + nle := newLessor(be, minLeaseTTL, true) + + nl := nle.Lookup(l.ID) + if !l.expiry.Equal(nl.expiry) { + t.Errorf("Time remaining was not persisted. Original: %v, New: %v", l.expiry.Round(0), nl.expiry.Round(0)) + } +} + // TestLessorRenewExtendPileup ensures Lessor extends leases on promotion if too many // expire at the same time. func TestLessorRenewExtendPileup(t *testing.T) { @@ -239,7 +293,7 @@ func TestLessorRenewExtendPileup(t *testing.T) { dir, be := NewTestBackend(t) defer os.RemoveAll(dir) - le := newLessor(be, minLeaseTTL) + le := newLessor(be, minLeaseTTL, false) ttl := int64(10) for i := 1; i <= leaseRevokeRate*10; i++ { if _, err := le.Grant(LeaseID(2*i), ttl); err != nil { @@ -258,7 +312,7 @@ func TestLessorRenewExtendPileup(t *testing.T) { bcfg.Path = filepath.Join(dir, "be") be = backend.New(bcfg) defer be.Close() - le = newLessor(be, minLeaseTTL) + le = newLessor(be, minLeaseTTL, false) defer le.Stop() // extend after recovery should extend expiration on lease pile-up @@ -282,12 +336,53 @@ func TestLessorRenewExtendPileup(t *testing.T) { } } + +//TestLessorPersistExpiryPromote ensures that on promotion, lease expirations are not redistributed +//regardless of revoke rate +func TestLessorPersistExpiryPromote(t *testing.T) { + oldRevokeRate := leaseRevokeRate + defer func() { leaseRevokeRate = oldRevokeRate }() + leaseRevokeRate = 10 + + dir, be := NewTestBackend(t) + defer os.RemoveAll(dir) + + le := newLessor(be, minLeaseTTL, true) + ttl := int64(10) + expiryMap := make(map[LeaseID]time.Time) + for i := 1; i <= leaseRevokeRate*10; i++ { + l, err := le.Grant(LeaseID(2*i), ttl) + if err != nil { + t.Fatal(err) + } + expiryMap[l.ID] = l.expiry + } + + // 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, true) + defer le.Stop() + + le.Promote(0) + + for _, l := range le.leaseMap { + if !expiryMap[l.ID].Equal(l.expiry) { + t.Errorf("expected expiry to not change for %v, got %v, want %v", l.ID, l.expiry, expiryMap[l.ID]) + } + } +} + func TestLessorDetach(t *testing.T) { dir, be := NewTestBackend(t) defer os.RemoveAll(dir) defer be.Close() - le := newLessor(be, minLeaseTTL) + le := newLessor(be, minLeaseTTL, false) defer le.Stop() le.SetRangeDeleter(func() TxnDelete { return newFakeDeleter(be) }) @@ -327,7 +422,7 @@ func TestLessorRecover(t *testing.T) { defer os.RemoveAll(dir) defer be.Close() - le := newLessor(be, minLeaseTTL) + le := newLessor(be, minLeaseTTL, false) defer le.Stop() l1, err1 := le.Grant(1, 10) l2, err2 := le.Grant(2, 20) @@ -336,7 +431,7 @@ func TestLessorRecover(t *testing.T) { } // Create a new lessor with the same backend - nle := newLessor(be, minLeaseTTL) + nle := newLessor(be, minLeaseTTL, false) defer nle.Stop() nl1 := nle.Lookup(l1.ID) if nl1 == nil || nl1.ttl != l1.ttl { @@ -349,6 +444,63 @@ func TestLessorRecover(t *testing.T) { } } +// TestLessorRecoverWithExpiry ensures Lessor recovers leases +// including persisted expiry from backend. +func TestLessorRecoverWithExpiry(t *testing.T) { + dir, be := NewTestBackend(t) + defer os.RemoveAll(dir) + defer be.Close() + + le := newLessor(be, minLeaseTTL, true) + defer le.Stop() + l1, err1 := le.Grant(1, 10) + if err1 != nil { + t.Fatalf("could not grant initial lease (%v)", err1) + } + + // Create a new lessor with the same backend + nle := newLessor(be, minLeaseTTL, true) + defer nle.Stop() + nl1 := nle.Lookup(l1.ID) + if nl1 == nil || nl1.ttl != l1.ttl { + t.Errorf("nl1 = %v, want nl1.ttl= %d", nl1.ttl, l1.ttl) + } + + if nl1 == nil || !nl1.expiry.Equal(l1.expiry) { + t.Errorf("nl1.expiry = %v, want nl1.expiry= %v", nl1.expiry, l1.expiry) + } + + if nl1 == nil || nl1.expiry == forever { + t.Errorf("nl1.expiry = %v (forever)", nl1.expiry) + } +} + +func TestLessorRecoverWithExpiryPersistChanging(t *testing.T) { + dir, be := NewTestBackend(t) + defer os.RemoveAll(dir) + defer be.Close() + + le := newLessor(be, minLeaseTTL, false) + defer le.Stop() + l1, err1 := le.Grant(1, 10) + if err1 != nil { + t.Fatalf("could not grant initial lease (%v)", err1) + } + + // Create a new lessor with the same backend, but persist set to true + nle := newLessor(be, minLeaseTTL, true) + defer nle.Stop() + nl1 := nle.Lookup(l1.ID) + + if nl1 == nil || !nl1.expiry.Equal(l1.expiry) { + t.Errorf("nl1.expiry = %v, want nl1.expiry= %v", nl1.expiry, l1.expiry) + } + + if nl1 == nil || !nl1.expiry.Equal(forever) { + t.Errorf("nl1.expiry is not forever (%v), expected pre-persist leases to not have an expiry", nl1.expiry) + } +} + func TestLessorExpire(t *testing.T) { dir, be := NewTestBackend(t) defer os.RemoveAll(dir) @@ -356,7 +508,7 @@ func TestLessorExpire(t *testing.T) { testMinTTL := int64(1) - le := newLessor(be, testMinTTL) + le := newLessor(be, minLeaseTTL, false) defer le.Stop() le.Promote(1 * time.Second) @@ -408,7 +560,7 @@ func TestLessorExpireAndDemote(t *testing.T) { testMinTTL := int64(1) - le := newLessor(be, testMinTTL) + le := newLessor(be, minLeaseTTL, false) defer le.Stop() le.Promote(1 * time.Second) @@ -456,7 +608,7 @@ func TestLessorMaxTTL(t *testing.T) { defer os.RemoveAll(dir) defer be.Close() - le := newLessor(be, minLeaseTTL) + le := newLessor(be, minLeaseTTL, false) defer le.Stop() _, err := le.Grant(1, MaxLeaseTTL+1) diff --git a/snapshot/v3_snapshot.go b/snapshot/v3_snapshot.go index df8c3571643..8c269da3317 100644 --- a/snapshot/v3_snapshot.go +++ b/snapshot/v3_snapshot.go @@ -373,7 +373,7 @@ func (s *v3Manager) saveDB() error { be := backend.NewDefaultBackend(dbpath) // a lessor never timeouts leases - lessor := lease.NewLessor(be, math.MaxInt64) + lessor := lease.NewLessor(be, math.MaxInt64, false) mvs := mvcc.NewStore(s.lg, be, lessor, (*initIndex)(&commit)) txn := mvs.Write()