Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[POC] store v2 deprecation #16000

Draft
wants to merge 48 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
e5ba13e
Update ValidateConfigurationChange to nil check for v2store
May 16, 2023
71c0a65
v2d store RaftCluster membership info in v2 during snapshot
May 16, 2023
b57ceb0
Merge branch 'etcd-io:main' into main
geetasg May 16, 2023
a749ef4
updating based on test failure
May 16, 2023
9f66d43
updating to address test failure
May 17, 2023
39a7837
refactor v2 deprecation change for snapshot
May 17, 2023
6798d77
persist "attributes" of member to new v2store
May 17, 2023
11fa7d3
Adding unit test
May 18, 2023
f55890c
update v2 deprecation test
May 17, 2023
e467de9
update test
May 18, 2023
7a8bb94
Adding test for recovering old version from v2 snapshot taken by new …
May 23, 2023
0b2fd5c
Merge branch 'etcd-io:main' into main
geetasg May 23, 2023
f41ed2e
Add downgrade test that creates snapshots and keys
May 23, 2023
adac92b
Add verification for consistent index
May 26, 2023
91359ba
start refactoring recovery logic
May 27, 2023
fa9b70b
update conf change validation
May 29, 2023
543d305
Stop v2 recovery during applySnapshot and bootstrap
May 29, 2023
8321280
Deactivate v2store recovery for non-test only
May 29, 2023
b44cb38
Adding comment to check back on v2discovery support
May 30, 2023
d8d0f62
Remove SetStore from etcdutl snapshot restore path
May 30, 2023
dc2744f
Update RaftCluster tests
May 30, 2023
0ce6603
Refactor to create a function to return membership in v2 format
May 30, 2023
1976267
Update backup command logic for saving membership to v2store
May 31, 2023
0b4c9c6
Revert "Update backup command logic for saving membership to v2store"
May 31, 2023
22855a9
Merge branch 'etcd-io:main' into main
geetasg May 31, 2023
8373547
Update server tests
May 31, 2023
ea7d7f0
update server unit tests
May 31, 2023
7dce310
Update consumers of RaftCluster SetStore
May 31, 2023
7a1ae73
Update backup command to not use SetStore
May 31, 2023
1bcd8c6
Stop recovering membership info from v2store data
May 31, 2023
f7b83bd
Disable v2 specific test. Revisit to rewrite for v3
May 31, 2023
338f747
Allow v2deprecation mode of write-only
May 31, 2023
6e51502
Remove SetStore method on RaftCluster
Jun 1, 2023
9605636
Update RemoveMember func to only use be
Jun 1, 2023
d9f149c
Update function signature for recoverSnapshot
Jun 2, 2023
8dcc34d
Remove v2store from RaftCluster
Jun 2, 2023
2db3039
Update function signature of bootstrapBackend
Jun 2, 2023
930ecd2
Remove v2store from bootstrap
Jun 2, 2023
9b0dd97
Use new v2store for etcdserver. Bootstrap does not return a store
Jun 2, 2023
448f9ac
Stop using v2store and v2applier from prod path
Jun 2, 2023
9c02d76
Try out removing v2 applier
Jun 2, 2023
733d109
Use v2 to v3 applier only to handle publish from 3.5
Jun 12, 2023
1bd5ae6
Fix comment
Jun 12, 2023
21a1e44
Fix comment
Jun 12, 2023
a301201
Fix goimport
Jun 12, 2023
9515be1
Add unit test for updateVersionV3
Jun 12, 2023
d2323b7
Update TestApplyRequestOnAdminMemberAttributes to use v2v3 applier
Jun 13, 2023
0543c86
Merge branch 'etcd-io:main' into poc1
geetasg Jun 13, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 5 additions & 14 deletions etcdutl/etcdutl/backup_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package etcdutl

