github.com/XiaoMi/Gaea@v1.2.5/proxy/server/namespace.go (about)

     1  // Copyright 2019 The Gaea Authors. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package server
    16  
    17  import (
    18  	"errors"
    19  	"fmt"
    20  	"net"
    21  	"strconv"
    22  	"strings"
    23  	"time"
    24  
    25  	"github.com/XiaoMi/Gaea/backend"
    26  	"github.com/XiaoMi/Gaea/log"
    27  	"github.com/XiaoMi/Gaea/models"
    28  	"github.com/XiaoMi/Gaea/mysql"
    29  	"github.com/XiaoMi/Gaea/proxy/plan"
    30  	"github.com/XiaoMi/Gaea/proxy/router"
    31  	"github.com/XiaoMi/Gaea/proxy/sequence"
    32  	"github.com/XiaoMi/Gaea/util"
    33  	"github.com/XiaoMi/Gaea/util/cache"
    34  )
    35  
    36  const (
    37  	namespaceDelayClose = 60
    38  )
    39  
    40  const (
    41  	defaultSQLCacheCapacity  = 64
    42  	defaultPlanCacheCapacity = 128
    43  
    44  	defaultSlowSQLTime       = 1000  // millisecond
    45  	defaultMaxSqlExecuteTime = 0     // 默认为0,不开启慢sql熔断功能
    46  	defaultMaxSqlResultSize  = 10000 // 默认为10000, 限制查询返回的结果集大小不超过该阈值
    47  )
    48  
    49  // UserProperty means runtime user properties
    50  type UserProperty struct {
    51  	RWFlag        int
    52  	RWSplit       int
    53  	OtherProperty int
    54  }
    55  
    56  // Namespace is struct driected used by server
    57  type Namespace struct {
    58  	name               string
    59  	allowedDBs         map[string]bool
    60  	defaultPhyDBs      map[string]string // logicDBName-phyDBName
    61  	sqls               map[string]string //key: sql fingerprint
    62  	slowSQLTime        int64             // session slow sql time, millisecond, default 1000
    63  	allowips           []util.IPInfo
    64  	router             *router.Router
    65  	sequences          *sequence.SequenceManager
    66  	slices             map[string]*backend.Slice // key: slice name
    67  	userProperties     map[string]*UserProperty  // key: user name ,value: user's properties
    68  	defaultCharset     string
    69  	defaultCollationID mysql.CollationID
    70  	openGeneralLog     bool
    71  	maxSqlExecuteTime  int // session max sql execute time,millisecond
    72  	maxSqlResultSize   int
    73  	defaultSlice       string
    74  
    75  	slowSQLCache         *cache.LRUCache
    76  	errorSQLCache        *cache.LRUCache
    77  	backendSlowSQLCache  *cache.LRUCache
    78  	backendErrorSQLCache *cache.LRUCache
    79  	planCache            *cache.LRUCache
    80  }
    81  
    82  // DumpToJSON  means easy encode json
    83  func (n *Namespace) DumpToJSON() []byte {
    84  	return models.JSONEncode(n)
    85  }
    86  
    87  // NewNamespace init namespace
    88  func NewNamespace(namespaceConfig *models.Namespace) (*Namespace, error) {
    89  	var err error
    90  	namespace := &Namespace{
    91  		name:                 namespaceConfig.Name,
    92  		sqls:                 make(map[string]string, 16),
    93  		userProperties:       make(map[string]*UserProperty, 2),
    94  		openGeneralLog:       namespaceConfig.OpenGeneralLog,
    95  		slowSQLCache:         cache.NewLRUCache(defaultSQLCacheCapacity),
    96  		errorSQLCache:        cache.NewLRUCache(defaultSQLCacheCapacity),
    97  		backendSlowSQLCache:  cache.NewLRUCache(defaultSQLCacheCapacity),
    98  		backendErrorSQLCache: cache.NewLRUCache(defaultSQLCacheCapacity),
    99  		planCache:            cache.NewLRUCache(defaultPlanCacheCapacity),
   100  		defaultSlice:         namespaceConfig.DefaultSlice,
   101  	}
   102  
   103  	defer func() {
   104  		if err != nil {
   105  			namespace.Close(false)
   106  		}
   107  	}()
   108  
   109  	// init black sql
   110  	namespace.sqls = parseBlackSqls(namespaceConfig.BlackSQL)
   111  
   112  	// init session slow sql time
   113  	namespace.slowSQLTime, err = parseSlowSQLTime(namespaceConfig.SlowSQLTime)
   114  	if err != nil {
   115  		return nil, fmt.Errorf("parse slowSQLTime error: %v", err)
   116  	}
   117  
   118  	// init session slow sql max execute time
   119  	if namespaceConfig.MaxSqlExecuteTime <= 0 {
   120  		namespace.maxSqlExecuteTime = defaultMaxSqlExecuteTime
   121  	} else {
   122  		namespace.maxSqlExecuteTime = namespaceConfig.MaxSqlExecuteTime
   123  	}
   124  
   125  	// init session slow sql max result size
   126  	if namespaceConfig.MaxSqlResultSize <= 0 && namespaceConfig.MaxSqlResultSize != -1 {
   127  		namespace.maxSqlResultSize = defaultMaxSqlResultSize
   128  	} else {
   129  		namespace.maxSqlResultSize = namespaceConfig.MaxSqlResultSize
   130  	}
   131  
   132  	allowDBs := make(map[string]bool, len(namespaceConfig.AllowedDBS))
   133  	for db, allowed := range namespaceConfig.AllowedDBS {
   134  		allowDBs[strings.TrimSpace(db)] = allowed
   135  	}
   136  	namespace.allowedDBs = allowDBs
   137  
   138  	defaultPhyDBs := make(map[string]string, len(namespaceConfig.DefaultPhyDBS))
   139  	for db, phyDB := range namespaceConfig.DefaultPhyDBS {
   140  		defaultPhyDBs[strings.TrimSpace(db)] = strings.TrimSpace(phyDB)
   141  	}
   142  
   143  	namespace.defaultPhyDBs, err = parseDefaultPhyDB(defaultPhyDBs, allowDBs)
   144  	if err != nil {
   145  		return nil, fmt.Errorf("parse defaultPhyDBs error: %v", err)
   146  	}
   147  
   148  	// init allow ip
   149  	allowips, err := parseAllowIps(namespaceConfig.AllowedIP)
   150  	if err != nil {
   151  		return nil, fmt.Errorf("parse allowips error: %v", err)
   152  	}
   153  	namespace.allowips = allowips
   154  
   155  	namespace.defaultCharset, namespace.defaultCollationID, err = parseCharset(namespaceConfig.DefaultCharset, namespaceConfig.DefaultCollation)
   156  	if err != nil {
   157  		return nil, fmt.Errorf("parse charset error: %v", err)
   158  	}
   159  
   160  	// init user properties
   161  	for _, user := range namespaceConfig.Users {
   162  		up := &UserProperty{RWFlag: user.RWFlag, RWSplit: user.RWSplit, OtherProperty: user.OtherProperty}
   163  		namespace.userProperties[user.UserName] = up
   164  	}
   165  
   166  	// init backend slices
   167  	namespace.slices, err = parseSlices(namespaceConfig.Slices, namespace.defaultCharset, namespace.defaultCollationID)
   168  	if err != nil {
   169  		return nil, fmt.Errorf("init slices of namespace: %s failed, err: %v", namespaceConfig.Name, err)
   170  	}
   171  
   172  	// init router
   173  	namespace.router, err = router.NewRouter(namespaceConfig)
   174  	if err != nil {
   175  		return nil, fmt.Errorf("init router of namespace: %s failed, err: %v", namespace.name, err)
   176  	}
   177  
   178  	// init global sequences config
   179  	// 目前只支持基于mysql的序列号
   180  	sequences := sequence.NewSequenceManager()
   181  	for _, v := range namespaceConfig.GlobalSequences {
   182  		globalSequenceSlice, ok := namespace.slices[v.SliceName]
   183  		if !ok {
   184  			return nil, fmt.Errorf("init global sequence error: slice not found, sequence: %v", v)
   185  		}
   186  		seqName := strings.ToUpper(v.DB) + "." + strings.ToUpper(v.Table)
   187  		seq := sequence.NewMySQLSequence(globalSequenceSlice, seqName, v.PKName)
   188  		sequences.SetSequence(v.DB, v.Table, seq)
   189  	}
   190  	namespace.sequences = sequences
   191  
   192  	return namespace, nil
   193  }
   194  
   195  // GetName return namespace of namespace
   196  func (n *Namespace) GetName() string {
   197  	return n.name
   198  }
   199  
   200  // GetSlice return slice of namespace
   201  func (n *Namespace) GetSlice(name string) *backend.Slice {
   202  	return n.slices[name]
   203  }
   204  
   205  // GetRouter return router of namespace
   206  func (n *Namespace) GetRouter() *router.Router {
   207  	return n.router
   208  }
   209  
   210  func (n *Namespace) GetSequences() *sequence.SequenceManager {
   211  	return n.sequences
   212  }
   213  
   214  // IsClientIPAllowed check ip
   215  func (n *Namespace) IsClientIPAllowed(clientIP net.IP) bool {
   216  	if len(n.allowips) == 0 {
   217  		return true
   218  	}
   219  	for _, ip := range n.allowips {
   220  		if ip.Match(clientIP) {
   221  			return true
   222  		}
   223  	}
   224  	return false
   225  }
   226  
   227  func (n *Namespace) getSessionSlowSQLTime() int64 {
   228  	return n.slowSQLTime
   229  }
   230  
   231  // IsAllowWrite check if user allow to write
   232  func (n *Namespace) IsAllowWrite(user string) bool {
   233  	return n.userProperties[user].RWFlag == models.ReadWrite
   234  }
   235  
   236  // IsRWSplit chekc if read write split
   237  func (n *Namespace) IsRWSplit(user string) bool {
   238  	return n.userProperties[user].RWSplit == models.ReadWriteSplit
   239  }
   240  
   241  // IsStatisticUser check if user is used to statistic
   242  func (n *Namespace) IsStatisticUser(user string) bool {
   243  	return n.userProperties[user].OtherProperty == models.StatisticUser
   244  }
   245  
   246  // GetUserProperty return user information
   247  func (n *Namespace) GetUserProperty(user string) int {
   248  	return n.userProperties[user].OtherProperty
   249  }
   250  
   251  func (n *Namespace) GetMaxExecuteTime() int {
   252  	return n.maxSqlExecuteTime
   253  }
   254  
   255  func (n *Namespace) GetMaxResultSize() int {
   256  	return n.maxSqlResultSize
   257  }
   258  
   259  // IsSQLAllowed check black sql
   260  func (n *Namespace) IsSQLAllowed(reqCtx *util.RequestContext, sql string) bool {
   261  	if len(n.sqls) == 0 {
   262  		return true
   263  	}
   264  
   265  	fingerprint := mysql.GetFingerprint(sql)
   266  	reqCtx.Set("fingerprint", fingerprint)
   267  	md5 := mysql.GetMd5(fingerprint)
   268  	if _, ok := n.sqls[md5]; ok {
   269  		return false
   270  	}
   271  
   272  	return true
   273  }
   274  
   275  // IsAllowedDB if allowed database
   276  func (n *Namespace) IsAllowedDB(dbname string) bool {
   277  	allowed, ok := n.allowedDBs[dbname]
   278  	return ok && allowed
   279  }
   280  
   281  // GetAllowedDBs return all allowed databases
   282  func (n *Namespace) GetAllowedDBs() []string {
   283  	var ret []string
   284  	for db := range n.allowedDBs {
   285  		ret = append(ret, db)
   286  	}
   287  	return ret
   288  }
   289  
   290  // GetDefaultPhyDB return default real database
   291  func (n *Namespace) GetDefaultPhyDB(dbname string) (string, error) {
   292  	if dbname == "" {
   293  		return "", nil
   294  	}
   295  	phyDB, ok := n.defaultPhyDBs[dbname]
   296  	if !ok {
   297  		return "", fmt.Errorf("invalid db %s", dbname)
   298  	}
   299  	return phyDB, nil
   300  }
   301  
   302  func (n *Namespace) GetPhysicalDBs() map[string]string {
   303  	return n.defaultPhyDBs
   304  }
   305  
   306  // GetDefaultCharset return default charset
   307  func (n *Namespace) GetDefaultCharset() string {
   308  	return n.defaultCharset
   309  }
   310  
   311  func (n *Namespace) GetDefaultSlice() string {
   312  	return n.defaultSlice
   313  }
   314  
   315  // GetDefaultCollationID return default collation id
   316  func (n *Namespace) GetDefaultCollationID() mysql.CollationID {
   317  	return n.defaultCollationID
   318  }
   319  
   320  // GetCachedPlan get plan in cache
   321  func (n *Namespace) GetCachedPlan(db, sql string) (plan.Plan, bool) {
   322  	v, ok := n.planCache.Get(db + "|" + sql)
   323  	if !ok {
   324  		return nil, false
   325  	}
   326  	return v.(plan.Plan), true
   327  }
   328  
   329  // SetCachedPlan set plan in cache
   330  func (n *Namespace) SetCachedPlan(db, sql string, p plan.Plan) {
   331  	n.planCache.SetIfAbsent(db+"|"+sql, p)
   332  }
   333  
   334  // SetSlowSQLFingerprint store slow sql fingerprint
   335  func (n *Namespace) SetSlowSQLFingerprint(md5, fingerprint string) {
   336  	n.slowSQLCache.Set(md5, cache.CachedString(fingerprint))
   337  }
   338  
   339  // GetSlowSQLFingerprint return slow sql fingerprint
   340  func (n *Namespace) GetSlowSQLFingerprint(md5 string) (string, bool) {
   341  	v, ok := n.slowSQLCache.Get(md5)
   342  	if !ok {
   343  		return "", false
   344  	}
   345  	return string(v.(cache.CachedString)), true
   346  }
   347  
   348  // GetSlowSQLFingerprints return slow sql fingerprints
   349  func (n *Namespace) GetSlowSQLFingerprints() map[string]string {
   350  	ret := make(map[string]string)
   351  	items := n.slowSQLCache.Items()
   352  	for _, item := range items {
   353  		ret[item.Key] = string(item.Value.(cache.CachedString))
   354  	}
   355  	return ret
   356  }
   357  
   358  // ClearSlowSQLFingerprints clear all slow sql fingerprints
   359  func (n *Namespace) ClearSlowSQLFingerprints() {
   360  	n.slowSQLCache.Clear()
   361  }
   362  
   363  // SetErrorSQLFingerprint store error sql fingerprint
   364  func (n *Namespace) SetErrorSQLFingerprint(md5, fingerprint string) {
   365  	n.errorSQLCache.Set(md5, cache.CachedString(fingerprint))
   366  }
   367  
   368  // GetErrorSQLFingerprint return error sql fingerprint
   369  func (n *Namespace) GetErrorSQLFingerprint(md5 string) (string, bool) {
   370  	v, ok := n.errorSQLCache.Get(md5)
   371  	if !ok {
   372  		return "", false
   373  	}
   374  	return string(v.(cache.CachedString)), true
   375  }
   376  
   377  // GetErrorSQLFingerprints return all error sql fingerprints
   378  func (n *Namespace) GetErrorSQLFingerprints() map[string]string {
   379  	ret := make(map[string]string)
   380  	items := n.errorSQLCache.Items()
   381  	for _, item := range items {
   382  		ret[item.Key] = string(item.Value.(cache.CachedString))
   383  	}
   384  	return ret
   385  }
   386  
   387  // ClearErrorSQLFingerprints clear all error sql fingerprints
   388  func (n *Namespace) ClearErrorSQLFingerprints() {
   389  	n.errorSQLCache.Clear()
   390  }
   391  
   392  // SetBackendSlowSQLFingerprint store backend slow sql fingerprint
   393  func (n *Namespace) SetBackendSlowSQLFingerprint(md5, fingerprint string) {
   394  	n.backendSlowSQLCache.Set(md5, cache.CachedString(fingerprint))
   395  }
   396  
   397  // GetBackendSlowSQLFingerprint return backend slow sql fingerprint
   398  func (n *Namespace) GetBackendSlowSQLFingerprint(md5 string) (string, bool) {
   399  	v, ok := n.backendSlowSQLCache.Get(md5)
   400  	if !ok {
   401  		return "", false
   402  	}
   403  	return string(v.(cache.CachedString)), true
   404  }
   405  
   406  // GetBackendSlowSQLFingerprints return all backend slow sql fingerprints
   407  func (n *Namespace) GetBackendSlowSQLFingerprints() map[string]string {
   408  	ret := make(map[string]string)
   409  	items := n.backendSlowSQLCache.Items()
   410  	for _, item := range items {
   411  		ret[item.Key] = string(item.Value.(cache.CachedString))
   412  	}
   413  	return ret
   414  }
   415  
   416  // ClearBackendSlowSQLFingerprints clear all backend slow sql fingerprints
   417  func (n *Namespace) ClearBackendSlowSQLFingerprints() {
   418  	n.backendSlowSQLCache.Clear()
   419  }
   420  
   421  // SetBackendErrorSQLFingerprint store backend error sql fingerprint
   422  func (n *Namespace) SetBackendErrorSQLFingerprint(md5, fingerprint string) {
   423  	n.backendErrorSQLCache.Set(md5, cache.CachedString(fingerprint))
   424  }
   425  
   426  // GetBackendErrorSQLFingerprint return backedn error sql fingerprint
   427  func (n *Namespace) GetBackendErrorSQLFingerprint(md5 string) (string, bool) {
   428  	v, ok := n.backendErrorSQLCache.Get(md5)
   429  	if !ok {
   430  		return "", false
   431  	}
   432  	return string(v.(cache.CachedString)), true
   433  }
   434  
   435  // GetBackendErrorSQLFingerprints return all backend error sql fingerprints
   436  func (n *Namespace) GetBackendErrorSQLFingerprints() map[string]string {
   437  	ret := make(map[string]string)
   438  	items := n.backendErrorSQLCache.Items()
   439  	for _, item := range items {
   440  		ret[item.Key] = string(item.Value.(cache.CachedString))
   441  	}
   442  	return ret
   443  }
   444  
   445  // ClearBackendErrorSQLFingerprints clear all backend error sql fingerprints
   446  func (n *Namespace) ClearBackendErrorSQLFingerprints() {
   447  	n.backendErrorSQLCache.Clear()
   448  }
   449  
   450  // Close recycle resources of namespace
   451  func (n *Namespace) Close(delay bool) {
   452  	var err error
   453  	// delay close time
   454  	if delay {
   455  		time.Sleep(time.Second * namespaceDelayClose)
   456  	}
   457  	for k := range n.slices {
   458  		err = n.slices[k].Close()
   459  		if err != nil {
   460  			log.Warn("delay close slice: %s failed, err: %v", k, err)
   461  			continue
   462  		}
   463  	}
   464  	n.slowSQLCache.Clear()
   465  	n.errorSQLCache.Clear()
   466  	n.backendSlowSQLCache.Clear()
   467  	n.backendErrorSQLCache.Clear()
   468  }
   469  
   470  func parseSlice(cfg *models.Slice, charset string, collationID mysql.CollationID) (*backend.Slice, error) {
   471  	var err error
   472  	s := new(backend.Slice)
   473  	s.Cfg = *cfg
   474  	s.SetCharsetInfo(charset, collationID)
   475  
   476  	// parse master
   477  	err = s.ParseMaster(cfg.Master)
   478  	if err != nil {
   479  		return nil, err
   480  	}
   481  
   482  	// parse slaves
   483  	err = s.ParseSlave(cfg.Slaves)
   484  	if err != nil {
   485  		return nil, err
   486  	}
   487  
   488  	// parse statistic slaves
   489  	err = s.ParseStatisticSlave(cfg.StatisticSlaves)
   490  	if err != nil {
   491  		return nil, err
   492  	}
   493  
   494  	return s, nil
   495  }
   496  
   497  func parseSlices(cfgSlices []*models.Slice, charset string, collationID mysql.CollationID) (map[string]*backend.Slice, error) {
   498  	slices := make(map[string]*backend.Slice, len(cfgSlices))
   499  	for _, v := range cfgSlices {
   500  		v.Name = strings.TrimSpace(v.Name) // modify origin slice name, trim space
   501  		if _, ok := slices[v.Name]; ok {
   502  			return nil, fmt.Errorf("duplicate slice [%s]", v.Name)
   503  		}
   504  
   505  		s, err := parseSlice(v, charset, collationID)
   506  		if err != nil {
   507  			return nil, err
   508  		}
   509  
   510  		slices[v.Name] = s
   511  	}
   512  
   513  	return slices, nil
   514  }
   515  
   516  func parseAllowIps(allowedIP []string) ([]util.IPInfo, error) {
   517  	var allowips []util.IPInfo
   518  	for _, ipStr := range allowedIP {
   519  		ipStr = strings.TrimSpace(ipStr)
   520  		if len(ipStr) == 0 {
   521  			continue
   522  		}
   523  		ipInfo, err := util.ParseIPInfo(ipStr)
   524  		if err != nil {
   525  			return nil, err
   526  		}
   527  		allowips = append(allowips, ipInfo)
   528  	}
   529  	return allowips, nil
   530  }
   531  
   532  func parseBlackSqls(sqls []string) map[string]string {
   533  	sqlMap := make(map[string]string, 10)
   534  	for _, sql := range sqls {
   535  		sql = strings.TrimSpace(sql)
   536  		if len(sql) == 0 {
   537  			continue
   538  		}
   539  		fingerprint := mysql.GetFingerprint(sql)
   540  		md5 := mysql.GetMd5(fingerprint)
   541  		sqlMap[md5] = fingerprint
   542  	}
   543  	return sqlMap
   544  }
   545  
   546  func parseSlowSQLTime(str string) (int64, error) {
   547  	if str == "" {
   548  		return defaultSlowSQLTime, nil
   549  	}
   550  	t, err := strconv.ParseInt(str, 10, 64)
   551  	if err != nil {
   552  		return 0, err
   553  	}
   554  	if t < 0 {
   555  		return 0, fmt.Errorf("less than zero")
   556  	}
   557  
   558  	return t, nil
   559  }
   560  
   561  func parseCharset(charset, collation string) (string, mysql.CollationID, error) {
   562  	if charset == "" && collation == "" {
   563  		return mysql.DefaultCharset, mysql.DefaultCollationID, nil
   564  	}
   565  
   566  	if collation == "" {
   567  		collationID, ok := mysql.CharsetIds[charset]
   568  		if !ok {
   569  			return "", 0, errors.New("invalid charset")
   570  		}
   571  		return charset, collationID, nil
   572  	}
   573  
   574  	if err := mysql.VerifyCharset(charset, collation); err != nil {
   575  		return "", 0, err
   576  	}
   577  	collationID, ok := mysql.CollationNames[collation]
   578  	if !ok {
   579  		return "", 0, errors.New("invalid collation")
   580  	}
   581  
   582  	return charset, collationID, nil
   583  }
   584  
   585  func parseDefaultPhyDB(defaultPhyDBs map[string]string, allowedDBs map[string]bool) (map[string]string, error) {
   586  	// no logic database mode
   587  	if len(defaultPhyDBs) == 0 {
   588  		result := make(map[string]string, len(allowedDBs))
   589  		for db := range allowedDBs {
   590  			result[db] = db
   591  		}
   592  		return result, nil
   593  	}
   594  
   595  	// logic database mode
   596  	for db := range allowedDBs {
   597  		if _, ok := defaultPhyDBs[db]; !ok {
   598  			return nil, fmt.Errorf("db %s have no phy db", db)
   599  		}
   600  	}
   601  	return defaultPhyDBs, nil
   602  }