repo_description: Sentinel Go enables reliability and resiliency for Go microservices
repo_topics: cloud-native go golang microservice microservices middleware rate-limiting resiliency
code: // Copyright 1999-2020 Alibaba Group Holding Ltd.
  //
  // Licensed under the Apache License, Version 2.0 (the "License");
  // you may not use this file except in compliance with the License.
  // You may obtain a copy of the License at
  //
  //     http://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing, software
  // distributed under the License is distributed on an "AS IS" BASIS,
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  // See the License for the specific language governing permissions and
  // limitations under the License.
  
  package flow
  
  import (
  	"encoding/json"
  	"fmt"
  
  	"github.com/alibaba/sentinel-golang/util"
  )
  
  // RelationStrategy indicates the flow control strategy based on the relation of invocations.
  type RelationStrategy int32
  
  const (
  	// CurrentResource means flow control by current resource directly.
  	CurrentResource RelationStrategy = iota
  	// AssociatedResource means flow control by the associated resource rather than current resource.
  	AssociatedResource
  )
  
  func (s RelationStrategy) String() string {
  	switch s {
  	case CurrentResource:
  		return "CurrentResource"
  	case AssociatedResource:
  		return "AssociatedResource"
  	default:
  		return "Undefined"
  	}
  }
  
  type TokenCalculateStrategy int32
  
  const (
  	Direct TokenCalculateStrategy = iota
  	WarmUp
  	MemoryAdaptive
  )
  
  func (s TokenCalculateStrategy) String() string {
  	switch s {
  	case Direct:
  		return "Direct"
  	case WarmUp:
  		return "WarmUp"
  	case MemoryAdaptive:
  		return "MemoryAdaptive"
  	default:
  		return "Undefined"
  	}
  }
  
  // ControlBehavior defines the behavior when requests have reached the capacity of the resource.
  type ControlBehavior int32
  
  const (
  	Reject ControlBehavior = iota
  	// Throttling indicates that pending requests will be throttled, wait in queue (until free capacity is available)
  	Throttling
  )
  
  func (s ControlBehavior) String() string {
  	switch s {
  	case Reject:
  		return "Reject"
  	case Throttling:
  		return "Throttling"
  	default:
  		return "Undefined"
  	}
  }
  
  // Rule describes the strategy of flow control, the flow control strategy is based on QPS statistic metric
  type Rule struct {
  	// ID represents the unique ID of the rule (optional).
  	ID string `json:"id,omitempty"`
  	// Resource represents the resource name.
  	Resource               string                 `json:"resource"`
  	TokenCalculateStrategy TokenCalculateStrategy `json:"tokenCalculateStrategy"`
  	ControlBehavior        ControlBehavior        `json:"controlBehavior"`
  	// Threshold means the threshold during StatIntervalInMs
  	// If StatIntervalInMs is 1000(1 second), Threshold means QPS
  	Threshold        float64          `json:"threshold"`
  	RelationStrategy RelationStrategy `json:"relationStrategy"`
  	RefResource      string           `json:"refResource"`
  	// MaxQueueingTimeMs only takes effect when ControlBehavior is Throttling.
  	// When MaxQueueingTimeMs is 0, it means Throttling only controls interval of requests,
  	// and requests exceeding the threshold will be rejected directly.
  	MaxQueueingTimeMs uint32 `json:"maxQueueingTimeMs"`
  	WarmUpPeriodSec   uint32 `json:"warmUpPeriodSec"`
  	WarmUpColdFactor  uint32 `json:"warmUpColdFactor"`
  	// StatIntervalInMs indicates the statistic interval and it's the optional setting for flow Rule.
  	// If user doesn't set StatIntervalInMs, that means using default metric statistic of resource.
  	// If the StatIntervalInMs user specifies can not reuse the global statistic of resource,
  	// 		sentinel will generate independent statistic structure for this rule.
  	StatIntervalInMs uint32 `json:"statIntervalInMs"`
  
  	// adaptive flow control algorithm related parameters
  	// limitation: LowMemUsageThreshold > HighMemUsageThreshold && MemHighWaterMarkBytes > MemLowWaterMarkBytes
  	// if the current memory usage is less than or equals to MemLowWaterMarkBytes, threshold == LowMemUsageThreshold
  	// if the current memory usage is more than or equals to MemHighWaterMarkBytes, threshold == HighMemUsageThreshold
  	// if  the current memory usage is in (MemLowWaterMarkBytes, MemHighWaterMarkBytes), threshold is in (HighMemUsageThreshold, LowMemUsageThreshold)
  	LowMemUsageThreshold  int64 `json:"lowMemUsageThreshold"`
  	HighMemUsageThreshold int64 `json:"highMemUsageThreshold"`
  	MemLowWaterMarkBytes  int64 `json:"memLowWaterMarkBytes"`
  	MemHighWaterMarkBytes int64 `json:"memHighWaterMarkBytes"`
  	// Regex indicates whether the rule is a regex rule
  	Regex bool `json:"regex"`
  }
  
  func (r *Rule) isEqualsTo(newRule *Rule) bool {
  	if newRule == nil {
  		return false
  	}
  	if !(r.Resource == newRule.Resource && r.RelationStrategy == newRule.RelationStrategy &&
  		r.RefResource == newRule.RefResource && r.StatIntervalInMs == newRule.StatIntervalInMs &&
  		r.TokenCalculateStrategy == newRule.TokenCalculateStrategy && r.ControlBehavior == newRule.ControlBehavior &&
  		util.Float64Equals(r.Threshold, newRule.Threshold) &&
  		r.MaxQueueingTimeMs == newRule.MaxQueueingTimeMs && r.WarmUpPeriodSec == newRule.WarmUpPeriodSec &&
  		r.WarmUpColdFactor == newRule.WarmUpColdFactor &&
  		r.LowMemUsageThreshold == newRule.LowMemUsageThreshold && r.HighMemUsageThreshold == newRule.HighMemUsageThreshold &&
  		r.MemLowWaterMarkBytes == newRule.MemLowWaterMarkBytes && r.MemHighWaterMarkBytes == newRule.MemHighWaterMarkBytes) {
  
  		return false
  	}
  	return true
  }
  
  func (r *Rule) isStatReusable(newRule *Rule) bool {
  	if newRule == nil {
  		return false
  	}
  	return r.Resource == newRule.Resource && r.RelationStrategy == newRule.RelationStrategy &&
  		r.RefResource == newRule.RefResource && r.StatIntervalInMs == newRule.StatIntervalInMs &&
  		r.needStatistic() && newRule.needStatistic()
  }
  
  func (r *Rule) needStatistic() bool {
  	return r.TokenCalculateStrategy == WarmUp || r.ControlBehavior == Reject
  }
  
  func (r *Rule) String() string {
  	b, err := json.Marshal(r)
  	if err != nil {
  		// Return the fallback string
  		return fmt.Sprintf("Rule{Resource=%s, TokenCalculateStrategy=%s, ControlBehavior=%s, "+
  			"Threshold=%.2f, RelationStrategy=%s, RefResource=%s, MaxQueueingTimeMs=%d, WarmUpPeriodSec=%d, WarmUpColdFactor=%d, StatIntervalInMs=%d, "+
  			"LowMemUsageThreshold=%v, HighMemUsageThreshold=%v, MemLowWaterMarkBytes=%v, MemHighWaterMarkBytes=%v}",
  			r.Resource, r.TokenCalculateStrategy, r.ControlBehavior, r.Threshold, r.RelationStrategy, r.RefResource,
  			r.MaxQueueingTimeMs, r.WarmUpPeriodSec, r.WarmUpColdFactor, r.StatIntervalInMs,
  			r.LowMemUsageThreshold, r.HighMemUsageThreshold, r.MemLowWaterMarkBytes, r.MemHighWaterMarkBytes)
  	}
  	return string(b)
  }
  
  func (r *Rule) ResourceName() string {
  	return r.Resource
  }
  