Ctrl K
ComunidadGuías…

model/sli.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package model

import (
	"fmt"

	"github.com/coroot/coroot/timeseries"
	"github.com/coroot/coroot/utils"
	"github.com/dustin/go-humanize"
)

var (
	DefaultHistogramBuckets = []float32{.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10}
	LLMHistogramBuckets     = []float32{.1, .25, .5, 1, 2.5, 5, 10, 20, 30, 60, 120, 300, 600}
)

func RoundUpToDefaultBucket(threshold float32) float32 {
	return RoundUpToBucket(threshold, DefaultHistogramBuckets)
}

func RoundUpToBucket(threshold float32, buckets []float32) float32 {
	for _, b := range buckets {
		if b >= threshold {
			return b
		}
	}
	return threshold
}

type AvailabilitySLI struct {
	Config CheckConfigSLOAvailability

	TotalRequests  *timeseries.TimeSeries
	FailedRequests *timeseries.TimeSeries

	TotalRequestsRaw  *timeseries.TimeSeries
	FailedRequestsRaw *timeseries.TimeSeries
}

type HistogramBucket struct {
	Le         float32
	TimeSeries *timeseries.TimeSeries
}

type LatencySLI struct {
	Config CheckConfigSLOLatency

	Histogram    []HistogramBucket
	HistogramRaw []HistogramBucket
}

func (sli *LatencySLI) GetTotalAndFast(raw bool) (*timeseries.TimeSeries, *timeseries.TimeSeries) {
	var total, fast *timeseries.TimeSeries
	histogram := sli.Histogram
	if raw {
		histogram = sli.HistogramRaw
	}
	for _, b := range histogram {
		if b.Le <= sli.Config.ObjectiveBucket {
			fast = b.TimeSeries
		}
		if timeseries.IsInf(b.Le, 1) {
			total = b.TimeSeries
		}
	}
	return total, fast
}

func HistogramSeries(buckets []HistogramBucket, objectiveBucket, objectivePercentage float32) []Series {
	res := make([]Series, 0, len(buckets))
	var from, to float32
	thresholdIdx := -1
	for i, b := range buckets {
		var h Series
		to = b.Le
		if i == 0 {
			from = 0
			h.Data = b.TimeSeries
		} else {
			from = buckets[i-1].Le
			h.Data = timeseries.Sub(b.TimeSeries, buckets[i-1].TimeSeries).MapInPlace(func(t timeseries.Time, v float32) float32 {
				if v < 0 {
					v = 0.
				}
				return v
			})
		}
		h.Color = "green"
		if objectiveBucket > 0 && objectivePercentage > 0 {
			if to > objectiveBucket {
				h.Color = "red"
			} else {
				thresholdIdx = i
			}
		}
		h.Value = fmt.Sprint(to)
		switch {
		case timeseries.IsInf(to, 0):
			h.Name = fmt.Sprintf(">%ss", humanize.Ftoa(float64(from)))
			h.Title = fmt.Sprintf(">%s s", humanize.Ftoa(float64(from)))
			h.Value = "inf"
		case from < 0.1:
			h.Name = fmt.Sprintf("%.0fms", to*1000)
			h.Title = fmt.Sprintf("%.0f-%.0f ms", from*1000, to*1000)
		default:
			h.Name = humanize.Ftoa(float64(to)) + "s"
			h.Title = fmt.Sprintf("%s-%s s", humanize.Ftoa(float64(from)), humanize.Ftoa(float64(to)))
		}
		res = append(res, h)
	}
	if thresholdIdx > -1 {
		res[thresholdIdx].Threshold = fmt.Sprintf(
			"<b>Latency objective</b><br> %s of requests should be served faster than %s",
			utils.FormatPercentage(objectivePercentage), utils.FormatLatency(objectiveBucket))
	}
	return res
}

func Quantile(histogram []HistogramBucket, q float32) *timeseries.TimeSeries {
	if len(histogram) == 0 {
		return nil
	}
	total := histogram[len(histogram)-1]
	type bucket struct {
		iter *timeseries.Iterator
		le   float32
	}
	var buckets []bucket
	for _, b := range histogram {
		buckets = append(buckets, bucket{
			iter: b.TimeSeries.Iter(),
			le:   b.Le,
		})
	}
	res := make([]float32, total.TimeSeries.Len())
	idx := 0
	totalIter := total.TimeSeries.Iter()
	var t, c, rank float32
	var i int
	var b bucket
	for totalIter.Next() {
		_, t = totalIter.Value()
		rank = t * q
		for _, b = range buckets {
			b.iter.Next()
		}
		var prev, lower, upper, bc float32
		for i, b = range buckets {
			upper = b.le
			if i > 0 {
				_, prev = buckets[i-1].iter.Value()
				lower = buckets[i-1].le
			}
			_, c = b.iter.Value()
			if timeseries.IsNaN(c) {
				c = 0.
			}
			if c < rank {
				continue
			}
			bc = c - prev
			if timeseries.IsInf(upper, 1) {
				res[idx] = lower
			} else {
				res[idx] = lower + (upper-lower)*((rank-prev)/bc)
			}
			break
		}
		idx++
	}

	return total.TimeSeries.NewWithData(res)
}