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

[3.4] Allow updateClusterVersion when downgrading from 3.5. #17821

Merged
merged 1 commit into from
Apr 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
25 changes: 25 additions & 0 deletions etcdserver/api/membership/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,15 @@ func ValidateClusterAndAssignIDs(lg *zap.Logger, local *RaftCluster, existing *R
return nil
}

// isValidDowngrade verifies whether the cluster can be downgraded from verFrom to verTo
func isValidDowngrade(verFrom *semver.Version, verTo *semver.Version) bool {
allowedDowngradeVersion := semver.Version{
Major: verFrom.Major,
Minor: verFrom.Minor - 1,
}
return verTo.Equal(allowedDowngradeVersion)
}

func mustDetectDowngrade(lg *zap.Logger, cv *semver.Version, nextClusterVersionCompatible bool) {
err := detectDowngrade(cv, nextClusterVersionCompatible)
if err != nil {
Expand Down Expand Up @@ -825,6 +834,22 @@ func detectDowngrade(cv *semver.Version, nextClusterVersionCompatible bool) erro
return nil
}

// IsValidClusterVersionChange checks the two scenario when version is valid to change:
// 1. Downgrade: cluster version is 1 minor version higher than local version,
// cluster version should change.
// 2. Cluster start: when not all members version are available, cluster version
// is set to MinVersion(3.0), when all members are at higher version, cluster version
// is lower than minimal server version, cluster version should change
func IsValidClusterVersionChange(verFrom *semver.Version, verTo *semver.Version, nextClusterVersionCompatible bool) bool {
verFrom = &semver.Version{Major: verFrom.Major, Minor: verFrom.Minor}
verTo = &semver.Version{Major: verTo.Major, Minor: verTo.Minor}

if (nextClusterVersionCompatible && isValidDowngrade(verFrom, verTo)) || (verFrom.Major == verTo.Major && verFrom.LessThan(*verTo)) {
return true
}
return false
}

// IsLocalMemberLearner returns if the local member is raft learner
func (c *RaftCluster) IsLocalMemberLearner() bool {
c.Lock()
Expand Down
78 changes: 78 additions & 0 deletions etcdserver/api/membership/cluster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"testing"

"github.com/coreos/go-semver/semver"
"github.com/stretchr/testify/assert"
"go.etcd.io/etcd/etcdserver/api/v2store"
"go.etcd.io/etcd/pkg/mock/mockstore"
"go.etcd.io/etcd/pkg/testutil"
Expand Down Expand Up @@ -948,6 +949,83 @@ func TestIsReadyToPromoteMember(t *testing.T) {
}
}

func TestIsVersionChangable(t *testing.T) {
tests := []struct {
name string
verFrom string
verTo string
nextClusterVersionCompatible bool
expectedResult bool
}{
{
name: "When local version is one minor lower than cluster version in downgrade mode",
verFrom: "3.5.0",
verTo: "3.4.0",
nextClusterVersionCompatible: true,
expectedResult: true,
},
{
name: "When local version is one minor lower than cluster version not in downgrade mode",
verFrom: "3.5.0",
verTo: "3.4.0",
expectedResult: false,
},
{
name: "When local version is one minor and one patch lower than cluster version in downgrade mode",
verFrom: "3.5.2",
verTo: "3.4.1",
nextClusterVersionCompatible: true,
expectedResult: true,
},
{
name: "When local version is one minor higher than cluster version",
verFrom: "3.3.0",
verTo: "3.4.0",
expectedResult: true,
},
{
name: "When local version is two minor higher than cluster version",
verFrom: "3.2.0",
verTo: "3.4.0",
expectedResult: true,
},
{
name: "When local version is one major higher than cluster version",
verFrom: "2.4.0",
verTo: "3.4.0",
expectedResult: false,
},
{
name: "When local version is equal to cluster version",
verFrom: "3.4.0",
verTo: "3.4.0",
expectedResult: false,
},
{
name: "When local version is one patch higher than cluster version",
verFrom: "3.4.0",
verTo: "3.4.1",
expectedResult: false,
},
{
name: "When local version is two minor lower than cluster version",
verFrom: "3.6.0",
verTo: "3.4.0",
nextClusterVersionCompatible: true,
expectedResult: false,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
verFrom := semver.Must(semver.NewVersion(tt.verFrom))
verTo := semver.Must(semver.NewVersion(tt.verTo))
ret := IsValidClusterVersionChange(verFrom, verTo, tt.nextClusterVersionCompatible)
assert.Equal(t, tt.expectedResult, ret)
})
}
}

func TestDetectDowngrade(t *testing.T) {
tests := []struct {
clusterVersion string
Expand Down
4 changes: 2 additions & 2 deletions etcdserver/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -2636,8 +2636,8 @@ func (s *EtcdServer) monitorVersions() {
}

// update cluster version only if the decided version is greater than
// the current cluster version
if v != nil && s.cluster.Version().LessThan(*v) {
// the current cluster version or it is a valid downgrade
if v != nil && membership.IsValidClusterVersionChange(s.cluster.Version(), v, s.Config().NextClusterVersionCompatible) {
s.goAttach(func() { s.updateClusterVersion(v.String()) })
}
}
Expand Down
Loading