Skip to content

Commit f896a17

Browse files
authored
Merge pull request #20590 from ahrtr/20250901_lease_renew
Re-check that the lease still exists during the renew process
2 parents 636bbdd + 943f4d2 commit f896a17

File tree

3 files changed

+68
-2
lines changed

3 files changed

+68
-2
lines changed

server/lease/lessor.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,8 @@ func (le *lessor) Renew(id LeaseID) (int64, error) {
428428
}
429429
}
430430

431+
// gofail: var beforeCheckpointInLeaseRenew struct{}
432+
431433
// Clear remaining TTL when we renew if it is set
432434
// By applying a RAFT entry only when the remainingTTL is already set, we limit the number
433435
// of RAFT entries written per lease to a max of 2 per checkpoint interval.
@@ -438,6 +440,12 @@ func (le *lessor) Renew(id LeaseID) (int64, error) {
438440
}
439441

440442
le.mu.Lock()
443+
// Re-check in case the lease was revoked immediately after the previous check
444+
l = le.leaseMap[id]
445+
if l == nil {
446+
le.mu.Unlock()
447+
return -1, ErrLeaseNotFound
448+
}
441449
l.refresh(0)
442450
item := &LeaseWithTime{id: l.ID, time: l.expiry}
443451
le.leaseExpiredNotifier.RegisterOrUpdate(item)

tests/e2e/v3_lease_no_proxy_test.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"github.com/stretchr/testify/assert"
2525
"github.com/stretchr/testify/require"
2626

27+
"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
2728
clientv3 "go.etcd.io/etcd/client/v3"
2829
"go.etcd.io/etcd/tests/v3/framework/e2e"
2930
"go.etcd.io/etcd/tests/v3/framework/testutils"
@@ -164,3 +165,60 @@ func testLeaseRevokeIssue(t *testing.T, clusterSize int, connectToOneFollower bo
164165
close(stopC)
165166
<-doneC
166167
}
168+
169+
func TestLeaseRevokeDuringRenew(t *testing.T) {
170+
e2e.BeforeTest(t)
171+
172+
ctx := t.Context()
173+
174+
t.Log("Starting a new etcd cluster")
175+
epc, err := e2e.NewEtcdProcessCluster(ctx, t,
176+
e2e.WithClusterSize(1),
177+
e2e.WithGoFailEnabled(true),
178+
e2e.WithGoFailClientTimeout(40*time.Second),
179+
)
180+
require.NoError(t, err)
181+
defer func() {
182+
require.NoErrorf(t, epc.Close(), "error closing etcd processes")
183+
}()
184+
185+
eps := epc.Procs[0].EndpointsGRPC()
186+
t.Logf("Creating two clients for lease operations: %v", eps)
187+
clientForRenew, err := clientv3.New(clientv3.Config{Endpoints: eps, DialTimeout: 3 * time.Second})
188+
require.NoError(t, err)
189+
defer clientForRenew.Close()
190+
191+
clientForRevoke, err := clientv3.New(clientv3.Config{Endpoints: eps, DialTimeout: 3 * time.Second})
192+
require.NoError(t, err)
193+
defer clientForRevoke.Close()
194+
195+
t.Log("Creating a new lease")
196+
leaseRsp, err := clientForRenew.Grant(ctx, 60)
197+
require.NoError(t, err)
198+
199+
t.Log("Activate the 'beforeCheckpointInLeaseRenew' failpoint")
200+
require.NoError(t, epc.Procs[0].Failpoints().SetupHTTP(ctx, "beforeCheckpointInLeaseRenew", `sleep("3s")`))
201+
202+
t.Logf("Starting a goroutine to keep alive the lease: %d", leaseRsp.ID)
203+
doneC := make(chan struct{})
204+
startC := make(chan struct{}, 1)
205+
var renewError error
206+
go func() {
207+
defer close(doneC)
208+
startC <- struct{}{}
209+
_, renewError = clientForRenew.KeepAliveOnce(ctx, leaseRsp.ID)
210+
}()
211+
212+
t.Log("Wait for the KeepAliveOnce goroutine to get started")
213+
<-startC
214+
time.Sleep(200 * time.Millisecond)
215+
216+
t.Logf("Revoke the lease: %d", leaseRsp.ID)
217+
_, lerr := clientForRevoke.Revoke(ctx, leaseRsp.ID)
218+
require.NoError(t, lerr)
219+
220+
t.Log("Waiting for the keepAlive goroutine to exit")
221+
<-doneC
222+
223+
require.ErrorIs(t, rpctypes.ErrLeaseNotFound, renewError)
224+
}

tests/robustness/Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,15 +86,15 @@ install-gofail: $(GOPATH)/bin/gofail
8686

8787
.PHONY: gofail-enable
8888
gofail-enable: $(GOPATH)/bin/gofail
89-
$(GOPATH)/bin/gofail enable server/etcdserver/ server/lease/leasehttp server/storage/backend/ server/storage/mvcc/ server/storage/wal/ server/etcdserver/api/v3rpc/ server/etcdserver/api/membership/ server/etcdserver/api/rafthttp/
89+
$(GOPATH)/bin/gofail enable server/etcdserver/ server/lease server/lease/leasehttp server/storage/backend/ server/storage/mvcc/ server/storage/wal/ server/etcdserver/api/v3rpc/ server/etcdserver/api/membership/ server/etcdserver/api/rafthttp/
9090
cd $(REPOSITORY_ROOT)/server && go get go.etcd.io/gofail@${GOFAIL_VERSION}
9191
cd $(REPOSITORY_ROOT)/etcdutl && go get go.etcd.io/gofail@${GOFAIL_VERSION}
9292
cd $(REPOSITORY_ROOT)/etcdctl && go get go.etcd.io/gofail@${GOFAIL_VERSION}
9393
cd $(REPOSITORY_ROOT)/tests && go get go.etcd.io/gofail@${GOFAIL_VERSION}
9494

9595
.PHONY: gofail-disable
9696
gofail-disable: $(GOPATH)/bin/gofail
97-
$(GOPATH)/bin/gofail disable server/etcdserver/ server/lease/leasehttp server/storage/backend/ server/storage/mvcc/ server/storage/wal/ server/etcdserver/api/v3rpc/ server/etcdserver/api/membership/ server/etcdserver/api/rafthttp/
97+
$(GOPATH)/bin/gofail disable server/etcdserver/ server/lease server/lease/leasehttp server/storage/backend/ server/storage/mvcc/ server/storage/wal/ server/etcdserver/api/v3rpc/ server/etcdserver/api/membership/ server/etcdserver/api/rafthttp/
9898
cd $(REPOSITORY_ROOT)/server && go mod tidy
9999
cd $(REPOSITORY_ROOT)/etcdutl && go mod tidy
100100
cd $(REPOSITORY_ROOT)/etcdctl && go mod tidy

0 commit comments

Comments
 (0)