File: ratio.go

package info (click to toggle)
golang-github-vulcand-oxy 2.0.0-3
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 728 kB
  • sloc: makefile: 14
file content (70 lines) | stat: -rw-r--r-- 1,905 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
package cbreaker

import (
	"fmt"
	"time"

	"github.com/vulcand/oxy/v2/internal/holsterv4/clock"
	"github.com/vulcand/oxy/v2/utils"
)

// ratioController allows passing portions traffic back to the endpoints,
// increasing the amount of passed requests using linear function:
//
//	allowedRequestsRatio = 0.5 * (Now() - Start())/Duration
type ratioController struct {
	duration time.Duration
	start    clock.Time
	allowed  int
	denied   int

	log utils.Logger
}

func newRatioController(rampUp time.Duration, log utils.Logger) *ratioController {
	return &ratioController{
		duration: rampUp,
		start:    clock.Now().UTC(),
		log:      log,
	}
}

func (r *ratioController) String() string {
	return fmt.Sprintf("RatioController(target=%f, current=%f, allowed=%d, denied=%d)", r.targetRatio(), r.computeRatio(r.allowed, r.denied), r.allowed, r.denied)
}

func (r *ratioController) allowRequest() bool {
	r.log.Debug("%v", r)
	t := r.targetRatio()
	// This condition answers the question - would we satisfy the target ratio if we allow this request?
	e := r.computeRatio(r.allowed+1, r.denied)
	if e < t {
		r.allowed++
		r.log.Debug("%v allowed", r)
		return true
	}
	r.denied++
	r.log.Debug("%v denied", r)
	return false
}

func (r *ratioController) computeRatio(allowed, denied int) float64 {
	if denied+allowed == 0 {
		return 0
	}
	return float64(allowed) / float64(denied+allowed)
}

func (r *ratioController) targetRatio() float64 {
	// Here's why it's 0.5:
	// We are watching the following ratio:
	// ratio = a / (a + d)
	// We can notice, that once we get to 0.5:
	// 0.5 = a / (a + d)
	// we can evaluate that a = d
	// that means equilibrium, where we would allow all the requests
	// after this point to achieve ratio of 1 (that can never be reached unless d is 0)
	// so we stop from there
	multiplier := 0.5 / float64(r.duration)
	return multiplier * float64(clock.Now().UTC().Sub(r.start))
}