import (
"fmt"
"os"
"path"
"regexp"
Expand All @@ -28,9 +29,9 @@ import (
"go.etcd.io/etcd/client/pkg/v3/types"
"go.etcd.io/etcd/pkg/v3/idutil"
"go.etcd.io/etcd/pkg/v3/pbutil"
"go.etcd.io/etcd/server/v3/etcdserver"
"go.etcd.io/etcd/server/v3/etcdserver/api/membership"
"go.etcd.io/etcd/server/v3/etcdserver/api/snap"
"go.etcd.io/etcd/server/v3/etcdserver/api/v2store"
"go.etcd.io/etcd/server/v3/storage/backend"
"go.etcd.io/etcd/server/v3/storage/datadir"
"go.etcd.io/etcd/server/v3/storage/schema"
Expand Down Expand Up @@ -178,21 +179,11 @@ func saveSnap(lg *zap.Logger, destSnap, srcSnap string, desired *desiredCluster)
// mustTranslateV2store processes storeData such that they match 'desiredCluster'.
// In particular the method overrides membership information.
func mustTranslateV2store(lg *zap.Logger, storeData []byte, desired *desiredCluster) []byte {
st := v2store.New()
if err := st.Recovery(storeData); err != nil {
lg.Panic("cannot translate v2store", zap.Error(err))
}

raftCluster := membership.NewClusterFromMembers(lg, desired.clusterId, desired.members)
raftCluster.SetID(desired.nodeId, desired.clusterId)
raftCluster.SetStore(st)
raftCluster.PushMembershipToStorage()

outputData, err := st.Save()
if err != nil {
lg.Panic("cannot save v2store", zap.Error(err))
}
return outputData
d := etcdserver.GetMembershipInfoInV2Format(lg, raftCluster)
fmt.Printf("storeData = %v d = %v\n", storeData, d)
return d
}

func translateWAL(lg *zap.Logger, srcWAL string, walsnap walpb.Snapshot) (etcdserverpb.Metadata, raftpb.HardState, []raftpb.Entry) {
Expand Down
9 changes: 1 addition & 8 deletions etcdutl/snapshot/v3_snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ import (
"go.etcd.io/etcd/server/v3/etcdserver"
"go.etcd.io/etcd/server/v3/etcdserver/api/membership"
"go.etcd.io/etcd/server/v3/etcdserver/api/snap"
"go.etcd.io/etcd/server/v3/etcdserver/api/v2store"
"go.etcd.io/etcd/server/v3/etcdserver/cindex"
"go.etcd.io/etcd/server/v3/storage/backend"
"go.etcd.io/etcd/server/v3/storage/schema"
Expand Down Expand Up @@ -396,8 +395,6 @@ func (s *v3Manager) saveWALAndSnap() (*raftpb.HardState, error) {
}

// add members again to persist them to the store we create.
st := v2store.New(etcdserver.StoreClusterPrefix, etcdserver.StoreKeysPrefix)
s.cl.SetStore(st)
be := backend.NewDefaultBackend(s.lg, s.outDbPath())
defer be.Close()
s.cl.SetBackend(schema.NewMembershipBackend(s.lg, be))
Expand Down Expand Up @@ -457,15 +454,11 @@ func (s *v3Manager) saveWALAndSnap() (*raftpb.HardState, error) {
return nil, err
}

b, berr := st.Save()
if berr != nil {
return nil, berr
}
confState := raftpb.ConfState{
Voters: nodeIDs,
}
raftSnap := raftpb.Snapshot{
Data: b,
Data: etcdserver.GetMembershipInfoInV2Format(s.lg, s.cl),
Metadata: raftpb.SnapshotMetadata{
Index: commit,
Term: term,
Expand Down
4 changes: 2 additions & 2 deletions server/config/v2_deprecation.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ const (
V2_DEPR_1_WRITE_ONLY = V2DeprecationEnum("write-only")
// V2store is WIPED if found !!!
V2_DEPR_1_WRITE_ONLY_DROP = V2DeprecationEnum("write-only-drop-data")
// V2store is neither written nor read. Usage of this configuration is blocking
// ability to rollback to etcd v3.5.
// V2store is neither written nor read for 3.6. v2snapshot is published only for backward compatibility
V2_DEPR_2_GONE = V2DeprecationEnum("gone")

//TODO geetasg does thie default need to change
V2_DEPR_DEFAULT = V2_DEPR_1_WRITE_ONLY
)

Expand Down
99 changes: 50 additions & 49 deletions server/etcdserver/api/membership/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,7 @@ type RaftCluster struct {
localID types.ID
cid types.ID

v2store v2store.Store
be MembershipBackend
be MembershipBackend

sync.Mutex // guards the fields below
version *semver.Version
Expand Down Expand Up @@ -245,8 +244,6 @@ func (c *RaftCluster) SetID(localID, cid types.ID) {
c.buildMembershipMetric()
}

func (c *RaftCluster) SetStore(st v2store.Store) { c.v2store = st }

func (c *RaftCluster) SetBackend(be MembershipBackend) {
c.be = be
c.be.MustCreateBackendBuckets()
Expand All @@ -260,13 +257,8 @@ func (c *RaftCluster) Recover(onSet func(*zap.Logger, *semver.Version)) {
c.Lock()
defer c.Unlock()

if c.be != nil {
c.version = c.be.ClusterVersionFromBackend()
c.members, c.removed = c.be.MustReadMembersFromBackend()
} else {
c.version = clusterVersionFromStore(c.lg, c.v2store)
c.members, c.removed = membersFromStore(c.lg, c.v2store)
}
c.version = c.be.ClusterVersionFromBackend()
c.members, c.removed = c.be.MustReadMembersFromBackend()
c.buildMembershipMetric()

if c.be != nil {
Expand Down Expand Up @@ -303,9 +295,16 @@ func (c *RaftCluster) Recover(onSet func(*zap.Logger, *semver.Version)) {

// ValidateConfigurationChange takes a proposed ConfChange and
// ensures that it is still valid.
func (c *RaftCluster) ValidateConfigurationChange(cc raftpb.ConfChange) error {
// TODO: this must be switched to backend as well.
membersMap, removedMap := membersFromStore(c.lg, c.v2store)
func (c *RaftCluster) ValidateConfigurationChange(cc raftpb.ConfChange, shouldApplyV3 ShouldApplyV3) error {

var membersMap map[types.ID]*Member
var removedMap map[types.ID]bool
membersMap, removedMap = c.be.MustReadMembersFromBackend()
if !shouldApplyV3 {
//TODO geetasg - RaftCluster purely based on backend cannot validate an entry older than ci
return nil
}

id := types.ID(cc.NodeID)
if removedMap[id] {
return ErrIDRemoved
Expand Down Expand Up @@ -390,10 +389,7 @@ func (c *RaftCluster) ValidateConfigurationChange(cc raftpb.ConfChange) error {
func (c *RaftCluster) AddMember(m *Member, shouldApplyV3 ShouldApplyV3) {
c.Lock()
defer c.Unlock()
if c.v2store != nil {
mustSaveMemberToStore(c.lg, c.v2store, m)
}
if c.be != nil && shouldApplyV3 {
if shouldApplyV3 {
c.be.MustSaveMemberToBackend(m)
}

Expand All @@ -415,10 +411,7 @@ func (c *RaftCluster) AddMember(m *Member, shouldApplyV3 ShouldApplyV3) {
func (c *RaftCluster) RemoveMember(id types.ID, shouldApplyV3 ShouldApplyV3) {
c.Lock()
defer c.Unlock()
if c.v2store != nil {
mustDeleteMemberFromStore(c.lg, c.v2store, id)
}
if c.be != nil && shouldApplyV3 {
if shouldApplyV3 {
c.be.MustDeleteMemberFromBackend(id)
}

Expand Down Expand Up @@ -452,10 +445,7 @@ func (c *RaftCluster) UpdateAttributes(id types.ID, attr Attributes, shouldApply

if m, ok := c.members[id]; ok {
m.Attributes = attr
if c.v2store != nil {
mustUpdateMemberAttrInStore(c.lg, c.v2store, m)
}
if c.be != nil && shouldApplyV3 {
if shouldApplyV3 {
c.be.MustSaveMemberToBackend(m)
}
return
Expand Down Expand Up @@ -486,10 +476,7 @@ func (c *RaftCluster) PromoteMember(id types.ID, shouldApplyV3 ShouldApplyV3) {

c.members[id].RaftAttributes.IsLearner = false
c.updateMembershipMetric(id, true)
if c.v2store != nil {
mustUpdateMemberInStore(c.lg, c.v2store, c.members[id])
}
if c.be != nil && shouldApplyV3 {
if shouldApplyV3 {
c.be.MustSaveMemberToBackend(c.members[id])
}

Expand All @@ -505,10 +492,7 @@ func (c *RaftCluster) UpdateRaftAttributes(id types.ID, raftAttr RaftAttributes,
defer c.Unlock()

c.members[id].RaftAttributes = raftAttr
if c.v2store != nil {
mustUpdateMemberInStore(c.lg, c.v2store, c.members[id])
}
if c.be != nil && shouldApplyV3 {
if shouldApplyV3 {
c.be.MustSaveMemberToBackend(c.members[id])
}

Expand Down Expand Up @@ -554,10 +538,7 @@ func (c *RaftCluster) SetVersion(ver *semver.Version, onSet func(*zap.Logger, *s
c.version = ver
sv := semver.Must(semver.NewVersion(version.Version))
serverversion.MustDetectDowngrade(c.lg, sv, c.version)
if c.v2store != nil {
mustSaveClusterVersionToStore(c.lg, c.v2store, ver)
}
if c.be != nil && shouldApplyV3 {
if shouldApplyV3 {
c.be.MustSaveClusterVersionToBackend(ver)
}
if oldVer != nil {
Expand Down Expand Up @@ -791,17 +772,9 @@ func (c *RaftCluster) VotingMemberIDs() []types.ID {
// PushMembershipToStorage is overriding storage information about cluster's
// members, such that they fully reflect internal RaftCluster's storage.
func (c *RaftCluster) PushMembershipToStorage() {
if c.be != nil {
c.be.TrimMembershipFromBackend()
for _, m := range c.members {
c.be.MustSaveMemberToBackend(m)
}
}
if c.v2store != nil {
TrimMembershipFromV2Store(c.lg, c.v2store)
for _, m := range c.members {
mustSaveMemberToStore(c.lg, c.v2store, m)
}
c.be.TrimMembershipFromBackend()
for _, m := range c.members {
c.be.MustSaveMemberToBackend(m)
}
}

Expand Down Expand Up @@ -854,3 +827,31 @@ func ValidateMaxLearnerConfig(maxLearners int, members []*Member, scaleUpLearner

return nil
}

func (c *RaftCluster) Store(store v2store.Store) {
c.Lock()
defer c.Unlock()
for _, m := range c.members {
mustSaveMemberToStore(c.lg, store, m)
if m.ClientURLs != nil {
mustUpdateMemberAttrInStore(c.lg, store, m)
}
c.lg.Info(
"snapshot storing member",
zap.String("id", m.ID.String()),
zap.Strings("peer-urls", m.PeerURLs),
zap.Bool("is-learner", m.IsLearner),
)
}
for id, _ := range c.removed {
mustDeleteMemberFromStore(c.lg, store, id)
}
if c.version != nil {
mustSaveClusterVersionToStore(c.lg, store, c.version)
}
}

func (c *RaftCluster) RecoverMembersFromStore(st v2store.Store) {
c.members, c.removed = membersFromStore(c.lg, st)
c.buildMembershipMetric()
}
62 changes: 55 additions & 7 deletions server/etcdserver/api/membership/cluster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,9 @@ func TestClusterValidateAndAssignIDs(t *testing.T) {

func TestClusterValidateConfigurationChange(t *testing.T) {
cl := NewCluster(zaptest.NewLogger(t), WithMaxLearners(1))
cl.SetStore(v2store.New())
be := newMembershipBackend()
cl.SetBackend(be)

for i := 1; i <= 4; i++ {
var isLearner bool
if i == 1 {
Expand Down Expand Up @@ -454,7 +456,7 @@ func TestClusterValidateConfigurationChange(t *testing.T) {
},
}
for i, tt := range tests {
err := cl.ValidateConfigurationChange(tt.cc)
err := cl.ValidateConfigurationChange(tt.cc, ApplyBoth)
if err != tt.werr {
t.Errorf("#%d: validateConfigurationChange error = %v, want %v", i, err, tt.werr)
}
Expand All @@ -473,7 +475,6 @@ func TestClusterGenID(t *testing.T) {
}
previd := cs.ID()

cs.SetStore(mockstore.NewNop())
cs.AddMember(newTestMember(3, nil, "", nil), true)
cs.genID()
if cs.ID() == previd {
Expand Down Expand Up @@ -516,8 +517,8 @@ func TestNodeToMemberBad(t *testing.T) {
func TestClusterAddMember(t *testing.T) {
st := mockstore.NewRecorder()
c := newTestCluster(t, nil)
c.SetStore(st)
c.AddMember(newTestMember(1, nil, "node1", nil), true)
c.Store(st)

wactions := []testutil.Action{
{
Expand All @@ -539,8 +540,8 @@ func TestClusterAddMember(t *testing.T) {
func TestClusterAddMemberAsLearner(t *testing.T) {
st := mockstore.NewRecorder()
c := newTestCluster(t, nil)
c.SetStore(st)
c.AddMember(newTestMemberAsLearner(1, nil, "node1", nil), true)
c.Store(st)

wactions := []testutil.Action{
{
Expand Down Expand Up @@ -582,8 +583,8 @@ func TestClusterMembers(t *testing.T) {
func TestClusterRemoveMember(t *testing.T) {
st := mockstore.NewRecorder()
c := newTestCluster(t, nil)
c.SetStore(st)
c.RemoveMember(1, true)
c.Store(st)

wactions := []testutil.Action{
{Name: "Delete", Params: []interface{}{MemberStoreKey(1), true, true}},
Expand Down Expand Up @@ -647,8 +648,10 @@ func TestNodeToMember(t *testing.T) {

func newTestCluster(t testing.TB, membs []*Member) *RaftCluster {
c := &RaftCluster{lg: zaptest.NewLogger(t), members: make(map[types.ID]*Member), removed: make(map[types.ID]bool)}
be := newMembershipBackend()
c.SetBackend(be)
for _, m := range membs {
c.members[m.ID] = m
c.AddMember(m, true)
}
return c
}
Expand Down Expand Up @@ -975,3 +978,48 @@ func TestIsReadyToPromoteMember(t *testing.T) {
}
}
}

/*
covered by TestV2DeprecationSnapshotMatches
// TestMembershipStore tests code path used by snapshot
func TestMembershipStore(t *testing.T) {
name := "etcd"
clientURLs := []string{"http://127.0.0.1:4001"}
tests := []struct {
mems []*Member
removed map[types.ID]bool
}{
// update attributes of existing member
{
[]*Member{
newTestMember(2, nil, name, clientURLs),
},
map[types.ID]bool{types.ID(1): true},
},
}
for i, tt := range tests {
c := newTestCluster(t, tt.mems)
for id, _ := range tt.removed {
c.RemoveMember(id, true)
}

//snapshot
st := v2store.New("/0", "/1")
c.Store(st)
d, _ := st.SaveNoCopy()

//3.5 recover from snapshot
rst := v2store.New("/0", "/1")
rst.Recovery(d)
rc := &RaftCluster{lg: zaptest.NewLogger(t), members: make(map[types.ID]*Member), removed: make(map[types.ID]bool)}
rc.SetBackend(c.be)
//rc.SetStore(rst)
rc.Recover(func(lg *zap.Logger, v *semver.Version) { return })

//membership should match
if g := rc.Members(); !reflect.DeepEqual(g, tt.mems) {
t.Errorf("#%d: members = %+v, want %+v", i, g, tt.mems)
}
}
}
*/
Loading