File: limiter_wrapper.go

package info (click to toggle)
gitlab-agent 16.11.5-1
  • links: PTS, VCS
  • area: contrib
  • in suites: experimental
  • size: 7,072 kB
  • sloc: makefile: 193; sh: 55; ruby: 3
file content (77 lines) | stat: -rw-r--r-- 2,429 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
71
72
73
74
75
76
77
package metric

import (
	"context"
	"time"

	"go.opentelemetry.io/otel/attribute"
	"go.opentelemetry.io/otel/codes"
	otelmetric "go.opentelemetry.io/otel/metric"
	"go.opentelemetry.io/otel/trace"
)

const (
	rateLimiterBlockDurationName               = "limiter_block_duration"
	rateLimiterLimitName                       = "limiter_limit"
	allowedAttr                  attribute.Key = "allowed"
	limiterNameAttr              attribute.Key = "limiter_name"
	limiterLimitUnitAttr         attribute.Key = "unit"
)

type LimiterWrapper struct {
	nameAttr attribute.KeyValue
	tr       trace.Tracer
	hist     otelmetric.Float64Histogram
}

func NewLimiterWrapper(limiterName string, limit float64, limitUnit string, m otelmetric.Meter, tr trace.Tracer) (*LimiterWrapper, error) {
	nameAttr := limiterNameAttr.String(limiterName)
	limitAttrs := otelmetric.WithAttributeSet(attribute.NewSet(
		nameAttr,
		limiterLimitUnitAttr.String(limitUnit),
	)) // allocate once
	_, err := m.Float64ObservableGauge(
		rateLimiterLimitName,
		otelmetric.WithDescription("Limit for the rate limiter"),
		otelmetric.WithFloat64Callback(func(ctx context.Context, observer otelmetric.Float64Observer) error {
			observer.Observe(limit, limitAttrs)
			return nil
		}),
	)
	if err != nil {
		return nil, err
	}
	hist, err := m.Float64Histogram(
		rateLimiterBlockDurationName,
		otelmetric.WithUnit("s"),
		otelmetric.WithDescription("Duration the rate limiter blocked for deciding to allow/block the call"),
		otelmetric.WithExplicitBucketBoundaries(0.001, 0.004, 0.016, 0.064, 0.256, 1.024, 4.096, 16.384),
	)
	if err != nil {
		return nil, err
	}
	return &LimiterWrapper{
		nameAttr: nameAttr,
		tr:       tr,
		hist:     hist,
	}, nil
}

func (w *LimiterWrapper) Start(ctx context.Context) (context.Context, func(allowed bool)) {
	start := time.Now()
	ctx, span := w.tr.Start(ctx, "limiter", trace.WithSpanKind(trace.SpanKindInternal)) //nolint:spancheck
	return ctx, func(allowed bool) {                                                    //nolint:spancheck
		duration := float64(time.Since(start)) / float64(time.Second)
		// Pass background context because we always want to record the duration.
		w.hist.Record(context.Background(), duration, otelmetric.WithAttributeSet(attribute.NewSet(
			allowedAttr.Bool(allowed),
			w.nameAttr,
		)))
		code := codes.Ok
		if !allowed {
			code = codes.Error
		}
		span.SetStatus(code, "")
		span.End()
	}
}