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

ticdc: upgrade ticdc first if version >= v5.1.0 #5140

Closed
wants to merge 22 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
7f46ef8
ticdc: upgrade ticdc first if version >= v5.1.0
Rustin170506 Jul 4, 2023
2acc11c
Fix
Rustin170506 Jul 28, 2023
39200e2
Use function
Rustin170506 Jul 28, 2023
d43c0ae
Revert
Rustin170506 Jul 28, 2023
06ef8a0
Fix upgrade order
Rustin170506 Jul 28, 2023
0f92ad4
Address comment
Rustin170506 Aug 3, 2023
3b6f2b8
Merge branch 'master' into rustin-patch-ticdc
csuzhangxc Aug 3, 2023
c76da7f
Merge branch 'master' into rustin-patch-ticdc
ti-chi-bot[bot] Aug 4, 2023
c62247b
Merge branch 'master' into rustin-patch-ticdc
ti-chi-bot[bot] Aug 4, 2023
4a92ae8
Merge branch 'master' into rustin-patch-ticdc
ti-chi-bot[bot] Aug 15, 2023
2ed1b12
Merge branch 'master' into rustin-patch-ticdc
ti-chi-bot[bot] Aug 16, 2023
f74203a
Merge branch 'master' into rustin-patch-ticdc
ti-chi-bot[bot] Aug 17, 2023
2b30bb1
Address comment
Rustin170506 Aug 18, 2023
c92bc90
Merge branch 'master' into rustin-patch-ticdc
ti-chi-bot[bot] Aug 18, 2023
aa4f9b7
Merge branch 'master' into rustin-patch-ticdc
ti-chi-bot[bot] Aug 18, 2023
6281837
Fix broken test
Rustin170506 Aug 21, 2023
f83c2ab
Merge branch 'master' into rustin-patch-ticdc
ti-chi-bot[bot] Aug 22, 2023
30b2e22
Merge branch 'master' into rustin-patch-ticdc
ti-chi-bot[bot] Aug 22, 2023
cb6489c
Merge branch 'master' into rustin-patch-ticdc
csuzhangxc Aug 23, 2023
832a9e8
Merge branch 'master' into rustin-patch-ticdc
ti-chi-bot[bot] Aug 23, 2023
b240949
Update go.mod
csuzhangxc Aug 23, 2023
1dd8215
Merge branch 'master' into rustin-patch-ticdc
ti-chi-bot[bot] Aug 25, 2023
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
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ require (
github.com/cespare/xxhash/v2 v2.1.1 // indirect
github.com/chai2010/gettext-go v0.0.0-20170215093142-bf70f2a70fb1 // indirect
github.com/containerd/containerd v1.4.11 // indirect
github.com/coreos/go-semver v0.3.0 // indirect
github.com/coreos/go-semver v0.3.0
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dimchansky/utfbom v1.1.0 // indirect
github.com/docker/distribution v2.7.1+incompatible // indirect
Expand Down
22 changes: 5 additions & 17 deletions pkg/apis/pingcap/v1alpha1/tidbcluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"k8s.io/klog/v2"

"github.com/pingcap/tidb-operator/pkg/apis/label"
"github.com/pingcap/tidb-operator/pkg/apis/util"
)

const (
Expand All @@ -44,9 +45,6 @@ const (
// shutdown a TiCDC pod.
defaultTiCDCGracefulShutdownTimeout = 10 * time.Minute
defaultPDStartTimeout = 30

// the latest version
versionLatest = "latest"
)

var (
Expand Down Expand Up @@ -92,7 +90,7 @@ func (tc *TidbCluster) PDVersion() string {
return ""
}

return getImageVersion(tc.PDImage())
return util.GetImageVersion(tc.PDImage())
}

// TiKVImage return the image used by TiKV.
Expand Down Expand Up @@ -128,7 +126,7 @@ func (tc *TidbCluster) TiKVVersion() string {
return ""
}

return getImageVersion(tc.TiKVImage())
return util.GetImageVersion(tc.TiKVImage())
}

func (tc *TidbCluster) TiKVContainerPrivilege() *bool {
Expand Down Expand Up @@ -189,7 +187,7 @@ func (tc *TidbCluster) TiFlashVersion() string {
return ""
}

return getImageVersion(tc.TiFlashImage())
return util.GetImageVersion(tc.TiFlashImage())
}

func (tc *TidbCluster) TiFlashContainerPrivilege() *bool {
Expand Down Expand Up @@ -309,17 +307,7 @@ func (tc *TidbCluster) TiDBVersion() string {
return ""
}

return getImageVersion(tc.TiDBImage())
}

