// 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 circuitbreaker implements the circuit breaker pattern, which provides
// stability and prevents cascading failures in distributed systems.
//
// Sentinel circuit breaker module supports three strategies:
//
//  1. SlowRequestRatio: the ratio of slow response time entry(entry's response time is great than max slow response time) exceeds the threshold. The following entry to resource will be broken.
//                       In SlowRequestRatio strategy, user must set max response time.
//  2. ErrorRatio: the ratio of error entry exceeds the threshold. The following entry to resource will be broken.
//  3. ErrorCount: the number of error entry exceeds the threshold. The following entry to resource will be broken.
//
// Sentinel converts each circuit breaking Rule into a CircuitBreaker. Each CircuitBreaker has its own statistical structure.
//
// Sentinel circuit breaker is implemented based on state machines. There are three states:
//
//  1. Closed: all entries could pass checking.
//  2. Open: the circuit breaker is broken, all entries are blocked. After retry timeout, circuit breaker switches state to Half-Open and allows one entry to probe whether the resource returns to its expected state.
//  3. Half-Open: the circuit breaker is in a temporary state of probing, only one entry is allowed to access resource, others are blocked.
//
// Sentinel circuit breaker provides the listener to observe events of state changes.
//
//  type StateChangeListener interface {
//  	OnTransformToClosed(prev State, rule Rule)
//
//  	OnTransformToOpen(prev State, rule Rule, snapshot interface{})
//
//  	OnTransformToHalfOpen(prev State, rule Rule)
//  }
//
// Here is the example code to use circuit breaker:
//
//  type stateChangeTestListener struct {}
//
//  func (s *stateChangeTestListener) OnTransformToClosed(prev circuitbreaker.State, rule circuitbreaker.Rule) {
//  	fmt.Printf("rule.steategy: %+v, From %s to Closed, time: %d\n", rule.Strategy, prev.String(), util.CurrentTimeMillis())
//  }
//
//  func (s *stateChangeTestListener) OnTransformToOpen(prev circuitbreaker.State, rule circuitbreaker.Rule, snapshot interface{}) {
//  	fmt.Printf("rule.steategy: %+v, From %s to Open, snapshot: %.2f, time: %d\n", rule.Strategy, prev.String(), snapshot, util.CurrentTimeMillis())
//  }
//
//  func (s *stateChangeTestListener) OnTransformToHalfOpen(prev circuitbreaker.State, rule circuitbreaker.Rule) {
//  	fmt.Printf("rule.steategy: %+v, From %s to Half-Open, time: %d\n", rule.Strategy, prev.String(), util.CurrentTimeMillis())
//  }
//
//  func main() {
//  	err := sentinel.InitDefault()
//  	if err != nil {
//  		log.Fatal(err)
//  	}
//  	ch := make(chan struct{})
//  	// Register a state change listener so that we could observer the state change of the internal circuit breaker.
//  	circuitbreaker.RegisterStateChangeListeners(&stateChangeTestListener{})
//
//  	_, err = circuitbreaker.LoadRules([]*circuitbreaker.Rule{
//  	// Statistic time span=10s, recoveryTimeout=3s, slowRtUpperBound=50ms, maxSlowRequestRatio=50%
//  		{
//  			Resource:         "abc",
//  			Strategy:         circuitbreaker.SlowRequestRatio,
//  			RetryTimeoutMs:   3000,
//  			MinRequestAmount: 10,
//  			StatIntervalMs:   10000,
//  			MaxAllowedRtMs:   50,
//  			Threshold:        0.5,
//  		},
//  		// Statistic time span=10s, recoveryTimeout=3s, maxErrorRatio=50%
//  		{
//  			Resource:         "abc",
//  			Strategy:         circuitbreaker.ErrorRatio,
//  			RetryTimeoutMs:   3000,
//  			MinRequestAmount: 10,
//  			StatIntervalMs:   10000,
//  			Threshold:        0.5,
//  		},
//  	})
//  	if err != nil {
//  		log.Fatal(err)
//  	}
//
//  	fmt.Println("Sentinel Go circuit breaking demo is running. You may see the pass/block metric in the metric log.")
//  	go func() {
//  		for {
//  			e, b := sentinel.Entry("abc")
//  			if b != nil {
//  				//fmt.Println("g1blocked")
//  				time.Sleep(time.Duration(rand.Uint64()%20) * time.Millisecond)
//  			} else {
//  				if rand.Uint64()%20 > 9 {
//  					// Record current invocation as error.
//  					sentinel.TraceError(e, errors.New("biz error"))
//  				}
//  				//fmt.Println("g1passed")
//  				time.Sleep(time.Duration(rand.Uint64()%80+10) * time.Millisecond)
//  				e.Exit()
//  			}
//  		}
//		}()
//
//  	go func() {
//  		for {
//  			e, b := sentinel.Entry("abc")
//  			if b != nil {
//  				//fmt.Println("g2blocked")
//  				time.Sleep(time.Duration(rand.Uint64()%20) * time.Millisecond)
//  			} else {
//  				//fmt.Println("g2passed")
//  				time.Sleep(time.Duration(rand.Uint64()%80) * time.Millisecond)
//  				e.Exit()
//  			}
//  		}
//  	}()
//  	<-ch
//  }
//
package circuitbreaker
