diff --git a/lease/lessor.go b/lease/lessor.go index ec3291bc8..73717dbe6 100644 --- a/lease/lessor.go +++ b/lease/lessor.go @@ -26,6 +26,15 @@ var ( minLeaseTerm = 5 * time.Second ) +// DeleteableRange defines an interface with DeleteRange method. +// We define this interface only for lessor to limit the number +// of methods of storage.KV to what lessor actually needs. +// +// Having a minimum interface makes testing easy. +type DeleteableRange interface { + DeleteRange(key, end []byte) (int64, int64) +} + // a lessor is the owner of leases. It can grant, revoke, // renew and modify leases for lessee. // TODO: persist lease on to stable backend for failure recovery. @@ -40,12 +49,18 @@ type lessor struct { // FindExpired and Renew should be the most frequent operations. leaseMap map[uint64]*lease + // A DeleteableRange the lessor operates on. + // When a lease expires, the lessor will delete the + // leased range (or key) from the DeleteableRange. + dr DeleteableRange + idgen *idutil.Generator } -func NewLessor(lessorID uint8) *lessor { +func NewLessor(lessorID uint8, dr DeleteableRange) *lessor { return &lessor{ leaseMap: make(map[uint64]*lease), + dr: dr, idgen: idutil.NewGenerator(lessorID, time.Now()), } } @@ -62,7 +77,7 @@ func (le *lessor) Grant(expiry time.Time) *lease { le.mu.Lock() defer le.mu.Unlock() - l := &lease{id: id, expiry: expiry} + l := &lease{id: id, expiry: expiry, itemSet: make(map[leaseItem]struct{})} if _, ok := le.leaseMap[id]; ok { panic("lease: unexpected duplicate ID!") } @@ -85,7 +100,10 @@ func (le *lessor) Revoke(id uint64) error { delete(le.leaseMap, l.id) - // TODO: remove attached items + for item := range l.itemSet { + le.dr.DeleteRange([]byte(item.key), []byte(item.endRange)) + } + return nil } diff --git a/lease/lessor_test.go b/lease/lessor_test.go index 09e1712c6..548c57c79 100644 --- a/lease/lessor_test.go +++ b/lease/lessor_test.go @@ -24,7 +24,7 @@ import ( // The granted lease should have a unique ID with a term // that is greater than minLeaseTerm. func TestLessorGrant(t *testing.T) { - le := NewLessor(1) + le := NewLessor(1, &fakeDeleteable{}) l := le.Grant(time.Now().Add(time.Second)) gl := le.get(l.id) @@ -43,15 +43,28 @@ func TestLessorGrant(t *testing.T) { } // TestLessorRevoke ensures Lessor can revoke a lease. +// The items in the revoked lease should be removed from +// the DeleteableKV. // The revoked lease cannot be got from Lessor again. func TestLessorRevoke(t *testing.T) { - le := NewLessor(1) + fd := &fakeDeleteable{} + le := NewLessor(1, fd) // grant a lease with long term (100 seconds) to // avoid early termination during the test. l := le.Grant(time.Now().Add(100 * time.Second)) - err := le.Revoke(l.id) + items := []leaseItem{ + {"foo", ""}, + {"bar", "zar"}, + } + + err := le.Attach(l.id, items) + if err != nil { + t.Fatalf("failed to attach items to the lease: %v", err) + } + + err = le.Revoke(l.id) if err != nil { t.Fatal("failed to revoke lease:", err) } @@ -59,11 +72,16 @@ func TestLessorRevoke(t *testing.T) { if le.get(l.id) != nil { t.Errorf("got revoked lease %x", l.id) } + + wdeleted := []string{"foo_", "bar_zar"} + if !reflect.DeepEqual(fd.deleted, wdeleted) { + t.Errorf("deleted= %v, want %v", fd.deleted, wdeleted) + } } // TestLessorRenew ensures Lessor can renew an existing lease. func TestLessorRenew(t *testing.T) { - le := NewLessor(1) + le := NewLessor(1, &fakeDeleteable{}) l := le.Grant(time.Now().Add(5 * time.Second)) le.Renew(l.id, time.Now().Add(100*time.Second)) @@ -73,3 +91,12 @@ func TestLessorRenew(t *testing.T) { t.Errorf("failed to renew the lease for 100 seconds") } } + +type fakeDeleteable struct { + deleted []string +} + +func (fd *fakeDeleteable) DeleteRange(key, end []byte) (int64, int64) { + fd.deleted = append(fd.deleted, string(key)+"_"+string(end)) + return 0, 0 +}