// getImageVersion returns the verion of a image
func getImageVersion(image string) string {
colonIdx := strings.LastIndexByte(image, ':')
if colonIdx >= 0 {
return image[colonIdx+1:]
}

return versionLatest
return util.GetImageVersion(tc.TiDBImage())
}

// PumpImage return the image used by Pump.
Expand Down
31 changes: 31 additions & 0 deletions pkg/apis/util/util.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright 2023 PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package util

import "strings"

const (
// `latest` is a special version that always points to the latest version of the image.
VersionLatest = "latest"
)

// GetImageVersion returns the verion of a image
func GetImageVersion(image string) string {
colonIdx := strings.LastIndexByte(image, ':')
if colonIdx >= 0 {
return image[colonIdx+1:]
}

return VersionLatest
}
45 changes: 45 additions & 0 deletions pkg/apis/util/util_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright 2023 PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package util

import (
"testing"

. "github.com/onsi/gomega"
)

func TestGetImageVersion(t *testing.T) {
g := NewGomegaWithT(t)
cases := []struct {
image string
expected string
}{
{
image: "foo/bar:latest",
expected: "latest",
},
{
image: "foo/bar:1.2.3",
expected: "1.2.3",
},
{
image: "foo/bar",
expected: "latest",
},
}

for _, c := range cases {
g.Expect(GetImageVersion(c.image)).To(Equal(c.expected))
}
}
118 changes: 108 additions & 10 deletions pkg/controller/tidbcluster/tidb_cluster_control.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,22 @@
package tidbcluster

import (
"errors"
"fmt"
"regexp"

"github.com/coreos/go-semver/semver"
v1 "k8s.io/api/core/v1"
apiequality "k8s.io/apimachinery/pkg/api/equality"
apierrors "k8s.io/apimachinery/pkg/api/errors"
errorutils "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/client-go/tools/record"
"k8s.io/klog/v2"

"github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1"
"github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1/defaulting"
v1alpha1validation "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1/validation"
"github.com/pingcap/tidb-operator/pkg/apis/util"
"github.com/pingcap/tidb-operator/pkg/controller"
"github.com/pingcap/tidb-operator/pkg/manager"
"github.com/pingcap/tidb-operator/pkg/manager/member"
Expand Down Expand Up @@ -58,7 +65,9 @@ func NewDefaultTidbClusterControl(
discoveryManager member.TidbDiscoveryManager,
tidbClusterStatusManager manager.Manager,
conditionUpdater TidbClusterConditionUpdater,
recorder record.EventRecorder) ControlInterface {
recorder record.EventRecorder,
deps *controller.Dependencies,
) ControlInterface {
return &defaultTidbClusterControl{
tcControl: tcControl,
pdMemberManager: pdMemberManager,
Expand All @@ -77,6 +86,7 @@ func NewDefaultTidbClusterControl(
tidbClusterStatusManager: tidbClusterStatusManager,
conditionUpdater: conditionUpdater,
recorder: recorder,
deps: deps,
}
}

Expand All @@ -98,6 +108,7 @@ type defaultTidbClusterControl struct {
tidbClusterStatusManager manager.Manager
conditionUpdater TidbClusterConditionUpdater
recorder record.EventRecorder
deps *controller.Dependencies
}

// UpdateStatefulSet executes the core logic loop for a tidbcluster.
Expand Down Expand Up @@ -174,6 +185,23 @@ func (c *defaultTidbClusterControl) updateTidbCluster(tc *v1alpha1.TidbCluster)
return err
}

// Upgrade TiCDC first if needed.
upgradeTiCDCFirst, err := NeedToUpdateTiCDCFirst(ns, tcName, c.deps)
if err != nil {
return err
Rustin170506 marked this conversation as resolved.
Show resolved Hide resolved
}
if upgradeTiCDCFirst {
// works that should be done to make the ticdc cluster current state match the desired state:
// - waiting for the pd cluster available(pd cluster is in quorum)
// - waiting for the tikv cluster available(at least one peer works)
// - create or update ticdc deployment
// - sync ticdc cluster status from pd to TidbCluster object
if err := c.ticdcMemberManager.Sync(tc); err != nil {
metrics.ClusterUpdateErrors.WithLabelValues(ns, tcName, "ticdc").Inc()
return err
}
}

// works that should be done to make the pd cluster current state match the desired state:
// - create or update the pd service
// - create or update the pd headless service
Expand Down Expand Up @@ -250,16 +278,17 @@ func (c *defaultTidbClusterControl) updateTidbCluster(tc *v1alpha1.TidbCluster)
return err
}

