github.com/polarismesh/polaris@v1.17.8/store/mysql/circuitbreaker_config_v2.go (about)

     1  /**
     2   * Tencent is pleased to support the open source community by making Polaris available.
     3   *
     4   * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
     5   *
     6   * Licensed under the BSD 3-Clause License (the "License");
     7   * you may not use this file except in compliance with the License.
     8   * You may obtain a copy of the License at
     9   *
    10   * https://opensource.org/licenses/BSD-3-Clause
    11   *
    12   * Unless required by applicable law or agreed to in writing, software distributed
    13   * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
    14   * CONDITIONS OF ANY KIND, either express or implied. See the License for the
    15   * specific language governing permissions and limitations under the License.
    16   */
    17  
    18  package sqldb
    19  
    20  import (
    21  	"database/sql"
    22  	"fmt"
    23  	"strconv"
    24  	"strings"
    25  	"time"
    26  
    27  	"github.com/polarismesh/polaris/common/model"
    28  	"github.com/polarismesh/polaris/store"
    29  )
    30  
    31  const (
    32  	labelCreateCircuitBreakerRule = "createCircuitBreakerRule"
    33  	labelUpdateCircuitBreakerRule = "updateCircuitBreakerRule"
    34  	labelDeleteCircuitBreakerRule = "deleteCircuitBreakerRule"
    35  	labelEnableCircuitBreakerRule = "enableCircuitBreakerRule"
    36  )
    37  
    38  const (
    39  	insertCircuitBreakerRuleSql = `insert into circuitbreaker_rule_v2(
    40  			id, name, namespace, enable, revision, description, level, src_service, src_namespace, 
    41  			dst_service, dst_namespace, dst_method, config, ctime, mtime, etime)
    42  			values(?,?,?,?,?,?,?,?,?,?,?,?,?, sysdate(),sysdate(), %s)`
    43  	updateCircuitBreakerRuleSql = `update circuitbreaker_rule_v2 set name = ?, namespace=?, enable = ?, revision= ?,
    44  			description = ?, level = ?, src_service = ?, src_namespace = ?,
    45              dst_service = ?, dst_namespace = ?, dst_method = ?,
    46  			config = ?, mtime = sysdate(), etime=%s where id = ?`
    47  	deleteCircuitBreakerRuleSql = `update circuitbreaker_rule_v2 set flag = 1, mtime = sysdate() where id = ?`
    48  	enableCircuitBreakerRuleSql = `update circuitbreaker_rule_v2 set enable = ?, revision = ?, mtime = sysdate(), 
    49  			etime=%s where id = ?`
    50  	countCircuitBreakerRuleSql     = `select count(*) from circuitbreaker_rule_v2 where flag = 0`
    51  	queryCircuitBreakerRuleFullSql = `select id, name, namespace, enable, revision, description, level, src_service, 
    52  			src_namespace, dst_service, dst_namespace, dst_method, config, unix_timestamp(ctime), unix_timestamp(mtime), 
    53  			unix_timestamp(etime) from circuitbreaker_rule_v2 where flag = 0`
    54  	queryCircuitBreakerRuleBriefSql = `select id, name, namespace, enable, revision, level, src_service, src_namespace, 
    55  			dst_service, dst_namespace, dst_method, unix_timestamp(ctime), unix_timestamp(mtime), unix_timestamp(etime)
    56  			from circuitbreaker_rule_v2 where flag = 0`
    57  	queryCircuitBreakerRuleCacheSql = `select id, name, namespace, enable, revision, description, level, src_service, 
    58  			src_namespace, dst_service, dst_namespace, dst_method, config, flag, unix_timestamp(ctime), 
    59  			unix_timestamp(mtime), unix_timestamp(etime) from circuitbreaker_rule_v2 where mtime > FROM_UNIXTIME(?)`
    60  )
    61  
    62  const (
    63  	labelCreateCircuitBreakerRuleOld    = "createCircuitBreakerRuleOld"
    64  	labelTagCircuitBreakerRuleOld       = "tagCircuitBreakerRuleOld"
    65  	labelDeleteTagCircuitBreakerRuleOld = "deleteTagCircuitBreakerRuleOld"
    66  	labelReleaseCircuitBreakerRuleOld   = "releaseCircuitBreakerRuleOld"
    67  	labelUnbindCircuitBreakerRuleOld    = "unbindCircuitBreakerRuleOld"
    68  	labelUpdateCircuitBreakerRuleOld    = "updateCircuitBreakerRuleOld"
    69  	labelDeleteCircuitBreakerRuleOld    = "deleteCircuitBreakerRuleOld"
    70  )
    71  
    72  // circuitBreakerStore 的实现
    73  type circuitBreakerStore struct {
    74  	master *BaseDB
    75  	slave  *BaseDB
    76  }
    77  
    78  func (c *circuitBreakerStore) CreateCircuitBreakerRule(cbRule *model.CircuitBreakerRule) error {
    79  	err := RetryTransaction(labelCreateCircuitBreakerRule, func() error {
    80  		return c.createCircuitBreakerRule(cbRule)
    81  	})
    82  
    83  	return store.Error(err)
    84  }
    85  
    86  func (c *circuitBreakerStore) createCircuitBreakerRule(cbRule *model.CircuitBreakerRule) error {
    87  	return c.master.processWithTransaction(labelCreateCircuitBreakerRule, func(tx *BaseTx) error {
    88  		etimeStr := buildEtimeStr(cbRule.Enable)
    89  		str := fmt.Sprintf(insertCircuitBreakerRuleSql, etimeStr)
    90  		if _, err := tx.Exec(str, cbRule.ID, cbRule.Name, cbRule.Namespace, cbRule.Enable, cbRule.Revision,
    91  			cbRule.Description, cbRule.Level, cbRule.SrcService, cbRule.SrcNamespace, cbRule.DstService,
    92  			cbRule.DstNamespace, cbRule.DstMethod, cbRule.Rule); err != nil {
    93  			log.Errorf("[Store][database] fail to %s exec sql, err: %s", labelCreateCircuitBreakerRule, err.Error())
    94  			return err
    95  		}
    96  		if err := tx.Commit(); err != nil {
    97  			log.Errorf("[Store][database] fail to %s commit tx, rule(%+v) commit tx err: %s",
    98  				labelCreateCircuitBreakerRule, cbRule, err.Error())
    99  			return err
   100  		}
   101  		return nil
   102  	})
   103  }
   104  
   105  // UpdateCircuitBreakerRule 更新熔断规则
   106  func (c *circuitBreakerStore) UpdateCircuitBreakerRule(cbRule *model.CircuitBreakerRule) error {
   107  	err := RetryTransaction(labelUpdateCircuitBreakerRule, func() error {
   108  		return c.updateCircuitBreakerRule(cbRule)
   109  	})
   110  
   111  	return store.Error(err)
   112  }
   113  
   114  func (c *circuitBreakerStore) updateCircuitBreakerRule(cbRule *model.CircuitBreakerRule) error {
   115  	return c.master.processWithTransaction(labelUpdateCircuitBreakerRule, func(tx *BaseTx) error {
   116  		etimeStr := buildEtimeStr(cbRule.Enable)
   117  		str := fmt.Sprintf(updateCircuitBreakerRuleSql, etimeStr)
   118  		if _, err := tx.Exec(str, cbRule.Name, cbRule.Namespace, cbRule.Enable,
   119  			cbRule.Revision, cbRule.Description, cbRule.Level, cbRule.SrcService, cbRule.SrcNamespace,
   120  			cbRule.DstService, cbRule.DstNamespace, cbRule.DstMethod, cbRule.Rule, cbRule.ID); err != nil {
   121  			log.Errorf("[Store][database] fail to %s exec sql, err: %s", labelUpdateCircuitBreakerRule, err.Error())
   122  			return err
   123  		}
   124  
   125  		if err := tx.Commit(); err != nil {
   126  			log.Errorf("[Store][database] fail to %s commit tx, rule(%+v) commit tx err: %s",
   127  				labelUpdateCircuitBreakerRule, cbRule, err.Error())
   128  			return err
   129  		}
   130  
   131  		return nil
   132  	})
   133  }
   134  
   135  // DeleteCircuitBreakerRule 删除熔断规则
   136  func (c *circuitBreakerStore) DeleteCircuitBreakerRule(id string) error {
   137  	err := RetryTransaction("deleteCircuitBreakerRule", func() error {
   138  		return c.deleteCircuitBreakerRule(id)
   139  	})
   140  
   141  	return store.Error(err)
   142  }
   143  
   144  func (c *circuitBreakerStore) deleteCircuitBreakerRule(id string) error {
   145  	return c.master.processWithTransaction(labelDeleteCircuitBreakerRule, func(tx *BaseTx) error {
   146  		if _, err := tx.Exec(deleteCircuitBreakerRuleSql, id); err != nil {
   147  			log.Errorf(
   148  				"[Store][database] fail to %s exec sql, err: %s", labelDeleteCircuitBreakerRule, err.Error())
   149  			return err
   150  		}
   151  
   152  		if err := tx.Commit(); err != nil {
   153  			log.Errorf("[Store][database] fail to %s commit tx, rule(%s) commit tx err: %s",
   154  				labelDeleteCircuitBreakerRule, id, err.Error())
   155  			return err
   156  		}
   157  		return nil
   158  	})
   159  }
   160  
   161  // HasCircuitBreakerRule check circuitbreaker rule exists
   162  func (c *circuitBreakerStore) HasCircuitBreakerRule(id string) (bool, error) {
   163  	queryParams := map[string]string{"id": id}
   164  	count, err := c.getCircuitBreakerRulesCount(queryParams)
   165  	if nil != err {
   166  		return false, err
   167  	}
   168  	return count > 0, nil
   169  }
   170  
   171  // HasCircuitBreakerRuleByName check circuitbreaker rule exists by name
   172  func (c *circuitBreakerStore) HasCircuitBreakerRuleByName(name string, namespace string) (bool, error) {
   173  	queryParams := map[string]string{exactName: name, "namespace": namespace}
   174  	count, err := c.getCircuitBreakerRulesCount(queryParams)
   175  	if nil != err {
   176  		return false, err
   177  	}
   178  	return count > 0, nil
   179  }
   180  
   181  // HasCircuitBreakerRuleByNameExcludeId check circuitbreaker rule exists by name exclude id
   182  func (c *circuitBreakerStore) HasCircuitBreakerRuleByNameExcludeId(
   183  	name string, namespace string, id string) (bool, error) {
   184  	queryParams := map[string]string{exactName: name, "namespace": namespace, excludeId: id}
   185  	count, err := c.getCircuitBreakerRulesCount(queryParams)
   186  	if nil != err {
   187  		return false, err
   188  	}
   189  	return count > 0, nil
   190  }
   191  
   192  func fetchCircuitBreakerRuleRows(rows *sql.Rows) ([]*model.CircuitBreakerRule, error) {
   193  	defer rows.Close()
   194  	var out []*model.CircuitBreakerRule
   195  	for rows.Next() {
   196  		var cbRule model.CircuitBreakerRule
   197  		var flag int
   198  		var ctime, mtime, etime int64
   199  		err := rows.Scan(&cbRule.ID, &cbRule.Name, &cbRule.Namespace, &cbRule.Enable, &cbRule.Revision,
   200  			&cbRule.Description, &cbRule.Level, &cbRule.SrcService, &cbRule.SrcNamespace, &cbRule.DstService,
   201  			&cbRule.DstNamespace, &cbRule.DstMethod, &cbRule.Rule, &flag, &ctime, &mtime, &etime)
   202  		if err != nil {
   203  			log.Errorf("[Store][database] fetch circuitbreaker rule scan err: %s", err.Error())
   204  			return nil, err
   205  		}
   206  		cbRule.CreateTime = time.Unix(ctime, 0)
   207  		cbRule.ModifyTime = time.Unix(mtime, 0)
   208  		cbRule.EnableTime = time.Unix(etime, 0)
   209  		cbRule.Valid = true
   210  		if flag == 1 {
   211  			cbRule.Valid = false
   212  		}
   213  		out = append(out, &cbRule)
   214  	}
   215  	if err := rows.Err(); err != nil {
   216  		log.Errorf("[Store][database] fetch circuitbreaker rule next err: %s", err.Error())
   217  		return nil, err
   218  	}
   219  	return out, nil
   220  }
   221  
   222  func (c *circuitBreakerStore) GetCircuitBreakerRules(
   223  	filter map[string]string, offset uint32, limit uint32) (uint32, []*model.CircuitBreakerRule, error) {
   224  	var out []*model.CircuitBreakerRule
   225  	var err error
   226  
   227  	bValue, ok := filter[briefSearch]
   228  	var isBrief = ok && strings.ToLower(bValue) == "true"
   229  	delete(filter, briefSearch)
   230  
   231  	if isBrief {
   232  		out, err = c.getBriefCircuitBreakerRules(filter, offset, limit)
   233  	} else {
   234  		out, err = c.getFullCircuitBreakerRules(filter, offset, limit)
   235  	}
   236  	if err != nil {
   237  		return 0, nil, err
   238  	}
   239  	num, err := c.getCircuitBreakerRulesCount(filter)
   240  	if err != nil {
   241  		return 0, nil, err
   242  	}
   243  	return num, out, nil
   244  }
   245  
   246  func (c *circuitBreakerStore) getBriefCircuitBreakerRules(
   247  	filter map[string]string, offset uint32, limit uint32) ([]*model.CircuitBreakerRule, error) {
   248  	queryStr, args := genCircuitBreakerRuleSQL(filter)
   249  	args = append(args, offset, limit)
   250  	str := queryCircuitBreakerRuleBriefSql + queryStr + ` order by mtime desc limit ?, ?`
   251  
   252  	rows, err := c.master.Query(str, args...)
   253  	if err != nil {
   254  		log.Errorf("[Store][database] query brief circuitbreaker rules err: %s", err.Error())
   255  		return nil, err
   256  	}
   257  	out, err := fetchBriefCircuitBreakerRules(rows)
   258  	if err != nil {
   259  		return nil, err
   260  	}
   261  	return out, nil
   262  }
   263  
   264  var blurQueryKeys = map[string]bool{
   265  	"name":         true,
   266  	"description":  true,
   267  	"srcService":   true,
   268  	"srcNamespace": true,
   269  	"dstService":   true,
   270  	"dstNamespace": true,
   271  	"dstMethod":    true,
   272  }
   273  
   274  const (
   275  	svcSpecificQueryKeyService   = "service"
   276  	svcSpecificQueryKeyNamespace = "serviceNamespace"
   277  	exactName                    = "exactName"
   278  	excludeId                    = "excludeId"
   279  )
   280  
   281  func placeholders(n int) string {
   282  	var b strings.Builder
   283  	for i := 0; i < n-1; i++ {
   284  		b.WriteString("?,")
   285  	}
   286  	if n > 0 {
   287  		b.WriteString("?")
   288  	}
   289  	return b.String()
   290  }
   291  
   292  func genCircuitBreakerRuleSQL(query map[string]string) (string, []interface{}) {
   293  	str := ""
   294  	args := make([]interface{}, 0, len(query))
   295  	var svcNamespaceQueryValue string
   296  	var svcQueryValue string
   297  	for key, value := range query {
   298  		if len(value) == 0 {
   299  			continue
   300  		}
   301  		if key == svcSpecificQueryKeyService {
   302  			svcQueryValue = value
   303  			continue
   304  		}
   305  		if key == svcSpecificQueryKeyNamespace {
   306  			svcNamespaceQueryValue = value
   307  			continue
   308  		}
   309  		storeKey := toUnderscoreName(key)
   310  		if _, ok := blurQueryKeys[key]; ok {
   311  			str += fmt.Sprintf(" and %s like ?", storeKey)
   312  			args = append(args, "%"+value+"%")
   313  		} else if key == "enable" {
   314  			str += fmt.Sprintf(" and %s = ?", storeKey)
   315  			arg, _ := strconv.ParseBool(value)
   316  			args = append(args, arg)
   317  		} else if key == "level" {
   318  			tokens := strings.Split(value, ",")
   319  			str += fmt.Sprintf(" and %s in (%s)", storeKey, placeholders(len(tokens)))
   320  			for _, token := range tokens {
   321  				args = append(args, token)
   322  			}
   323  		} else if key == exactName {
   324  			str += " and name = ?"
   325  			args = append(args, value)
   326  		} else if key == excludeId {
   327  			str += " and id != ?"
   328  			args = append(args, value)
   329  		} else {
   330  			str += fmt.Sprintf(" and %s = ?", storeKey)
   331  			args = append(args, value)
   332  		}
   333  	}
   334  	if len(svcQueryValue) > 0 {
   335  		str += " and (dst_service = ? or dst_service = '*')"
   336  		args = append(args, svcQueryValue)
   337  	}
   338  	if len(svcNamespaceQueryValue) > 0 {
   339  		str += " and (dst_namespace = ? or dst_namespace = '*')"
   340  		args = append(args, svcNamespaceQueryValue)
   341  	}
   342  	return str, args
   343  }
   344  
   345  // fetchBriefRateLimitRows fetch the brief ratelimit list
   346  func fetchBriefCircuitBreakerRules(rows *sql.Rows) ([]*model.CircuitBreakerRule, error) {
   347  	defer rows.Close()
   348  	var out []*model.CircuitBreakerRule
   349  	for rows.Next() {
   350  		var cbRule model.CircuitBreakerRule
   351  		var ctime, mtime, etime int64
   352  		err := rows.Scan(&cbRule.ID, &cbRule.Name, &cbRule.Namespace, &cbRule.Enable, &cbRule.Revision,
   353  			&cbRule.Level, &cbRule.SrcService, &cbRule.SrcNamespace, &cbRule.DstService, &cbRule.DstNamespace,
   354  			&cbRule.DstMethod, &ctime, &mtime, &etime)
   355  		if err != nil {
   356  			log.Errorf("[Store][database] fetch brief circuitbreaker rule scan err: %s", err.Error())
   357  			return nil, err
   358  		}
   359  		cbRule.CreateTime = time.Unix(ctime, 0)
   360  		cbRule.ModifyTime = time.Unix(mtime, 0)
   361  		cbRule.EnableTime = time.Unix(etime, 0)
   362  		out = append(out, &cbRule)
   363  	}
   364  	if err := rows.Err(); err != nil {
   365  		log.Errorf("[Store][database] fetch brief circuitbreaker rule next err: %s", err.Error())
   366  		return nil, err
   367  	}
   368  	return out, nil
   369  }
   370  
   371  func (c *circuitBreakerStore) getFullCircuitBreakerRules(
   372  	filter map[string]string, offset uint32, limit uint32) ([]*model.CircuitBreakerRule, error) {
   373  	queryStr, args := genCircuitBreakerRuleSQL(filter)
   374  	args = append(args, offset, limit)
   375  	str := queryCircuitBreakerRuleFullSql + queryStr + ` order by mtime desc limit ?, ?`
   376  
   377  	rows, err := c.master.Query(str, args...)
   378  	if err != nil {
   379  		log.Errorf("[Store][database] query brief circuitbreaker rules err: %s", err.Error())
   380  		return nil, err
   381  	}
   382  	out, err := fetchFullCircuitBreakerRules(rows)
   383  	if err != nil {
   384  		return nil, err
   385  	}
   386  	return out, nil
   387  }
   388  
   389  func fetchFullCircuitBreakerRules(rows *sql.Rows) ([]*model.CircuitBreakerRule, error) {
   390  	defer rows.Close()
   391  	var out []*model.CircuitBreakerRule
   392  	for rows.Next() {
   393  		var cbRule model.CircuitBreakerRule
   394  		var ctime, mtime, etime int64
   395  		err := rows.Scan(&cbRule.ID, &cbRule.Name, &cbRule.Namespace, &cbRule.Enable, &cbRule.Revision,
   396  			&cbRule.Description, &cbRule.Level, &cbRule.SrcService, &cbRule.SrcNamespace, &cbRule.DstService,
   397  			&cbRule.DstNamespace, &cbRule.DstMethod, &cbRule.Rule, &ctime, &mtime, &etime)
   398  		if err != nil {
   399  			log.Errorf("[Store][database] fetch full circuitbreaker rule scan err: %s", err.Error())
   400  			return nil, err
   401  		}
   402  		cbRule.CreateTime = time.Unix(ctime, 0)
   403  		cbRule.ModifyTime = time.Unix(mtime, 0)
   404  		cbRule.EnableTime = time.Unix(etime, 0)
   405  		out = append(out, &cbRule)
   406  	}
   407  	if err := rows.Err(); err != nil {
   408  		log.Errorf("[Store][database] fetch full circuitbreaker rule next err: %s", err.Error())
   409  		return nil, err
   410  	}
   411  	return out, nil
   412  }
   413  
   414  func (c *circuitBreakerStore) getCircuitBreakerRulesCount(filter map[string]string) (uint32, error) {
   415  	queryStr, args := genCircuitBreakerRuleSQL(filter)
   416  	str := countCircuitBreakerRuleSql + queryStr
   417  	var total uint32
   418  	err := c.master.QueryRow(str, args...).Scan(&total)
   419  	switch {
   420  	case err == sql.ErrNoRows:
   421  		return 0, nil
   422  	case err != nil:
   423  		log.Errorf("[Store][database] get circuitbreaker rule count err: %s", err.Error())
   424  		return 0, err
   425  	default:
   426  	}
   427  	return total, nil
   428  }
   429  
   430  // GetCircuitBreakerRulesForCache list circuitbreaker rules by query
   431  func (c *circuitBreakerStore) GetCircuitBreakerRulesForCache(
   432  	mtime time.Time, firstUpdate bool) ([]*model.CircuitBreakerRule, error) {
   433  	str := queryCircuitBreakerRuleCacheSql
   434  	if firstUpdate {
   435  		str += " and flag != 1"
   436  	}
   437  	rows, err := c.slave.Query(str, timeToTimestamp(mtime))
   438  	if err != nil {
   439  		log.Errorf("[Store][database] query circuitbreaker rules with mtime err: %s", err.Error())
   440  		return nil, err
   441  	}
   442  	cbRules, err := fetchCircuitBreakerRuleRows(rows)
   443  	if err != nil {
   444  		return nil, err
   445  	}
   446  	return cbRules, nil
   447  }
   448  
   449  // EnableCircuitBreakerRule enable circuitbreaker rule
   450  func (c *circuitBreakerStore) EnableCircuitBreakerRule(cbRule *model.CircuitBreakerRule) error {
   451  	err := RetryTransaction("enableCircuitbreaker", func() error {
   452  		return c.enableCircuitBreakerRule(cbRule)
   453  	})
   454  
   455  	return store.Error(err)
   456  }
   457  
   458  func (c *circuitBreakerStore) enableCircuitBreakerRule(cbRule *model.CircuitBreakerRule) error {
   459  	return c.master.processWithTransaction(labelEnableCircuitBreakerRule, func(tx *BaseTx) error {
   460  
   461  		etimeStr := buildEtimeStr(cbRule.Enable)
   462  		str := fmt.Sprintf(enableCircuitBreakerRuleSql, etimeStr)
   463  		if _, err := tx.Exec(str, cbRule.Enable, cbRule.Revision, cbRule.ID); err != nil {
   464  			log.Errorf(
   465  				"[Store][database] fail to %s exec sql, err: %s", labelEnableCircuitBreakerRule, err.Error())
   466  			return err
   467  		}
   468  
   469  		if err := tx.Commit(); err != nil {
   470  			log.Errorf("[Store][database] fail to %s commit tx, rule(%+v) commit tx err: %s",
   471  				labelEnableCircuitBreakerRule, cbRule, err.Error())
   472  			return err
   473  		}
   474  		return nil
   475  	})
   476  }