From 865df75714df9a9cf2c27b99a69fb90033483084 Mon Sep 17 00:00:00 2001 From: Piotr Tabor Date: Thu, 13 May 2021 12:37:43 +0200 Subject: [PATCH] Save raftpb.ConfState in the backend. This makes (bbolt) backend a full feature snapshot in term of WAL/raft, i.e. carries: - commit : (applied_index) - confState Benefits: - Backend will be a sufficient point in time definition sufficient to start replaying WAL. We have applied_index & confState in consistent state. - In case of emergency a backend state can be used for recovery --- server/etcdserver/api/membership/cluster.go | 2 + server/etcdserver/api/membership/confstate.go | 63 ++++++++ .../api/membership/confstate_test.go | 79 ++++++++++ server/etcdserver/api/membership/store.go | 2 + server/etcdserver/server.go | 24 ++- server/etcdserver/server_test.go | 149 ++++++++++++------ server/mvcc/kvstore.go | 14 +- server/mvcc/kvstore_compaction.go | 2 +- server/mvcc/kvstore_compaction_test.go | 2 +- server/mvcc/kvstore_test.go | 10 +- 10 files changed, 283 insertions(+), 64 deletions(-) create mode 100644 server/etcdserver/api/membership/confstate.go create mode 100644 server/etcdserver/api/membership/confstate_test.go diff --git a/server/etcdserver/api/membership/cluster.go b/server/etcdserver/api/membership/cluster.go index b570bc699..d42612a69 100644 --- a/server/etcdserver/api/membership/cluster.go +++ b/server/etcdserver/api/membership/cluster.go @@ -694,6 +694,7 @@ func clusterVersionFromStore(lg *zap.Logger, st v2store.Store) *semver.Version { return semver.Must(semver.NewVersion(*e.Node.Value)) } +// The field is populated since etcd v3.5. func clusterVersionFromBackend(lg *zap.Logger, be backend.Backend) *semver.Version { ckey := backendClusterVersionKey() tx := be.ReadTx() @@ -712,6 +713,7 @@ func clusterVersionFromBackend(lg *zap.Logger, be backend.Backend) *semver.Versi return semver.Must(semver.NewVersion(string(vals[0]))) } +// The field is populated since etcd v3.5. func downgradeInfoFromBackend(lg *zap.Logger, be backend.Backend) *DowngradeInfo { dkey := backendDowngradeKey() tx := be.ReadTx() diff --git a/server/etcdserver/api/membership/confstate.go b/server/etcdserver/api/membership/confstate.go new file mode 100644 index 000000000..9bfc71b37 --- /dev/null +++ b/server/etcdserver/api/membership/confstate.go @@ -0,0 +1,63 @@ +// Copyright 2021 The etcd Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package membership + +import ( + "encoding/json" + "log" + + "go.etcd.io/etcd/raft/v3/raftpb" + "go.etcd.io/etcd/server/v3/mvcc" + "go.etcd.io/etcd/server/v3/mvcc/backend" + "go.uber.org/zap" +) + +var ( + confStateKey = []byte("confState") +) + +// MustUnsafeSaveConfStateToBackend persists confState using given transaction (tx). +// confState in backend is persisted since etcd v3.5. +func MustUnsafeSaveConfStateToBackend(lg *zap.Logger, tx backend.BatchTx, confState *raftpb.ConfState) { + confStateBytes, err := json.Marshal(confState) + if err != nil { + lg.Panic("Cannot marshal raftpb.ConfState", zap.Stringer("conf-state", confState), zap.Error(err)) + } + + tx.UnsafePut(mvcc.MetaBucketName, confStateKey, confStateBytes) +} + +// UnsafeConfStateFromBackend retrieves ConfState from the backend. +// Returns nil if confState in backend is not persisted (e.g. backend writen by