github.com/polarismesh/polaris@v1.17.8/store/boltdb/circuitbreaker_rule.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 boltdb
    19  
    20  import (
    21  	"sort"
    22  	"strconv"
    23  	"strings"
    24  	"time"
    25  
    26  	bolt "go.etcd.io/bbolt"
    27  
    28  	"github.com/polarismesh/polaris/common/model"
    29  	"github.com/polarismesh/polaris/store"
    30  )
    31  
    32  const (
    33  	// rule 相关信息以及映射
    34  	tblCircuitBreakerRule string = "circuitbreaker_rule_v2"
    35  )
    36  
    37  const (
    38  	CbFieldLevel        = "Level"
    39  	CbFieldSrcService   = "SrcService"
    40  	CbFieldSrcNamespace = "SrcNamespace"
    41  	CbFieldDstService   = "DstService"
    42  	CbFieldDstNamespace = "DstNamespace"
    43  	CbFieldDstMethod    = "DstMethod"
    44  	CbFieldRule         = "Rule"
    45  )
    46  
    47  const (
    48  	// rule 相关信息以及映射
    49  	tblCircuitBreaker string = "circuitbreaker_rule"
    50  
    51  	// relation 相关信息以及映射信息
    52  	tblCircuitBreakerRelation string = "circuitbreaker_rule_relation"
    53  	VersionForMaster          string = "master"
    54  	CBFieldNameValid          string = "Valid"
    55  	CBFieldNameVersion        string = "Version"
    56  	CBFieldNameID             string = "ID"
    57  	CBFieldNameModifyTime     string = "ModifyTime"
    58  
    59  	CBRFieldNameServiceID   string = "ServiceID"
    60  	CBRFieldNameRuleID      string = "RuleID"
    61  	CBRFieldNameRuleVersion string = "RuleVersion"
    62  
    63  	CBRelationFieldServiceID   string = "ServiceID"
    64  	CBRelationFieldRuleID      string = "RuleID"
    65  	CBRelationFieldRuleVersion string = "RuleVersion"
    66  	CBRelationFieldValid       string = "Valid"
    67  	CBRelationFieldCreateTime  string = "CreateTime"
    68  	CBRelationFieldModifyTime  string = "ModifyTime"
    69  )
    70  
    71  type circuitBreakerStore struct {
    72  	handler BoltHandler
    73  }
    74  
    75  func initCircuitBreakerRule(cb *model.CircuitBreakerRule) {
    76  	cb.Valid = true
    77  	cb.CreateTime = time.Now()
    78  	cb.ModifyTime = time.Now()
    79  }
    80  
    81  // cleanCircuitBreaker 彻底清理熔断规则
    82  func (c *circuitBreakerStore) cleanCircuitBreakerRule(id string) error {
    83  	if err := c.handler.DeleteValues(tblCircuitBreakerRule, []string{id}); err != nil {
    84  		log.Errorf("[Store][circuitBreaker] clean invalid circuit-breaker rule(%s) err: %s",
    85  			id, err.Error())
    86  		return store.Error(err)
    87  	}
    88  
    89  	return nil
    90  }
    91  
    92  // CreateCircuitBreakerRule create general circuitbreaker rule
    93  func (c *circuitBreakerStore) CreateCircuitBreakerRule(cbRule *model.CircuitBreakerRule) error {
    94  	dbOp := c.handler
    95  
    96  	initCircuitBreakerRule(cbRule)
    97  	if err := c.cleanCircuitBreakerRule(cbRule.ID); err != nil {
    98  		log.Errorf("[Store][circuitBreaker] clean circuit breaker rule(%s) err: %s",
    99  			cbRule.ID, err.Error())
   100  		return store.Error(err)
   101  	}
   102  	if err := dbOp.SaveValue(tblCircuitBreakerRule, cbRule.ID, cbRule); err != nil {
   103  		log.Errorf("[Store][circuitBreaker] create circuit breaker(%s, %s) err: %s",
   104  			cbRule.ID, cbRule.Name, err.Error())
   105  		return store.Error(err)
   106  	}
   107  
   108  	return nil
   109  }
   110  
   111  // UpdateCircuitBreakerRule update general circuitbreaker rule
   112  func (c *circuitBreakerStore) UpdateCircuitBreakerRule(cbRule *model.CircuitBreakerRule) error {
   113  	dbOp := c.handler
   114  	properties := map[string]interface{}{
   115  		CommonFieldName:        cbRule.Name,
   116  		CommonFieldNamespace:   cbRule.Namespace,
   117  		CommonFieldRevision:    cbRule.Revision,
   118  		CommonFieldDescription: cbRule.Description,
   119  		CommonFieldModifyTime:  time.Now(),
   120  		CbFieldLevel:           cbRule.Level,
   121  		CbFieldSrcService:      cbRule.SrcService,
   122  		CbFieldSrcNamespace:    cbRule.SrcNamespace,
   123  		CbFieldDstService:      cbRule.DstService,
   124  		CbFieldDstNamespace:    cbRule.DstNamespace,
   125  		CbFieldDstMethod:       cbRule.DstMethod,
   126  		CbFieldRule:            cbRule.Rule,
   127  	}
   128  	if err := dbOp.UpdateValue(tblCircuitBreakerRule, cbRule.ID, properties); err != nil {
   129  		log.Errorf("[Store][CircuitBreaker] update rule(%s) exec err: %s", cbRule.ID, err.Error())
   130  		return store.Error(err)
   131  	}
   132  	return nil
   133  }
   134  
   135  // DeleteCircuitBreakerRule delete general circuitbreaker rule
   136  func (c *circuitBreakerStore) DeleteCircuitBreakerRule(id string) error {
   137  	handler := c.handler
   138  	return handler.Execute(true, func(tx *bolt.Tx) error {
   139  
   140  		properties := make(map[string]interface{})
   141  		properties[CommonFieldValid] = false
   142  		properties[CommonFieldModifyTime] = time.Now()
   143  
   144  		if err := updateValue(tx, tblCircuitBreakerRule, id, properties); err != nil {
   145  			log.Errorf("[Store][CircuitBreaker] delete rule(%s) err: %s", id, err.Error())
   146  			return err
   147  		}
   148  
   149  		return nil
   150  	})
   151  }
   152  
   153  // getCircuitBreakerRuleWithID 根据规则ID拉取熔断规则
   154  func (c *circuitBreakerStore) getCircuitBreakerRuleWithID(id string) (*model.CircuitBreakerRule, error) {
   155  	if id == "" {
   156  		return nil, ErrBadParam
   157  	}
   158  
   159  	handler := c.handler
   160  	result, err := handler.LoadValues(tblCircuitBreakerRule, []string{id}, &model.CircuitBreakerRule{})
   161  
   162  	if err != nil {
   163  		log.Errorf("[Store][boltdb] get rate limit fail : %s", err.Error())
   164  		return nil, err
   165  	}
   166  
   167  	if len(result) > 1 {
   168  		return nil, ErrMultipleResult
   169  	}
   170  
   171  	if len(result) == 0 {
   172  		return nil, nil
   173  	}
   174  
   175  	cbRule := result[id].(*model.CircuitBreakerRule)
   176  	if cbRule.Valid {
   177  		return cbRule, nil
   178  	}
   179  
   180  	return nil, nil
   181  }
   182  
   183  // HasCircuitBreakerRule check circuitbreaker rule exists
   184  func (c *circuitBreakerStore) HasCircuitBreakerRule(id string) (bool, error) {
   185  	cbRule, err := c.getCircuitBreakerRuleWithID(id)
   186  	if nil != err {
   187  		return false, err
   188  	}
   189  	return cbRule != nil, nil
   190  }
   191  
   192  // HasCircuitBreakerRuleByName check circuitbreaker rule exists for name
   193  func (c *circuitBreakerStore) HasCircuitBreakerRuleByName(name string, namespace string) (bool, error) {
   194  	filter := map[string]string{
   195  		exactName:   name,
   196  		"namespace": namespace,
   197  	}
   198  	total, _, err := c.GetCircuitBreakerRules(filter, 0, 10)
   199  	if nil != err {
   200  		return false, err
   201  	}
   202  	return total > 0, nil
   203  }
   204  
   205  // HasCircuitBreakerRuleByNameExcludeId check circuitbreaker rule exists for name not this id
   206  func (c *circuitBreakerStore) HasCircuitBreakerRuleByNameExcludeId(
   207  	name string, namespace string, id string) (bool, error) {
   208  	filter := map[string]string{
   209  		exactName:   name,
   210  		"namespace": namespace,
   211  		excludeId:   id,
   212  	}
   213  	total, _, err := c.GetCircuitBreakerRules(filter, 0, 10)
   214  	if nil != err {
   215  		return false, err
   216  	}
   217  	return total > 0, nil
   218  }
   219  
   220  var (
   221  	cbSearchFields = []string{CommonFieldID, CommonFieldName, CommonFieldNamespace, CommonFieldDescription,
   222  		CbFieldLevel, CbFieldSrcService, CbFieldSrcNamespace, CbFieldDstService, CbFieldDstNamespace,
   223  		CbFieldDstMethod, CommonFieldEnable, CommonFieldValid,
   224  	}
   225  	cbBlurSearchFields = map[string]bool{
   226  		CommonFieldName:        true,
   227  		CommonFieldDescription: true,
   228  		CbFieldSrcService:      true,
   229  		CbFieldDstService:      true,
   230  		CbFieldDstMethod:       true,
   231  	}
   232  )
   233  
   234  // GetCircuitBreakerRules get all circuitbreaker rules by query and limit
   235  func (c *circuitBreakerStore) GetCircuitBreakerRules(
   236  	filter map[string]string, offset uint32, limit uint32) (uint32, []*model.CircuitBreakerRule, error) {
   237  	svc, hasSvc := filter[svcSpecificQueryKeyService]
   238  	delete(filter, svcSpecificQueryKeyService)
   239  	svcNs, hasSvcNs := filter[svcSpecificQueryKeyNamespace]
   240  	delete(filter, svcSpecificQueryKeyNamespace)
   241  	exactNameValue, hasExactName := filter[exactName]
   242  	delete(filter, exactName)
   243  	excludeIdValue, hasExcludeId := filter[excludeId]
   244  	delete(filter, excludeId)
   245  	delete(filter, "brief")
   246  	lowerFilter := make(map[string]string, len(filter))
   247  	for k, v := range filter {
   248  		lowerFilter[strings.ToLower(k)] = v
   249  	}
   250  	result, err := c.handler.LoadValuesByFilter(tblCircuitBreakerRule, cbSearchFields, &model.CircuitBreakerRule{},
   251  		func(m map[string]interface{}) bool {
   252  			validVal, ok := m[CommonFieldValid]
   253  			if ok && !validVal.(bool) {
   254  				return false
   255  			}
   256  			if hasSvc && hasSvcNs {
   257  				srcSvcValue := m[CbFieldSrcService]
   258  				srcNsValue := m[CbFieldSrcNamespace]
   259  				dstSvcValue := m[CbFieldDstService]
   260  				dstNsValue := m[CbFieldDstNamespace]
   261  				if !((srcSvcValue == svc && srcNsValue == svcNs) || (dstSvcValue == svc && dstNsValue == svcNs)) {
   262  					return false
   263  				}
   264  			}
   265  			if hasExactName {
   266  				if exactNameValue != m[CommonFieldName] {
   267  					return false
   268  				}
   269  			}
   270  			if hasExcludeId {
   271  				if excludeIdValue == m[CBFieldNameID] {
   272  					return false
   273  				}
   274  			}
   275  			if len(lowerFilter) == 0 {
   276  				return true
   277  			}
   278  			var matched = true
   279  			for fieldKey, fieldValue := range m {
   280  				lowerKey := strings.ToLower(fieldKey)
   281  				filterValue, ok := lowerFilter[lowerKey]
   282  				if !ok {
   283  					continue
   284  				}
   285  				_, isBlur := cbBlurSearchFields[fieldKey]
   286  				if isBlur {
   287  					if !strings.Contains(fieldValue.(string), filterValue) {
   288  						matched = false
   289  						break
   290  					}
   291  				} else if fieldKey == CommonFieldEnable {
   292  					filterEnable, _ := strconv.ParseBool(filterValue)
   293  					if filterEnable != fieldValue.(bool) {
   294  						matched = false
   295  						break
   296  					}
   297  				} else if fieldKey == CbFieldLevel {
   298  					levels := strings.Split(filterValue, ",")
   299  					var inLevel = false
   300  					for _, level := range levels {
   301  						levelInt, _ := strconv.Atoi(level)
   302  						if int64(levelInt) == fieldValue.(int64) {
   303  							inLevel = true
   304  							break
   305  						}
   306  					}
   307  					if !inLevel {
   308  						matched = false
   309  						break
   310  					}
   311  				} else {
   312  					if filterValue != fieldValue.(string) {
   313  						matched = false
   314  						break
   315  					}
   316  				}
   317  			}
   318  			return matched
   319  		})
   320  	if nil != err {
   321  		return 0, nil, err
   322  	}
   323  	out := make([]*model.CircuitBreakerRule, 0, len(result))
   324  	for _, value := range result {
   325  		out = append(out, value.(*model.CircuitBreakerRule))
   326  	}
   327  	return uint32(len(out)), sublistCircuitBreakerRules(out, offset, limit), nil
   328  }
   329  
   330  func sublistCircuitBreakerRules(cbRules []*model.CircuitBreakerRule, offset, limit uint32) []*model.CircuitBreakerRule {
   331  	beginIndex := offset
   332  	endIndex := beginIndex + limit
   333  	totalCount := uint32(len(cbRules))
   334  	// handle invalid offset, limit
   335  	if totalCount == 0 {
   336  		return cbRules
   337  	}
   338  	if beginIndex >= endIndex {
   339  		return cbRules
   340  	}
   341  	if beginIndex >= totalCount {
   342  		return cbRules
   343  	}
   344  	if endIndex > totalCount {
   345  		endIndex = totalCount
   346  	}
   347  
   348  	sort.Slice(cbRules, func(i, j int) bool {
   349  		// sort by modify time
   350  		if cbRules[i].ModifyTime.After(cbRules[j].ModifyTime) {
   351  			return true
   352  		} else if cbRules[i].ModifyTime.Before(cbRules[j].ModifyTime) {
   353  			return false
   354  		} else {
   355  			return strings.Compare(cbRules[i].ID, cbRules[j].ID) < 0
   356  		}
   357  	})
   358  
   359  	return cbRules[beginIndex:endIndex]
   360  }
   361  
   362  // GetCircuitBreakerRulesForCache get increment circuitbreaker rules
   363  func (c *circuitBreakerStore) GetCircuitBreakerRulesForCache(
   364  	mtime time.Time, firstUpdate bool) ([]*model.CircuitBreakerRule, error) {
   365  	handler := c.handler
   366  
   367  	if firstUpdate {
   368  		mtime = time.Time{}
   369  	}
   370  
   371  	results, err := handler.LoadValuesByFilter(
   372  		tblCircuitBreakerRule, []string{CommonFieldModifyTime}, &model.CircuitBreakerRule{},
   373  		func(m map[string]interface{}) bool {
   374  			mt := m[CommonFieldModifyTime].(time.Time)
   375  			isAfter := !mt.Before(mtime)
   376  			return isAfter
   377  		})
   378  
   379  	if err != nil {
   380  		return nil, err
   381  	}
   382  
   383  	if len(results) == 0 {
   384  		return []*model.CircuitBreakerRule{}, nil
   385  	}
   386  
   387  	out := make([]*model.CircuitBreakerRule, 0, len(results))
   388  	for _, value := range results {
   389  		out = append(out, value.(*model.CircuitBreakerRule))
   390  	}
   391  
   392  	return out, nil
   393  }
   394  
   395  // EnableCircuitBreakerRule enable specific circuitbreaker rule
   396  func (c *circuitBreakerStore) EnableCircuitBreakerRule(cbRule *model.CircuitBreakerRule) error {
   397  	handler := c.handler
   398  	return handler.Execute(true, func(tx *bolt.Tx) error {
   399  		properties := make(map[string]interface{})
   400  		properties[CommonFieldEnable] = cbRule.Enable
   401  		properties[CommonFieldRevision] = cbRule.Revision
   402  		properties[CommonFieldModifyTime] = time.Now()
   403  		if cbRule.Enable {
   404  			properties[CommonFieldEnableTime] = time.Now()
   405  		} else {
   406  			properties[CommonFieldEnableTime] = time.Unix(0, 0)
   407  		}
   408  		// create ratelimit_config
   409  		if err := updateValue(tx, tblCircuitBreakerRule, cbRule.ID, properties); err != nil {
   410  			log.Errorf("[Store][RateLimit] update circuitbreaker rule(%s) err: %s",
   411  				cbRule.ID, err.Error())
   412  			return err
   413  		}
   414  		return nil
   415  	})
   416  }