// works that should be done to make the ticdc cluster current state match the desired state:
// - waiting for the pd cluster available(pd cluster is in quorum)
// - waiting for the tikv cluster available(at least one peer works)
// - create or update ticdc deployment
// - sync ticdc cluster status from pd to TidbCluster object
if err := c.ticdcMemberManager.Sync(tc); err != nil {
metrics.ClusterUpdateErrors.WithLabelValues(ns, tcName, "ticdc").Inc()
return err
if !upgradeTiCDCFirst {
// works that should be done to make the ticdc cluster current state match the desired state:
// - waiting for the pd cluster available(pd cluster is in quorum)
// - waiting for the tikv cluster available(at least one peer works)
// - create or update ticdc deployment
// - sync ticdc cluster status from pd to TidbCluster object
if err := c.ticdcMemberManager.Sync(tc); err != nil {
metrics.ClusterUpdateErrors.WithLabelValues(ns, tcName, "ticdc").Inc()
return err
}
}

// syncing the labels from Pod to PVC and PV, these labels include:
// - label.StoreIDLabelKey
// - label.MemberIDLabelKey
Expand Down Expand Up @@ -296,6 +325,75 @@ func (c *defaultTidbClusterControl) updateTidbCluster(tc *v1alpha1.TidbCluster)
return err
}

// NeedToUpdateTiCDCFirst checks if the TiCDC version is greater than or equal to v5.1.0.
// If so, we need to update TiCDC first before updating other components.
// See more: https://github.com/pingcap/tidb-operator/issues/4966#issuecomment-1606727165
func NeedToUpdateTiCDCFirst(ns string, tcName string, deps *controller.Dependencies) (bool, error) {
if deps == nil {
return false, nil
}

// Check if need to upgrade TiCDC first.
upgradeTiCDCFirst := false

oldSts, err := deps.StatefulSetLister.StatefulSets(ns).Get(controller.TiCDCMemberName(tcName))
if err != nil && !apierrors.IsNotFound(err) {
return false, fmt.Errorf("updateTidbCluster: failed to get sts %s for cluster %s/%s, error: %s", controller.TiCDCMemberName(tcName), ns, tcName, err)
}
stsNotExist := apierrors.IsNotFound(err)
if !stsNotExist {
Comment on lines +343 to +344
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NOTICE: the TiCDC sts wouldn't be created before PD&TiKV is ready, so this will not affect the behavior of TidbCluster creation.

// Get TiCDC version from old sts.
ticdcImage := oldSts.Spec.Template.Spec.Containers[0].Image
oldTiCDCVersion := util.GetImageVersion(ticdcImage)
upgradeTiCDCFirst, err = needToUpdateTiCDCFirst(oldTiCDCVersion)
// For some reason, we can't get the old TiCDC version, so we can't determine if we need to upgrade TiCDC first.
// In this case, we just return false.
if err != nil {
klog.Warningf("cluster %s/%s can't determine if need to upgrade TiCDC first, error: %s", ns, tcName, err)
return false, nil
}
}

return upgradeTiCDCFirst, nil
}

func needToUpdateTiCDCFirst(ticdcVersion string) (bool, error) {
// NOTE: 2023-07-04 is the date we add this check,
// so the latest version should be greater than v5.1.0.
if ticdcVersion == util.VersionLatest {
return true, nil
}
ver := &semver.Version{}
sanitizedVersion, err := sanitizeVersion(ticdcVersion)
if err != nil {
return false, err
}
if err := ver.Set(sanitizedVersion); err != nil {
return false, errors.New("ticdc version is invalid")
}

if ver.LessThan(*semver.New("5.1.0-alpha")) {
return false, nil
} else {
return true, nil
}
}

// versionRe matches the version string like v7.2.0, v7.2.0-pre,
// v6.1.3-20230517-5484207 and v5.1.1-20211227 to extract the version.
// You can find all the version strings in https://hub.docker.com/r/pingcap/ticdc/tags.
var versionRe = regexp.MustCompile(`v(\d+\.\d+\.\d+)(?:-\+)?`)

// sanitizeVersion remove the prefix "v" and suffix git hash.
// For example, the version string v3.0.0-rc.1-10-gd3f8e0a becomes 3.0.0-rc.1.
func sanitizeVersion(v string) (string, error) {
match := versionRe.FindStringSubmatch(v)
if len(match) > 1 {
return match[1], nil
}
return "", fmt.Errorf("invalid version string: %s", v)
}

func (c *defaultTidbClusterControl) recordMetrics(tc *v1alpha1.TidbCluster) {
ns := tc.GetNamespace()
tcName := tc.GetName()
Expand Down
Loading
Loading