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

Improve performance by removing TidbControl's lock #2489

Merged
merged 4 commits into from
May 20, 2020
Merged
Show file tree
Hide file tree
Changes from 2 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
154 changes: 75 additions & 79 deletions pkg/controller/tidb_control.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,17 @@ import (
"crypto/x509"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"time"

"github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1"
"github.com/pingcap/tidb-operator/pkg/httputil"
"github.com/pingcap/tidb-operator/pkg/util"
"github.com/pingcap/tidb/config"
"io/ioutil"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"net/http"
"sync"
"time"
)

const (
Expand All @@ -54,89 +54,41 @@ type TiDBControlInterface interface {

// defaultTiDBControl is default implementation of TiDBControlInterface.
type defaultTiDBControl struct {
// Now, all controllers use this single tidbConrol instance,
// we should add a mutex to avoid two controllers update the httpClient.Transport at same time.
mutex sync.Mutex
// TODO use httpClients cache instead, like pkg/pdapi/pdapi.go does.
httpClient *http.Client
kubeCli kubernetes.Interface
kubeCli kubernetes.Interface
// for unit test only
testURL string
}

// NewDefaultTiDBControl returns a defaultTiDBControl instance
func NewDefaultTiDBControl(kubeCli kubernetes.Interface) TiDBControlInterface {
return &defaultTiDBControl{httpClient: &http.Client{Timeout: timeout}, kubeCli: kubeCli}
}

func (tdc *defaultTiDBControl) useTLSHTTPClient(tc *v1alpha1.TidbCluster) error {
if !tc.IsTLSClusterEnabled() {
return nil
}

tcName := tc.Name
ns := tc.Namespace
secretName := util.ClusterClientTLSSecretName(tcName)
secret, err := tdc.kubeCli.CoreV1().Secrets(ns).Get(secretName, metav1.GetOptions{})
if err != nil {
return err
}

clientCert, certExists := secret.Data[v1.TLSCertKey]
clientKey, keyExists := secret.Data[v1.TLSPrivateKeyKey]
if !certExists || !keyExists {
return fmt.Errorf("cert or key does not exist in secret %s/%s", ns, secretName)
}

tlsCert, err := tls.X509KeyPair(clientCert, clientKey)
if err != nil {
return fmt.Errorf("unable to load certificates from secret %s/%s: %v", ns, secretName, err)
}

rootCAs := x509.NewCertPool()
rootCAs.AppendCertsFromPEM(secret.Data[v1.ServiceAccountRootCAKey])
config := &tls.Config{
RootCAs: rootCAs,
Certificates: []tls.Certificate{tlsCert},
}
tdc.httpClient.Transport = &http.Transport{TLSClientConfig: config}
return nil
func NewDefaultTiDBControl(kubeCli kubernetes.Interface) *defaultTiDBControl {
return &defaultTiDBControl{kubeCli: kubeCli}
}

func (tdc *defaultTiDBControl) GetHealth(tc *v1alpha1.TidbCluster, ordinal int32) (bool, error) {
tdc.mutex.Lock()
defer tdc.mutex.Unlock()

tcName := tc.GetName()
ns := tc.GetNamespace()
scheme := tc.Scheme()

if err := tdc.useTLSHTTPClient(tc); err != nil {
httpClient, err := tdc.getHTTPClient(tc)
if err != nil {
return false, err
}

hostName := fmt.Sprintf("%s-%d", TiDBMemberName(tcName), ordinal)
url := fmt.Sprintf("%s://%s.%s.%s:10080/status", scheme, hostName, TiDBPeerMemberName(tcName), ns)
_, err := tdc.getBodyOK(url)
baseURL := tdc.getBaseURL(tc, ordinal)
url := fmt.Sprintf("%s/status", baseURL)
_, err = getBodyOK(httpClient, url)
return err == nil, nil
}

func (tdc *defaultTiDBControl) GetInfo(tc *v1alpha1.TidbCluster, ordinal int32) (*DBInfo, error) {
tdc.mutex.Lock()
defer tdc.mutex.Unlock()

tcName := tc.GetName()
ns := tc.GetNamespace()
scheme := tc.Scheme()
if err := tdc.useTLSHTTPClient(tc); err != nil {
httpClient, err := tdc.getHTTPClient(tc)
if err != nil {
return nil, err
}

hostName := fmt.Sprintf("%s-%d", TiDBMemberName(tcName), ordinal)
url := fmt.Sprintf("%s://%s.%s.%s:10080/info", scheme, hostName, TiDBPeerMemberName(tcName), ns)
baseURL := tdc.getBaseURL(tc, ordinal)
url := fmt.Sprintf("%s/info", baseURL)
req, err := http.NewRequest("POST", url, nil)
if err != nil {
return nil, err
}
res, err := tdc.httpClient.Do(req)
res, err := httpClient.Do(req)
if err != nil {
return nil, err
}
Expand All @@ -158,23 +110,18 @@ func (tdc *defaultTiDBControl) GetInfo(tc *v1alpha1.TidbCluster, ordinal int32)
}

func (tdc *defaultTiDBControl) GetSettings(tc *v1alpha1.TidbCluster, ordinal int32) (*config.Config, error) {
tdc.mutex.Lock()
defer tdc.mutex.Unlock()

tcName := tc.GetName()
ns := tc.GetNamespace()
scheme := tc.Scheme()
if err := tdc.useTLSHTTPClient(tc); err != nil {
httpClient, err := tdc.getHTTPClient(tc)
if err != nil {
return nil, err
}

hostName := fmt.Sprintf("%s-%d", TiDBMemberName(tcName), ordinal)
url := fmt.Sprintf("%s://%s.%s.%s:10080/settings", scheme, hostName, TiDBPeerMemberName(tcName), ns)
baseURL := tdc.getBaseURL(tc, ordinal)
url := fmt.Sprintf("%s/settings", baseURL)
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
res, err := tdc.httpClient.Do(req)
res, err := httpClient.Do(req)
if err != nil {
return nil, err
}
Expand All @@ -195,8 +142,8 @@ func (tdc *defaultTiDBControl) GetSettings(tc *v1alpha1.TidbCluster, ordinal int
return &info, nil
}

func (tdc *defaultTiDBControl) getBodyOK(apiURL string) ([]byte, error) {
res, err := tdc.httpClient.Get(apiURL)
func getBodyOK(httpClient *http.Client, apiURL string) ([]byte, error) {
res, err := httpClient.Get(apiURL)
if err != nil {
return nil, err
}
Expand All @@ -213,6 +160,55 @@ func (tdc *defaultTiDBControl) getBodyOK(apiURL string) ([]byte, error) {
return body, err
}

func (tdc *defaultTiDBControl) getHTTPClient(tc *v1alpha1.TidbCluster) (*http.Client, error) {
httpClient := &http.Client{Timeout: timeout}
if !tc.IsTLSClusterEnabled() {
return httpClient, nil
}

tcName := tc.Name
ns := tc.Namespace
secretName := util.ClusterClientTLSSecretName(tcName)
secret, err := tdc.kubeCli.CoreV1().Secrets(ns).Get(secretName, metav1.GetOptions{})
if err != nil {
return nil, err
}

clientCert, certExists := secret.Data[v1.TLSCertKey]
clientKey, keyExists := secret.Data[v1.TLSPrivateKeyKey]
if !certExists || !keyExists {
return nil, fmt.Errorf("cert or key does not exist in secret %s/%s", ns, secretName)
}

tlsCert, err := tls.X509KeyPair(clientCert, clientKey)
if err != nil {
return nil, fmt.Errorf("unable to load certificates from secret %s/%s: %v", ns, secretName, err)
}

rootCAs := x509.NewCertPool()
rootCAs.AppendCertsFromPEM(secret.Data[v1.ServiceAccountRootCAKey])
config := &tls.Config{
RootCAs: rootCAs,
Certificates: []tls.Certificate{tlsCert},
}
httpClient.Transport = &http.Transport{TLSClientConfig: config}

return httpClient, nil
}

func (tdc *defaultTiDBControl) getBaseURL(tc *v1alpha1.TidbCluster, ordinal int32) string {
if tdc.testURL != "" {
return tdc.testURL
}

tcName := tc.GetName()
ns := tc.GetNamespace()
scheme := tc.Scheme()
hostName := fmt.Sprintf("%s-%d", TiDBMemberName(tcName), ordinal)

return fmt.Sprintf("%s://%s.%s.%s:10080", scheme, hostName, TiDBPeerMemberName(tcName), ns)
}

// FakeTiDBControl is a fake implementation of TiDBControlInterface.
type FakeTiDBControl struct {
healthInfo map[string]bool
Expand Down
Loading