github.com/polarismesh/polaris@v1.17.8/store/boltdb/faultdetector.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  	"strings"
    23  	"time"
    24  
    25  	bolt "go.etcd.io/bbolt"
    26  
    27  	"github.com/polarismesh/polaris/common/model"
    28  	"github.com/polarismesh/polaris/store"
    29  )
    30  
    31  type faultDetectStore struct {
    32  	handler BoltHandler
    33  }
    34  
    35  const (
    36  	// rule 相关信息以及映射
    37  	tblFaultDetectRule string = "faultdetect_rule"
    38  )
    39  
    40  func initFaultDetectRule(cb *model.FaultDetectRule) {
    41  	cb.Valid = true
    42  	cb.CreateTime = time.Now()
    43  	cb.ModifyTime = time.Now()
    44  }
    45  
    46  // cleanCircuitBreaker 彻底清理熔断规则
    47  func (c *faultDetectStore) cleanFaultDetectRule(id string) error {
    48  	if err := c.handler.DeleteValues(tblFaultDetectRule, []string{id}); err != nil {
    49  		log.Errorf("[Store][fault-detect] clean invalid fault-detect rule(%s) err: %s",
    50  			id, err.Error())
    51  		return store.Error(err)
    52  	}
    53  
    54  	return nil
    55  }
    56  
    57  // CreateFaultDetectRule create fault detect rule
    58  func (c *faultDetectStore) CreateFaultDetectRule(fdRule *model.FaultDetectRule) error {
    59  	dbOp := c.handler
    60  
    61  	initFaultDetectRule(fdRule)
    62  	if err := c.cleanFaultDetectRule(fdRule.ID); err != nil {
    63  		log.Errorf("[Store][fault-detect] clean fault-detect rule(%s) err: %s",
    64  			fdRule.ID, err.Error())
    65  		return store.Error(err)
    66  	}
    67  	if err := dbOp.SaveValue(tblFaultDetectRule, fdRule.ID, fdRule); err != nil {
    68  		log.Errorf("[Store][fault-detect] create fault-detect(%s, %s) err: %s",
    69  			fdRule.ID, fdRule.Name, err.Error())
    70  		return store.Error(err)
    71  	}
    72  
    73  	return nil
    74  }
    75  
    76  // UpdateFaultDetectRule update fault detect rule
    77  func (c *faultDetectStore) UpdateFaultDetectRule(fdRule *model.FaultDetectRule) error {
    78  	dbOp := c.handler
    79  	fdRule.Valid = true
    80  	fdRule.ModifyTime = time.Now()
    81  
    82  	if err := dbOp.SaveValue(tblFaultDetectRule, fdRule.ID, fdRule); err != nil {
    83  		log.Errorf("[Store][fault-detect] update rule(%s) exec err: %s", fdRule.ID, err.Error())
    84  		return store.Error(err)
    85  	}
    86  
    87  	return nil
    88  }
    89  
    90  // DeleteFaultDetectRule delete fault detect rule
    91  func (c *faultDetectStore) DeleteFaultDetectRule(id string) error {
    92  	handler := c.handler
    93  	return handler.Execute(true, func(tx *bolt.Tx) error {
    94  
    95  		properties := make(map[string]interface{})
    96  		properties[CommonFieldValid] = false
    97  		properties[CommonFieldModifyTime] = time.Now()
    98  
    99  		if err := updateValue(tx, tblFaultDetectRule, id, properties); err != nil {
   100  			log.Errorf("[Store][fault-detect] delete rule(%s) err: %s", id, err.Error())
   101  			return err
   102  		}
   103  
   104  		return nil
   105  	})
   106  }
   107  
   108  func (c *faultDetectStore) getFaultDetectRuleWithID(id string) (*model.FaultDetectRule, error) {
   109  	if id == "" {
   110  		return nil, ErrBadParam
   111  	}
   112  
   113  	handler := c.handler
   114  	result, err := handler.LoadValues(tblFaultDetectRule, []string{id}, &model.FaultDetectRule{})
   115  
   116  	if err != nil {
   117  		log.Errorf("[Store][fault-detect] get rule fail : %s", err.Error())
   118  		return nil, err
   119  	}
   120  
   121  	if len(result) > 1 {
   122  		return nil, ErrMultipleResult
   123  	}
   124  
   125  	if len(result) == 0 {
   126  		return nil, nil
   127  	}
   128  
   129  	cbRule := result[id].(*model.FaultDetectRule)
   130  	if cbRule.Valid {
   131  		return cbRule, nil
   132  	}
   133  
   134  	return nil, nil
   135  }
   136  
   137  // HasFaultDetectRule check fault detect rule exists
   138  func (c *faultDetectStore) HasFaultDetectRule(id string) (bool, error) {
   139  	cbRule, err := c.getFaultDetectRuleWithID(id)
   140  	if nil != err {
   141  		return false, err
   142  	}
   143  	return cbRule != nil, nil
   144  }
   145  
   146  // HasFaultDetectRuleByName check fault detect rule exists by name
   147  func (c *faultDetectStore) HasFaultDetectRuleByName(name string, namespace string) (bool, error) {
   148  	filter := map[string]string{
   149  		exactName:   name,
   150  		"namespace": namespace,
   151  	}
   152  	total, _, err := c.GetFaultDetectRules(filter, 0, 10)
   153  	if nil != err {
   154  		return false, err
   155  	}
   156  	return total > 0, nil
   157  }
   158  
   159  // HasFaultDetectRuleByNameExcludeId check fault detect rule exists by name not this id
   160  func (c *faultDetectStore) HasFaultDetectRuleByNameExcludeId(name string, namespace string, id string) (bool, error) {
   161  	filter := map[string]string{
   162  		exactName:   name,
   163  		"namespace": namespace,
   164  		excludeId:   id,
   165  	}
   166  	total, _, err := c.GetFaultDetectRules(filter, 0, 10)
   167  	if nil != err {
   168  		return false, err
   169  	}
   170  	return total > 0, nil
   171  }
   172  
   173  const (
   174  	fdFieldDstService   = "DstService"
   175  	fdFieldDstNamespace = "DstNamespace"
   176  	fdFieldDstMethod    = "DstMethod"
   177  )
   178  
   179  var (
   180  	fdSearchFields = []string{
   181  		CommonFieldID, CommonFieldName, CommonFieldNamespace, CommonFieldDescription, fdFieldDstService,
   182  		fdFieldDstNamespace, fdFieldDstMethod, CommonFieldEnable, CommonFieldValid,
   183  	}
   184  	fdBlurSearchFields = map[string]bool{
   185  		CommonFieldName:        true,
   186  		CommonFieldDescription: true,
   187  		fdFieldDstService:      true,
   188  		fdFieldDstNamespace:    true,
   189  		fdFieldDstMethod:       true,
   190  	}
   191  )
   192  
   193  // GetFaultDetectRules get all circuitbreaker rules by query and limit
   194  func (c *faultDetectStore) GetFaultDetectRules(
   195  	filter map[string]string, offset uint32, limit uint32) (uint32, []*model.FaultDetectRule, error) {
   196  	svc, hasSvc := filter[svcSpecificQueryKeyService]
   197  	delete(filter, svcSpecificQueryKeyService)
   198  	svcNs, hasSvcNs := filter[svcSpecificQueryKeyNamespace]
   199  	delete(filter, svcSpecificQueryKeyNamespace)
   200  	exactNameValue, hasExactName := filter[exactName]
   201  	delete(filter, exactName)
   202  	excludeIdValue, hasExcludeId := filter[excludeId]
   203  	delete(filter, excludeId)
   204  	delete(filter, "brief")
   205  	result, err := c.handler.LoadValuesByFilter(tblFaultDetectRule, fdSearchFields, &model.FaultDetectRule{},
   206  		func(m map[string]interface{}) bool {
   207  			validVal, ok := m[CommonFieldValid]
   208  			if ok && !validVal.(bool) {
   209  				return false
   210  			}
   211  			if hasSvc && hasSvcNs {
   212  				dstServiceValue := m[fdFieldDstService]
   213  				dstNamespaceValue := m[fdFieldDstNamespace]
   214  				if !(dstServiceValue == svc && dstNamespaceValue == svcNs) {
   215  					return false
   216  				}
   217  			}
   218  			if hasExactName {
   219  				if exactNameValue != m[CommonFieldName] {
   220  					return false
   221  				}
   222  			}
   223  			if hasExcludeId {
   224  				if excludeIdValue == m[CBFieldNameID] {
   225  					return false
   226  				}
   227  			}
   228  			if len(filter) == 0 {
   229  				return true
   230  			}
   231  			var matched = true
   232  			for fieldKey, fieldValue := range m {
   233  				lowerKey := strings.ToLower(fieldKey)
   234  				filterValue, ok := filter[lowerKey]
   235  				if !ok {
   236  					continue
   237  				}
   238  				_, isBlur := fdBlurSearchFields[fieldKey]
   239  				if isBlur {
   240  					if !strings.Contains(fieldValue.(string), filterValue) {
   241  						matched = false
   242  						break
   243  					}
   244  				} else {
   245  					if filterValue != fieldValue.(string) {
   246  						matched = false
   247  						break
   248  					}
   249  				}
   250  			}
   251  			return matched
   252  		})
   253  	if nil != err {
   254  		return 0, nil, err
   255  	}
   256  	out := make([]*model.FaultDetectRule, 0, len(result))
   257  	for _, value := range result {
   258  		out = append(out, value.(*model.FaultDetectRule))
   259  	}
   260  	return uint32(len(out)), sublistFaultDetectRules(out, offset, limit), nil
   261  }
   262  
   263  func sublistFaultDetectRules(cbRules []*model.FaultDetectRule, offset, limit uint32) []*model.FaultDetectRule {
   264  	beginIndex := offset
   265  	endIndex := beginIndex + limit
   266  	totalCount := uint32(len(cbRules))
   267  	// handle invalid offset, limit
   268  	if totalCount == 0 {
   269  		return cbRules
   270  	}
   271  	if beginIndex >= endIndex {
   272  		return cbRules
   273  	}
   274  	if beginIndex >= totalCount {
   275  		return cbRules
   276  	}
   277  	if endIndex > totalCount {
   278  		endIndex = totalCount
   279  	}
   280  
   281  	sort.Slice(cbRules, func(i, j int) bool {
   282  		// sort by modify time
   283  		if cbRules[i].ModifyTime.After(cbRules[j].ModifyTime) {
   284  			return true
   285  		} else if cbRules[i].ModifyTime.Before(cbRules[j].ModifyTime) {
   286  			return false
   287  		} else {
   288  			return strings.Compare(cbRules[i].ID, cbRules[j].ID) < 0
   289  		}
   290  	})
   291  
   292  	return cbRules[beginIndex:endIndex]
   293  }
   294  
   295  // GetFaultDetectRulesForCache get increment circuitbreaker rules
   296  func (c *faultDetectStore) GetFaultDetectRulesForCache(
   297  	mtime time.Time, firstUpdate bool) ([]*model.FaultDetectRule, error) {
   298  	handler := c.handler
   299  
   300  	if firstUpdate {
   301  		mtime = time.Time{}
   302  	}
   303  
   304  	results, err := handler.LoadValuesByFilter(
   305  		tblFaultDetectRule, []string{CommonFieldModifyTime}, &model.FaultDetectRule{},
   306  		func(m map[string]interface{}) bool {
   307  			mt := m[CommonFieldModifyTime].(time.Time)
   308  			isAfter := !mt.Before(mtime)
   309  			return isAfter
   310  		})
   311  
   312  	if err != nil {
   313  		return nil, err
   314  	}
   315  
   316  	if len(results) == 0 {
   317  		return []*model.FaultDetectRule{}, nil
   318  	}
   319  
   320  	out := make([]*model.FaultDetectRule, 0, len(results))
   321  	for _, value := range results {
   322  		out = append(out, value.(*model.FaultDetectRule))
   323  	}
   324  
   325  	return out, nil
   326  }