v2 etcdctl backup: producing consistent state of membership

This commit is contained in:
Piotr Tabor
2021-04-02 21:08:03 +02:00
parent a70386a1a4
commit 067521981e
5 changed files with 167 additions and 63 deletions

View File

@@ -859,3 +859,20 @@ func (c *RaftCluster) VotingMemberIDs() []types.ID {
sort.Sort(types.IDSlice(ids))
return ids
}
// 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 {
TrimMembershipFromBackend(c.lg, c.be)
for _, m := range c.members {
mustSaveMemberToBackend(c.lg, c.be, m)
}
}
if c.v2store != nil {
TrimMembershipFromV2Store(c.lg, c.v2store)
for _, m := range c.members {
mustSaveMemberToStore(c.lg, c.v2store, m)
}
}
}

View File

@@ -20,6 +20,7 @@ import (
"fmt"
"math/rand"
"sort"
"strings"
"time"
"go.etcd.io/etcd/client/pkg/v3/types"
@@ -61,12 +62,10 @@ func NewMemberAsLearner(name string, peerURLs types.URLs, clusterName string, no
}
func computeMemberId(peerURLs types.URLs, clusterName string, now *time.Time) types.ID {
var b []byte
peerURLstrs := peerURLs.StringSlice()
sort.Strings(peerURLstrs)
for _, p := range peerURLstrs {
b = append(b, []byte(p)...)
}
joinedPeerUrls := strings.Join(peerURLstrs, "")
b := []byte(joinedPeerUrls)
b = append(b, []byte(clusterName)...)
if now != nil {

View File

@@ -57,6 +57,8 @@ func mustSaveMemberToBackend(lg *zap.Logger, be backend.Backend, m *Member) {
tx.UnsafePut(membersBucketName, mkey, mvalue)
}
// TrimClusterFromBackend removes all information about cluster (versions)
// from the v3 backend.
func TrimClusterFromBackend(be backend.Backend) error {
tx := be.BatchTx()
tx.Lock()
@@ -83,7 +85,7 @@ func readMembersFromBackend(lg *zap.Logger, be backend.Backend) (map[types.ID]*M
tx.RLock()
defer tx.RUnlock()
err := tx.UnsafeForEach(membersBucketName, func(k, v []byte) error {
memberId := MustParseMemberIDFromBytes(lg, k)
memberId := mustParseMemberIDFromBytes(lg, k)
m := &Member{ID: memberId}
if err := json.Unmarshal(v, &m); err != nil {
return err
@@ -96,7 +98,7 @@ func readMembersFromBackend(lg *zap.Logger, be backend.Backend) (map[types.ID]*M
}
err = tx.UnsafeForEach(membersRemovedBucketName, func(k, v []byte) error {
memberId := MustParseMemberIDFromBytes(lg, k)
memberId := mustParseMemberIDFromBytes(lg, k)
removed[memberId] = true
return nil
})
@@ -114,24 +116,48 @@ func mustReadMembersFromBackend(lg *zap.Logger, be backend.Backend) (map[types.I
return members, removed
}
// TrimMembershipFromBackend removes all information about members &
// removed_members from the v3 backend.
func TrimMembershipFromBackend(lg *zap.Logger, be backend.Backend) error {
lg.Info("Trimming membership information from the backend...")
tx := be.BatchTx()
tx.Lock()
defer tx.Unlock()
err := tx.UnsafeForEach(membersBucketName, func(k, v []byte) error {
tx.UnsafeDelete(membersBucketName, k)
lg.Debug("Removed member from the backend",
zap.Stringer("member", mustParseMemberIDFromBytes(lg, k)))
return nil
})
if err != nil {
return err
}
err = tx.UnsafeForEach(membersRemovedBucketName, func(k, v []byte) error {
return tx.UnsafeForEach(membersRemovedBucketName, func(k, v []byte) error {
tx.UnsafeDelete(membersRemovedBucketName, k)
lg.Debug("Removed removed_member from the backend",
zap.Stringer("member", mustParseMemberIDFromBytes(lg, k)))
return nil
})
if err != nil {
return err
}
// TrimMembershipFromV2Store removes all information about members &
// removed_members from the v2 store.
func TrimMembershipFromV2Store(lg *zap.Logger, s v2store.Store) error {
members, removed := membersFromStore(lg, s)
for mID := range members {
_, err := s.Delete(MemberStoreKey(mID), true, true)
if err != nil {
return err
}
}
for mID := range removed {
_, err := s.Delete(RemovedMemberStoreKey(mID), true, true)
if err != nil {
return err
}
}
return nil
}
@@ -289,7 +315,7 @@ func MemberAttributesStorePath(id types.ID) string {
return path.Join(MemberStoreKey(id), attributesSuffix)
}
func MustParseMemberIDFromBytes(lg *zap.Logger, key []byte) types.ID {
func mustParseMemberIDFromBytes(lg *zap.Logger, key []byte) types.ID {
id, err := types.IDFromString(string(key))
if err != nil {
lg.Panic("failed to parse member id from key", zap.Error(err))