From e79699f2fa478e470c7b964eb491ce8606bfc138 Mon Sep 17 00:00:00 2001 From: hillium Date: Thu, 25 Mar 2021 10:53:43 +0800 Subject: [PATCH 1/8] version, restore: add version check. --- pkg/conn/conn.go | 2 +- pkg/task/restore.go | 8 +++ pkg/version/version.go | 113 ++++++++++++++++++++++++------------ pkg/version/version_test.go | 88 ++++++++++++++++++++++++---- 4 files changed, 164 insertions(+), 47 deletions(-) diff --git a/pkg/conn/conn.go b/pkg/conn/conn.go index 3f46241c5..8ace7caf6 100644 --- a/pkg/conn/conn.go +++ b/pkg/conn/conn.go @@ -195,7 +195,7 @@ func NewMgr( return nil, errors.Trace(err) } if checkRequirements { - err = version.CheckClusterVersion(ctx, controller.GetPDClient()) + err = version.CheckClusterVersion(ctx, controller.GetPDClient(), version.CheckVersionForBR) if err != nil { return nil, errors.Annotate(err, "running BR in incompatible version of cluster, "+ "if you believe it's OK, use --check-requirements=false to skip.") diff --git a/pkg/task/restore.go b/pkg/task/restore.go index 25b825340..57446f438 100644 --- a/pkg/task/restore.go +++ b/pkg/task/restore.go @@ -6,6 +6,8 @@ import ( "context" "time" + "github.com/pingcap/br/pkg/version" + "github.com/opentracing/opentracing-go" "github.com/pingcap/errors" "github.com/pingcap/failpoint" @@ -209,6 +211,12 @@ func RunRestore(c context.Context, g glue.Glue, cmdName string, cfg *RestoreConf return errors.Trace(err) } g.Record("Size", utils.ArchiveSize(backupMeta)) + backupVersion := version.NormalizeBackupVersion(backupMeta.BrVersion) + if cfg.CheckRequirements && backupVersion != nil { + if versionErr := version.CheckClusterVersion(ctx, mgr.GetPDClient(), version.CheckVersionForBackup(backupVersion)); versionErr != nil { + return versionErr + } + } if err = client.InitBackupMeta(backupMeta, u); err != nil { return errors.Trace(err) diff --git a/pkg/version/version.go b/pkg/version/version.go index 5f1ce4eaf..534c52d44 100644 --- a/pkg/version/version.go +++ b/pkg/version/version.go @@ -6,15 +6,17 @@ import ( "context" "fmt" "regexp" + "strconv" "strings" + "github.com/pingcap/log" + pd "github.com/tikv/pd/client" + "go.uber.org/zap" + "github.com/pingcap/kvproto/pkg/metapb" "github.com/coreos/go-semver/semver" "github.com/pingcap/errors" - "github.com/pingcap/log" - pd "github.com/tikv/pd/client" - "go.uber.org/zap" berrors "github.com/pingcap/br/pkg/errors" "github.com/pingcap/br/pkg/version/build" @@ -74,12 +76,12 @@ func IsTiFlash(store *metapb.Store) bool { return false } +// VerChecker is a callback for the CheckClusterVersion, decides whether the cluster is suitable to execute restore. +// See also: CheckVersionForBackup and CheckVersionForBR. +type VerChecker func(store *metapb.Store, ver *semver.Version) error + // CheckClusterVersion check TiKV version. -func CheckClusterVersion(ctx context.Context, client pd.Client) error { - BRVersion, err := semver.NewVersion(removeVAndHash(build.ReleaseVersion)) - if err != nil { - return errors.Annotatef(berrors.ErrVersionMismatch, "%s: invalid version, please recompile using `git fetch origin --tags && make build`", err) - } +func CheckClusterVersion(ctx context.Context, client pd.Client, checker VerChecker) error { stores, err := client.GetAllStores(ctx, pd.WithExcludeTombstone()) if err != nil { return errors.Trace(err) @@ -99,44 +101,67 @@ func CheckClusterVersion(ctx context.Context, client pd.Client) error { } tikvVersionString := removeVAndHash(s.Version) - tikvVersion, err := semver.NewVersion(tikvVersionString) - if err != nil { - return errors.Annotatef(berrors.ErrVersionMismatch, "%s: TiKV node %s version %s is invalid", err, s.Address, tikvVersionString) + tikvVersion, getVersionErr := semver.NewVersion(tikvVersionString) + if getVersionErr != nil { + return errors.Annotatef(berrors.ErrVersionMismatch, "%s: TiKV node %s version %s is invalid", getVersionErr, s.Address, tikvVersionString) } - - if tikvVersion.Compare(*minTiKVVersion) < 0 { - return errors.Annotatef(berrors.ErrVersionMismatch, "TiKV node %s version %s don't support BR, please upgrade cluster to %s", - s.Address, tikvVersionString, build.ReleaseVersion) + if checkerErr := checker(s, tikvVersion); checkerErr != nil { + return checkerErr } + } + return nil +} - if tikvVersion.Major != BRVersion.Major { - return errors.Annotatef(berrors.ErrVersionMismatch, "TiKV node %s version %s and BR %s major version mismatch, please use the same version of BR", - s.Address, tikvVersionString, build.ReleaseVersion) +// CheckVersionForBackup checks the version for backup and +func CheckVersionForBackup(backupVersion *semver.Version) VerChecker { + return func(store *metapb.Store, ver *semver.Version) error { + if backupVersion.Major != ver.Major { + return errors.Annotatef(berrors.ErrVersionMismatch, + "backup with cluster version %s cannot be restored at cluster of version %s: major version mismatches", + backupVersion, ver) } + return nil + } +} - // BR(https://github.com/pingcap/br/pull/233) and TiKV(https://github.com/tikv/tikv/pull/7241) have breaking changes - // if BR include #233 and TiKV not include #7241, BR will panic TiKV during restore - // These incompatible version is 3.1.0 and 4.0.0-rc.1 - if tikvVersion.Major == 3 { - if tikvVersion.Compare(*incompatibleTiKVMajor3) < 0 && BRVersion.Compare(*incompatibleTiKVMajor3) >= 0 { - return errors.Annotatef(berrors.ErrVersionMismatch, "TiKV node %s version %s and BR %s version mismatch, please use the same version of BR", - s.Address, tikvVersionString, build.ReleaseVersion) - } - } +// CheckVersionForBR checks whether version of the cluster and BR itself is compatible. +func CheckVersionForBR(s *metapb.Store, tikvVersion *semver.Version) error { + BRVersion, err := semver.NewVersion(removeVAndHash(build.ReleaseVersion)) + if err != nil { + return errors.Annotatef(berrors.ErrVersionMismatch, "%s: invalid version, please recompile using `git fetch origin --tags && make build`", err) + } - if tikvVersion.Major == 4 { - if tikvVersion.Compare(*incompatibleTiKVMajor4) < 0 && BRVersion.Compare(*incompatibleTiKVMajor4) >= 0 { - return errors.Annotatef(berrors.ErrVersionMismatch, "TiKV node %s version %s and BR %s version mismatch, please use the same version of BR", - s.Address, tikvVersionString, build.ReleaseVersion) - } + if tikvVersion.Compare(*minTiKVVersion) < 0 { + return errors.Annotatef(berrors.ErrVersionMismatch, "TiKV node %s version %s don't support BR, please upgrade cluster to %s", + s.Address, tikvVersion, build.ReleaseVersion) + } + + if tikvVersion.Major != BRVersion.Major { + return errors.Annotatef(berrors.ErrVersionMismatch, "TiKV node %s version %s and BR %s major version mismatch, please use the same version of BR", + s.Address, tikvVersion, build.ReleaseVersion) + } + + // BR(https://github.com/pingcap/br/pull/233) and TiKV(https://github.com/tikv/tikv/pull/7241) have breaking changes + // if BR include #233 and TiKV not include #7241, BR will panic TiKV during restore + // These incompatible version is 3.1.0 and 4.0.0-rc.1 + if tikvVersion.Major == 3 { + if tikvVersion.Compare(*incompatibleTiKVMajor3) < 0 && BRVersion.Compare(*incompatibleTiKVMajor3) >= 0 { + return errors.Annotatef(berrors.ErrVersionMismatch, "TiKV node %s version %s and BR %s version mismatch, please use the same version of BR", + s.Address, tikvVersion, build.ReleaseVersion) } + } - // don't warn if we are the master build, which always have the version v4.0.0-beta.2-* - if build.GitBranch != "master" && tikvVersion.Compare(*BRVersion) > 0 { - log.Warn(fmt.Sprintf("BR version is outdated, please consider use version %s of BR", tikvVersionString)) - break + if tikvVersion.Major == 4 { + if tikvVersion.Compare(*incompatibleTiKVMajor4) < 0 && BRVersion.Compare(*incompatibleTiKVMajor4) >= 0 { + return errors.Annotatef(berrors.ErrVersionMismatch, "TiKV node %s version %s and BR %s version mismatch, please use the same version of BR", + s.Address, tikvVersion, build.ReleaseVersion) } } + + // don't warn if we are the master build, which always have the version v4.0.0-beta.2-* + if build.GitBranch != "master" && tikvVersion.Compare(*BRVersion) > 0 { + log.Warn(fmt.Sprintf("BR version is outdated, please consider use version %s of BR", tikvVersion)) + } return nil } @@ -199,3 +224,19 @@ func CheckTiDBVersion(versionStr string, requiredMinVersion, requiredMaxVersion } return CheckVersion("TiDB", *version, requiredMinVersion, requiredMaxVersion) } + +// NormalizeBackupVersion normalizes the version string from backupmeta. +func NormalizeBackupVersion(version string) *semver.Version { + // We need to unquote here because we get the version from PD HTTP API, + // which returns quoted string. + quoted, err := strconv.Unquote(strings.TrimSpace(version)) + if err != nil { + quoted = version + } + normalizedVerStr := strings.TrimSpace(quoted) + ver, err := semver.NewVersion(normalizedVerStr) + if err != nil { + log.Warn("cannot parse backup version", zap.String("version", normalizedVerStr), zap.Error(err)) + } + return ver +} diff --git a/pkg/version/version_test.go b/pkg/version/version_test.go index 7e9e344a3..3f3b309fb 100644 --- a/pkg/version/version_test.go +++ b/pkg/version/version_test.go @@ -4,6 +4,7 @@ package version import ( "context" + "fmt" "testing" "github.com/coreos/go-semver/semver" @@ -50,7 +51,7 @@ func (s *checkSuite) TestCheckClusterVersion(c *C) { mock.getAllStores = func() []*metapb.Store { return tiflash("v4.0.0-rc.1") } - err := CheckClusterVersion(context.Background(), &mock) + err := CheckClusterVersion(context.Background(), &mock, CheckVersionForBR) c.Assert(err, ErrorMatches, `incompatible.*version v4.0.0-rc.1, try update it to 4.0.0.*`) } @@ -59,7 +60,7 @@ func (s *checkSuite) TestCheckClusterVersion(c *C) { mock.getAllStores = func() []*metapb.Store { return tiflash("v3.1.0-beta.1") } - err := CheckClusterVersion(context.Background(), &mock) + err := CheckClusterVersion(context.Background(), &mock, CheckVersionForBR) c.Assert(err, ErrorMatches, `incompatible.*version v3.1.0-beta.1, try update it to 3.1.0.*`) } @@ -68,7 +69,7 @@ func (s *checkSuite) TestCheckClusterVersion(c *C) { mock.getAllStores = func() []*metapb.Store { return tiflash("v3.0.15") } - err := CheckClusterVersion(context.Background(), &mock) + err := CheckClusterVersion(context.Background(), &mock, CheckVersionForBR) c.Assert(err, ErrorMatches, `incompatible.*version v3.0.15, try update it to 3.1.0.*`) } @@ -77,7 +78,7 @@ func (s *checkSuite) TestCheckClusterVersion(c *C) { mock.getAllStores = func() []*metapb.Store { return []*metapb.Store{{Version: minTiKVVersion.String()}} } - err := CheckClusterVersion(context.Background(), &mock) + err := CheckClusterVersion(context.Background(), &mock, CheckVersionForBR) c.Assert(err, IsNil) } @@ -87,7 +88,7 @@ func (s *checkSuite) TestCheckClusterVersion(c *C) { // TiKV is too lower to support BR return []*metapb.Store{{Version: `v2.1.0`}} } - err := CheckClusterVersion(context.Background(), &mock) + err := CheckClusterVersion(context.Background(), &mock, CheckVersionForBR) c.Assert(err, ErrorMatches, ".*TiKV .* don't support BR, please upgrade cluster .*") } @@ -97,7 +98,7 @@ func (s *checkSuite) TestCheckClusterVersion(c *C) { // TiKV v3.1.0-beta.2 is incompatible with BR v3.1.0 return []*metapb.Store{{Version: minTiKVVersion.String()}} } - err := CheckClusterVersion(context.Background(), &mock) + err := CheckClusterVersion(context.Background(), &mock, CheckVersionForBR) c.Assert(err, ErrorMatches, "TiKV .* mismatch, please .*") } @@ -107,7 +108,7 @@ func (s *checkSuite) TestCheckClusterVersion(c *C) { // TiKV v4.0.0-rc major version mismatch with BR v3.1.0 return []*metapb.Store{{Version: "v4.0.0-rc"}} } - err := CheckClusterVersion(context.Background(), &mock) + err := CheckClusterVersion(context.Background(), &mock, CheckVersionForBR) c.Assert(err, ErrorMatches, "TiKV .* major version mismatch, please .*") } @@ -117,7 +118,7 @@ func (s *checkSuite) TestCheckClusterVersion(c *C) { // TiKV v4.0.0-rc.2 is incompatible with BR v4.0.0-beta.1 return []*metapb.Store{{Version: "v4.0.0-beta.1"}} } - err := CheckClusterVersion(context.Background(), &mock) + err := CheckClusterVersion(context.Background(), &mock, CheckVersionForBR) c.Assert(err, ErrorMatches, "TiKV .* mismatch, please .*") } @@ -127,17 +128,35 @@ func (s *checkSuite) TestCheckClusterVersion(c *C) { // TiKV v4.0.0-rc.1 with BR v4.0.0-rc.2 is ok return []*metapb.Store{{Version: "v4.0.0-rc.1"}} } - err := CheckClusterVersion(context.Background(), &mock) + err := CheckClusterVersion(context.Background(), &mock, CheckVersionForBR) c.Assert(err, IsNil) } + { + // Even across many patch versions, backup should be usable. + mock.getAllStores = func() []*metapb.Store { + return []*metapb.Store{{Version: "v4.0.0-rc.1"}} + } + err := CheckClusterVersion(context.Background(), &mock, CheckVersionForBackup(semver.New("4.0.12"))) + c.Assert(err, IsNil) + } + + { + // Restore across major version isn't allowed. + mock.getAllStores = func() []*metapb.Store { + return []*metapb.Store{{Version: "v4.0.0-rc.1"}} + } + err := CheckClusterVersion(context.Background(), &mock, CheckVersionForBackup(semver.New("5.0.0-rc"))) + c.Assert(err, Not(IsNil)) + } + { build.ReleaseVersion = "v4.0.0-rc.1" mock.getAllStores = func() []*metapb.Store { // TiKV v4.0.0-rc.2 with BR v4.0.0-rc.1 is ok return []*metapb.Store{{Version: "v4.0.0-rc.2"}} } - err := CheckClusterVersion(context.Background(), &mock) + err := CheckClusterVersion(context.Background(), &mock, CheckVersionForBR) c.Assert(err, IsNil) } } @@ -229,3 +248,52 @@ func (s *checkSuite) TestCheckVersion(c *C) { err = CheckVersion("TiNB", *semver.New("3.0.0-beta"), *semver.New("2.3.5"), *semver.New("3.0.0")) c.Assert(err, ErrorMatches, "TiNB version too new.*") } + +type versionEqualsC struct{} + +func (v versionEqualsC) Info() *CheckerInfo { + return &CheckerInfo{ + Name: "VersionEquals", + Params: []string{"source", "target"}, + } +} + +func (v versionEqualsC) Check(params []interface{}, names []string) (result bool, error string) { + source := params[0].(*semver.Version) + target := params[1].(*semver.Version) + + if source == nil || target == nil { + if target == source { + return true, "" + } else { + return false, fmt.Sprintf("one of version is nil but another is not (%s and %s)", params[0], params[1]) + } + } + + if source.Equal(*target) { + return true, "" + } else { + return false, fmt.Sprintf("version not equal (%s vs %s)", source, target) + } +} + +var versionEquals versionEqualsC + +func (s *checkSuite) TestNormalizeBackupVersion(c *C) { + cases := []struct { + target string + source string + }{ + {"4.0.0", `"4.0.0\n"`}, + {"5.0.0-rc.x", `"5.0.0-rc.x\n"`}, + {"5.0.0-rc.x", `5.0.0-rc.x`}, + {"4.0.12", `"4.0.12"` + "\n"}, + {"", ""}, + } + + for _, testCase := range cases { + target, _ := semver.NewVersion(testCase.target) + source := NormalizeBackupVersion(testCase.source) + c.Assert(source, versionEquals, target) + } +} From c544aa7fbf8e0f2bb0ba3e89f97088e2ae69f9ad Mon Sep 17 00:00:00 2001 From: hillium Date: Thu, 25 Mar 2021 11:17:07 +0800 Subject: [PATCH 2/8] restore: use cluster version --- pkg/task/restore.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/task/restore.go b/pkg/task/restore.go index 57446f438..fa34c36e5 100644 --- a/pkg/task/restore.go +++ b/pkg/task/restore.go @@ -211,7 +211,7 @@ func RunRestore(c context.Context, g glue.Glue, cmdName string, cfg *RestoreConf return errors.Trace(err) } g.Record("Size", utils.ArchiveSize(backupMeta)) - backupVersion := version.NormalizeBackupVersion(backupMeta.BrVersion) + backupVersion := version.NormalizeBackupVersion(backupMeta.ClusterVersion) if cfg.CheckRequirements && backupVersion != nil { if versionErr := version.CheckClusterVersion(ctx, mgr.GetPDClient(), version.CheckVersionForBackup(backupVersion)); versionErr != nil { return versionErr From 48c74450276a717c340403e2f58643eb091a384f Mon Sep 17 00:00:00 2001 From: hillium Date: Thu, 25 Mar 2021 11:33:55 +0800 Subject: [PATCH 3/8] *: fix lint --- pkg/task/restore.go | 2 +- pkg/version/version_test.go | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/pkg/task/restore.go b/pkg/task/restore.go index fa34c36e5..d0e8c57b7 100644 --- a/pkg/task/restore.go +++ b/pkg/task/restore.go @@ -214,7 +214,7 @@ func RunRestore(c context.Context, g glue.Glue, cmdName string, cfg *RestoreConf backupVersion := version.NormalizeBackupVersion(backupMeta.ClusterVersion) if cfg.CheckRequirements && backupVersion != nil { if versionErr := version.CheckClusterVersion(ctx, mgr.GetPDClient(), version.CheckVersionForBackup(backupVersion)); versionErr != nil { - return versionErr + return errors.Trace(versionErr) } } diff --git a/pkg/version/version_test.go b/pkg/version/version_test.go index 3f3b309fb..25f5a916d 100644 --- a/pkg/version/version_test.go +++ b/pkg/version/version_test.go @@ -265,16 +265,14 @@ func (v versionEqualsC) Check(params []interface{}, names []string) (result bool if source == nil || target == nil { if target == source { return true, "" - } else { - return false, fmt.Sprintf("one of version is nil but another is not (%s and %s)", params[0], params[1]) } + return false, fmt.Sprintf("one of version is nil but another is not (%s and %s)", params[0], params[1]) } if source.Equal(*target) { return true, "" - } else { - return false, fmt.Sprintf("version not equal (%s vs %s)", source, target) } + return false, fmt.Sprintf("version not equal (%s vs %s)", source, target) } var versionEquals versionEqualsC From d5a52bb31a54bfe92f75296508f17682ef780edb Mon Sep 17 00:00:00 2001 From: hillium Date: Thu, 25 Mar 2021 11:43:16 +0800 Subject: [PATCH 4/8] version: trim backup version anyway --- pkg/version/version.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/version/version.go b/pkg/version/version.go index 534c52d44..4df5acd67 100644 --- a/pkg/version/version.go +++ b/pkg/version/version.go @@ -229,9 +229,10 @@ func CheckTiDBVersion(versionStr string, requiredMinVersion, requiredMaxVersion func NormalizeBackupVersion(version string) *semver.Version { // We need to unquote here because we get the version from PD HTTP API, // which returns quoted string. - quoted, err := strconv.Unquote(strings.TrimSpace(version)) + trimmedRawVersion := strings.TrimSpace(version) + quoted, err := strconv.Unquote(trimmedRawVersion) if err != nil { - quoted = version + quoted = trimmedRawVersion } normalizedVerStr := strings.TrimSpace(quoted) ver, err := semver.NewVersion(normalizedVerStr) From 06b9a74920a13b2f089cb7019ff7ba4497e631d6 Mon Sep 17 00:00:00 2001 From: hillium Date: Thu, 25 Mar 2021 11:44:22 +0800 Subject: [PATCH 5/8] restore: rename some variables --- pkg/version/version.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/version/version.go b/pkg/version/version.go index 4df5acd67..68b5ad9ce 100644 --- a/pkg/version/version.go +++ b/pkg/version/version.go @@ -229,12 +229,12 @@ func CheckTiDBVersion(versionStr string, requiredMinVersion, requiredMaxVersion func NormalizeBackupVersion(version string) *semver.Version { // We need to unquote here because we get the version from PD HTTP API, // which returns quoted string. - trimmedRawVersion := strings.TrimSpace(version) - quoted, err := strconv.Unquote(trimmedRawVersion) + trimmedVerStr := strings.TrimSpace(version) + unquotedVerStr, err := strconv.Unquote(trimmedVerStr) if err != nil { - quoted = trimmedRawVersion + unquotedVerStr = trimmedVerStr } - normalizedVerStr := strings.TrimSpace(quoted) + normalizedVerStr := strings.TrimSpace(unquotedVerStr) ver, err := semver.NewVersion(normalizedVerStr) if err != nil { log.Warn("cannot parse backup version", zap.String("version", normalizedVerStr), zap.Error(err)) From 13e18a43b93ad44082619af64bd698b636ac06ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B1=B1=E5=B2=9A?= <36239017+YuJuncen@users.noreply.github.com> Date: Thu, 22 Apr 2021 17:36:01 +0800 Subject: [PATCH 6/8] Apply suggestions from code review Co-authored-by: Neil Shen --- pkg/version/version.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/pkg/version/version.go b/pkg/version/version.go index 68b5ad9ce..5b7c81dc6 100644 --- a/pkg/version/version.go +++ b/pkg/version/version.go @@ -12,9 +12,7 @@ import ( "github.com/pingcap/log" pd "github.com/tikv/pd/client" "go.uber.org/zap" - "github.com/pingcap/kvproto/pkg/metapb" - "github.com/coreos/go-semver/semver" "github.com/pingcap/errors" From 462549b3ae24758a607624dd16c91120e265dbf9 Mon Sep 17 00:00:00 2001 From: hillium Date: Thu, 22 Apr 2021 18:06:16 +0800 Subject: [PATCH 7/8] utils: run go fmt --- pkg/version/version.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/version/version.go b/pkg/version/version.go index 5b7c81dc6..7f07a6f57 100644 --- a/pkg/version/version.go +++ b/pkg/version/version.go @@ -9,12 +9,12 @@ import ( "strconv" "strings" + "github.com/coreos/go-semver/semver" + "github.com/pingcap/errors" + "github.com/pingcap/kvproto/pkg/metapb" "github.com/pingcap/log" pd "github.com/tikv/pd/client" "go.uber.org/zap" - "github.com/pingcap/kvproto/pkg/metapb" - "github.com/coreos/go-semver/semver" - "github.com/pingcap/errors" berrors "github.com/pingcap/br/pkg/errors" "github.com/pingcap/br/pkg/version/build" From f14958be25f16a4ae3dc76243cc9bb50ca4c3c68 Mon Sep 17 00:00:00 2001 From: hillium Date: Wed, 28 Apr 2021 11:57:49 +0800 Subject: [PATCH 8/8] version: address comment --- pkg/version/version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/version/version.go b/pkg/version/version.go index 7f07a6f57..24fcab15f 100644 --- a/pkg/version/version.go +++ b/pkg/version/version.go @@ -113,7 +113,7 @@ func CheckClusterVersion(ctx context.Context, client pd.Client, checker VerCheck // CheckVersionForBackup checks the version for backup and func CheckVersionForBackup(backupVersion *semver.Version) VerChecker { return func(store *metapb.Store, ver *semver.Version) error { - if backupVersion.Major != ver.Major { + if backupVersion.Major > ver.Major { return errors.Annotatef(berrors.ErrVersionMismatch, "backup with cluster version %s cannot be restored at cluster of version %s: major version mismatches", backupVersion, ver)