Skip to content

Commit

Permalink
Merge pull request #17821 from siyuanfoundation/3.4-downgrade
Browse files Browse the repository at this point in the history
[3.4] Allow updateClusterVersion when downgrading from 3.5.
  • Loading branch information
ahrtr committed Apr 23, 2024
2 parents 913a080 + 0d62c99 commit 48b0c49
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 2 deletions.
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

0 comments on commit 48b0c49

Please sign in to comment.