tidb version 源码
tidb version 代码
文件路径:/br/pkg/version/version.go
// Copyright 2020 PingCAP, Inc. Licensed under Apache-2.0.
package version
import (
"context"
"fmt"
"math"
"regexp"
"strconv"
"strings"
"github.com/coreos/go-semver/semver"
"github.com/pingcap/errors"
"github.com/pingcap/kvproto/pkg/metapb"
"github.com/pingcap/log"
berrors "github.com/pingcap/tidb/br/pkg/errors"
"github.com/pingcap/tidb/br/pkg/logutil"
"github.com/pingcap/tidb/br/pkg/utils"
"github.com/pingcap/tidb/br/pkg/version/build"
"github.com/pingcap/tidb/sessionctx/variable"
"github.com/pingcap/tidb/util/engine"
pd "github.com/tikv/pd/client"
"go.uber.org/zap"
)
var (
minTiKVVersion = semver.New("3.1.0-beta.2")
incompatibleTiKVMajor3 = semver.New("3.1.0")
incompatibleTiKVMajor4 = semver.New("4.0.0-rc.1")
compatibleTiFlashMajor3 = semver.New("3.1.0")
compatibleTiFlashMajor4 = semver.New("4.0.0")
versionHash = regexp.MustCompile("-[0-9]+-g[0-9a-f]{7,}")
)
// NextMajorVersion returns the next major version.
func NextMajorVersion() semver.Version {
nextMajorVersion, err := semver.NewVersion(removeVAndHash(build.ReleaseVersion))
if err != nil {
// build.ReleaseVersion is unknown, assuming infinitely-new nightly version.
return semver.Version{Major: math.MaxInt64, PreRelease: "nightly"}
}
nextMajorVersion.BumpMajor()
return *nextMajorVersion
}
// removeVAndHash sanitizes a version string.
func removeVAndHash(v string) string {
v = versionHash.ReplaceAllLiteralString(v, "")
v = strings.TrimSuffix(v, "-dirty")
return strings.TrimPrefix(v, "v")
}
func checkTiFlashVersion(store *metapb.Store) error {
flash, err := semver.NewVersion(removeVAndHash(store.Version))
if err != nil {
return errors.Annotatef(berrors.ErrVersionMismatch, "failed to parse TiFlash %s version %s, err %s",
store.GetPeerAddress(), store.Version, err)
}
if flash.Major == 3 && flash.LessThan(*compatibleTiFlashMajor3) {
return errors.Annotatef(berrors.ErrVersionMismatch, "incompatible TiFlash %s version %s, try update it to %s",
store.GetPeerAddress(), store.Version, compatibleTiFlashMajor3)
}
if flash.Major == 4 && flash.LessThan(*compatibleTiFlashMajor4) {
return errors.Annotatef(berrors.ErrVersionMismatch, "incompatible TiFlash %s version %s, try update it to %s",
store.GetPeerAddress(), store.Version, compatibleTiFlashMajor4)
}
return nil
}
// 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, checker VerChecker) error {
stores, err := client.GetAllStores(ctx, pd.WithExcludeTombstone())
if err != nil {
return errors.Trace(err)
}
for _, s := range stores {
isTiFlash := engine.IsTiFlash(s)
log.Debug("checking compatibility of store in cluster",
zap.Uint64("ID", s.GetId()),
zap.Bool("TiFlash?", isTiFlash),
zap.String("address", s.GetAddress()),
zap.String("version", s.GetVersion()),
)
if isTiFlash {
if err := checkTiFlashVersion(s); err != nil {
return errors.Trace(err)
}
continue
}
tikvVersionString := removeVAndHash(s.Version)
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 checkerErr := checker(s, tikvVersion); checkerErr != nil {
return checkerErr
}
}
return nil
}
// 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 && backupVersion.Major-ver.Major > 1 {
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
}
}
// CheckVersionForBRPiTR checks whether version of the cluster and BR-pitr itself is compatible.
// Note: BR'version >= 6.1.0 at least in this function
func CheckVersionForBRPiTR(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)
}
// tikvVersion should at least 6.1.0
if tikvVersion.Major < 6 || (tikvVersion.Major == 6 && tikvVersion.Minor == 0) {
return errors.Annotatef(berrors.ErrVersionMismatch, "TiKV node %s version %s is too low when use PiTR, please update tikv's version to at least v6.1.0(v6.2.0+ recommanded)",
s.Address, tikvVersion)
}
// The versions of BR and TiKV should be the same when use BR 6.1.0
if BRVersion.Major == 6 && BRVersion.Minor == 1 {
if tikvVersion.Major != 6 || tikvVersion.Minor != 1 {
return errors.Annotatef(berrors.ErrVersionMismatch, "TiKV node %s version %s and BR %s version mismatch when use PiTR v6.1.0, please use the same version of BR",
s.Address, tikvVersion, build.ReleaseVersion)
}
} else {
// If BRVersion > v6.1.0, the version of TiKV should be at least v6.2.0
if tikvVersion.Major == 6 && tikvVersion.Minor <= 1 {
return errors.Annotatef(berrors.ErrVersionMismatch, "TiKV node %s version %s and BR %s version mismatch when use PiTR v6.2.0+, please use the tikv with version v6.2.0+",
s.Address, tikvVersion, build.ReleaseVersion)
}
}
return nil
}
// CheckVersionForDDL checks whether we use queue or table to execute ddl during restore.
func CheckVersionForDDL(s *metapb.Store, tikvVersion *semver.Version) error {
// use tikvVersion instead of tidbVersion since br doesn't have mysql client to connect tidb.
requireVersion := semver.New("6.2.0-alpha")
if tikvVersion.Compare(*requireVersion) < 0 {
log.Info("detected the old version of tidb cluster. set enable concurrent ddl to false")
variable.EnableConcurrentDDL.Store(false)
return nil
}
return nil
}
// 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.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)
}
// BR 6.x works with TiKV 5.x and not guarantee works with 4.x
if BRVersion.Major < tikvVersion.Major || BRVersion.Major-tikvVersion.Major > 1 {
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)
}
}
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
}
// CheckVersion checks if the actual version is within [requiredMinVersion, requiredMaxVersion).
func CheckVersion(component string, actual, requiredMinVersion, requiredMaxVersion semver.Version) error {
if actual.Compare(requiredMinVersion) < 0 {
return errors.Annotatef(berrors.ErrVersionMismatch,
"%s version too old, required to be in [%s, %s), found '%s'",
component,
requiredMinVersion,
requiredMaxVersion,
actual,
)
}
// Compare the major version number to make sure beta version does not pass
// the check. This is because beta version may contains incompatible
// changes.
if actual.Major >= requiredMaxVersion.Major {
return errors.Annotatef(berrors.ErrVersionMismatch,
"%s version too new, expected to be within [%s, %d.0.0), found '%s'",
component,
requiredMinVersion,
requiredMaxVersion.Major,
actual,
)
}
return nil
}
// ExtractTiDBVersion extracts TiDB version from TiDB SQL `version()` outputs.
func ExtractTiDBVersion(version string) (*semver.Version, error) {
// version format: "5.7.10-TiDB-v2.1.0-rc.1-7-g38c939f"
// ^~~~~~~~~^ we only want this part
// version format: "5.7.10-TiDB-v2.0.4-1-g06a0bf5"
// ^~~~^
// version format: "5.7.10-TiDB-v2.0.7"
// ^~~~^
// version format: "5.7.25-TiDB-v3.0.0-beta-211-g09beefbe0-dirty"
// ^~~~~~~~~^
// The version is generated by `git describe --tags` on the TiDB repository.
versions := strings.Split(strings.TrimSuffix(version, "-dirty"), "-")
end := len(versions)
switch end {
case 3, 4:
case 5, 6:
end -= 2
default:
return nil, errors.Annotatef(berrors.ErrVersionMismatch, "not a valid TiDB version: %s", version)
}
rawVersion := strings.Join(versions[2:end], "-")
rawVersion = strings.TrimPrefix(rawVersion, "v")
return semver.NewVersion(rawVersion)
}
// CheckTiDBVersion is equals to ExtractTiDBVersion followed by CheckVersion.
func CheckTiDBVersion(versionStr string, requiredMinVersion, requiredMaxVersion semver.Version) error {
serverInfo := ParseServerInfo(versionStr)
if serverInfo.ServerType != ServerTypeTiDB {
return errors.Errorf("server with version '%s' is not TiDB", versionStr)
}
return CheckVersion("TiDB", *serverInfo.ServerVersion, 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.
trimmedVerStr := strings.TrimSpace(version)
unquotedVerStr, err := strconv.Unquote(trimmedVerStr)
if err != nil {
unquotedVerStr = trimmedVerStr
}
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))
}
return ver
}
// FetchVersion gets the version information from the database server
//
// NOTE: the executed query will be:
// - `select tidb_version()` if target db is tidb
// - `select version()` if target db is not tidb
func FetchVersion(ctx context.Context, db utils.QueryExecutor) (string, error) {
var versionInfo string
const queryTiDB = "SELECT tidb_version();"
tidbRow := db.QueryRowContext(ctx, queryTiDB)
err := tidbRow.Scan(&versionInfo)
if err == nil && tidbReleaseVersionFullRegex.FindString(versionInfo) != "" {
return versionInfo, nil
}
log.L().Warn("select tidb_version() failed, will fallback to 'select version();'", logutil.ShortError(err))
const query = "SELECT version();"
row := db.QueryRowContext(ctx, query)
err = row.Scan(&versionInfo)
if err != nil {
return "", errors.Annotatef(err, "sql: %s", query)
}
return versionInfo, nil
}
type ServerType int
const (
// ServerTypeUnknown represents unknown server type
ServerTypeUnknown = iota
// ServerTypeMySQL represents MySQL server type
ServerTypeMySQL
// ServerTypeMariaDB represents MariaDB server type
ServerTypeMariaDB
// ServerTypeTiDB represents TiDB server type
ServerTypeTiDB
// ServerTypeAll represents All server types
ServerTypeAll
)
var serverTypeString = []string{
ServerTypeUnknown: "Unknown",
ServerTypeMySQL: "MySQL",
ServerTypeMariaDB: "MariaDB",
ServerTypeTiDB: "TiDB",
}
// String implements Stringer.String
func (s ServerType) String() string {
if s >= ServerTypeAll {
return ""
}
return serverTypeString[s]
}
// ServerInfo is the combination of ServerType and ServerInfo
type ServerInfo struct {
ServerType ServerType
ServerVersion *semver.Version
HasTiKV bool
}
var (
mysqlVersionRegex = regexp.MustCompile(`^\d+\.\d+\.\d+([0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?`)
// `select version()` result
tidbVersionRegex = regexp.MustCompile(`-[v]?\d+\.\d+\.\d+([0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?`)
// `select tidb_version()` result
tidbReleaseVersionRegex = regexp.MustCompile(`v\d+\.\d+\.\d+([0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?`)
// `select tidb_version()` result with full release version
tidbReleaseVersionFullRegex = regexp.MustCompile(`Release Version:\s*v\d+\.\d+\.\d+([0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?`)
)
// ParseServerInfo parses exported server type and version info from version string
func ParseServerInfo(src string) ServerInfo {
lowerCase := strings.ToLower(src)
serverInfo := ServerInfo{}
isReleaseVersion := false
switch {
case strings.Contains(lowerCase, "release version:"):
// this version string is tidb release version
serverInfo.ServerType = ServerTypeTiDB
isReleaseVersion = true
case strings.Contains(lowerCase, "tidb"):
serverInfo.ServerType = ServerTypeTiDB
case strings.Contains(lowerCase, "mariadb"):
serverInfo.ServerType = ServerTypeMariaDB
case mysqlVersionRegex.MatchString(lowerCase):
serverInfo.ServerType = ServerTypeMySQL
default:
serverInfo.ServerType = ServerTypeUnknown
}
var versionStr string
if serverInfo.ServerType == ServerTypeTiDB {
if isReleaseVersion {
versionStr = tidbReleaseVersionRegex.FindString(src)
} else {
versionStr = tidbVersionRegex.FindString(src)
versionStr = strings.TrimPrefix(versionStr, "-")
}
versionStr = strings.TrimPrefix(versionStr, "v")
} else {
versionStr = mysqlVersionRegex.FindString(src)
}
var err error
serverInfo.ServerVersion, err = semver.NewVersion(versionStr)
if err != nil {
log.L().Warn("fail to parse version, fallback to 0.0.0",
zap.String("version", versionStr))
serverInfo.ServerVersion = semver.New("0.0.0")
}
log.L().Info("detect server version",
zap.String("type", serverInfo.ServerType.String()),
zap.String("version", serverInfo.ServerVersion.String()))
return serverInfo
}
相关信息
相关文章
0
赞
热门推荐
-
2、 - 优质文章
-
3、 gate.io
-
8、 golang
-
9、 openharmony
-
10、 Vue中input框自动聚焦