Skip to content

Commit effaa79

Browse files
authored
Merge pull request #20387 from ahrtr/20250722_learner_3.6
[release-3.6] Check promotion against v2store when updating v2store
2 parents 1ed440d + e5f4311 commit effaa79

File tree

2 files changed

+66
-2
lines changed

2 files changed

+66
-2
lines changed

server/etcdserver/api/membership/cluster.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -522,8 +522,9 @@ func (c *RaftCluster) PromoteMember(id types.ID, shouldApplyV3 ShouldApplyV3) {
522522
defer c.Unlock()
523523

524524
if c.v2store != nil {
525-
if _, ok := c.members[id]; ok {
526-
m := *(c.members[id])
525+
membersMap, _ := membersFromStore(c.lg, c.v2store)
526+
if _, ok := membersMap[id]; ok {
527+
m := *(membersMap[id])
527528
m.RaftAttributes.IsLearner = false
528529
mustUpdateMemberInStore(c.lg, c.v2store, &m)
529530
} else {

tests/e2e/member_no_proxy_test.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Copyright 2025 The etcd Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
//go:build !cluster_proxy
16+
17+
package e2e
18+
19+
import (
20+
"context"
21+
"strings"
22+
"testing"
23+
"time"
24+
25+
"github.com/stretchr/testify/require"
26+
27+
"go.etcd.io/etcd/tests/v3/framework/e2e"
28+
"go.etcd.io/etcd/tests/v3/framework/testutils"
29+
)
30+
31+
// TestReproduce20340 reproduces the issue https://github.com/etcd-io/etcd/issues/20340.
32+
// Refer to https://github.com/etcd-io/etcd/issues/20340#issuecomment-3105037914.
33+
func TestReproduce20340(t *testing.T) {
34+
e2e.BeforeTest(t)
35+
36+
ctx := context.Background()
37+
38+
epc, members := mustCreateNewClusterByPromotingMembers(t, e2e.CurrentVersion, 3)
39+
defer func() {
40+
require.NoError(t, epc.Close())
41+
}()
42+
43+
t.Logf("Removing the second member (%x)", members[0].ID)
44+
etcdctl := epc.Procs[0].Etcdctl()
45+
testutils.ExecuteWithTimeout(t, 10*time.Second, func() {
46+
for {
47+
_, merr := etcdctl.MemberRemove(ctx, members[0].ID)
48+
if merr != nil {
49+
if strings.Contains(merr.Error(), "etcdserver: unhealthy cluster") {
50+
time.Sleep(1 * time.Second)
51+
continue
52+
}
53+
t.Fatalf("failed to remove member: %s", merr.Error())
54+
}
55+
break
56+
}
57+
})
58+
59+
epc.Procs = append(epc.Procs[:1], epc.Procs[2:]...)
60+
61+
t.Logf("Restarting member: %s", epc.Procs[0].Config().Name)
62+
require.NoError(t, epc.Procs[0].Restart(ctx))
63+
}

0 commit comments

Comments
 (0)