Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 76 additions & 1 deletion balancer/weightedroundrobin/balancer.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ package weightedroundrobin
import (
"encoding/json"
"fmt"
"math"
rand "math/rand/v2"
"sync"
"sync/atomic"
Expand Down Expand Up @@ -91,6 +92,15 @@ var (
OptionalLabels: []string{"grpc.lb.locality"},
Default: false,
})

endpointsInSlowStartMetric = estats.RegisterInt64Count(estats.MetricDescriptor{
Name: "grpc.lb.wrr.endpoints_in_slow_start",
Description: "EXPERIMENTAL. Number of endpoints currently in the slow start period. This is incremented when a new scheduler is created.",
Unit: "{endpoint}",
Labels: []string{"grpc.target"},
OptionalLabels: []string{"grpc.lb.locality"},
Default: false,
})
)

func init() {
Expand Down Expand Up @@ -132,6 +142,28 @@ func (bb) ParseConfig(js json.RawMessage) (serviceconfig.LoadBalancingConfig, er
return nil, fmt.Errorf("wrr: errorUtilizationPenalty must be non-negative")
}

// Validate slow start configuration if provided.
if lbCfg.SlowStartConfig != nil {
ssc := lbCfg.SlowStartConfig
if ssc.Aggression != 0 && ssc.Aggression <= 0 {
return nil, fmt.Errorf("wrr: slowStartConfig.aggression must be greater than 0.0")
}
if ssc.MinWeightPercent != 0 && (ssc.MinWeightPercent < 0 || ssc.MinWeightPercent > 100) {
return nil, fmt.Errorf("wrr: slowStartConfig.minWeightPercent must be in range [0.0, 100.0]")
}
if ssc.SlowStartWindow != 0 && ssc.SlowStartWindow <= 0 {
return nil, fmt.Errorf("wrr: slowStartConfig.slowStartWindow must be greater than 0")
}

// Set defaults for slow start config.
if ssc.Aggression == 0 {
ssc.Aggression = 1.0
}
if ssc.MinWeightPercent == 0 {
ssc.MinWeightPercent = 10.0
}
}

// For easier comparisons later, ensure the OOB reporting period is unset
// (0s) when OOB reports are disabled.
if !lbCfg.EnableOOBLoadReport {
Expand Down Expand Up @@ -361,6 +393,7 @@ func (b *wrrBalancer) updateSubConnState(sc balancer.SubConn, state balancer.Sub
ew.mu.Lock()
ew.nonEmptySince = time.Time{}
ew.lastUpdated = time.Time{}
ew.readySince = internal.TimeNow() // Set readySince for slow start period.
cfg := ew.cfg
ew.mu.Unlock()
ew.updateORCAListener(cfg)
Expand All @@ -380,6 +413,9 @@ func (b *wrrBalancer) updateSubConnState(sc balancer.SubConn, state balancer.Sub
if ew.stopORCAListener != nil {
ew.stopORCAListener()
}
ew.mu.Lock()
ew.readySince = time.Time{} // Reset readySince when going non-READY.
ew.mu.Unlock()
ew.pickedSC = nil
}
}
Expand Down Expand Up @@ -427,7 +463,9 @@ func (p *picker) endpointWeights(recordMetrics bool) []float64 {
wp := make([]float64, len(p.weightedPickers))
now := internal.TimeNow()
for i, wpi := range p.weightedPickers {
wp[i] = wpi.weightedEndpoint.weight(now, time.Duration(p.cfg.WeightExpirationPeriod), time.Duration(p.cfg.BlackoutPeriod), recordMetrics)
baseWeight := wpi.weightedEndpoint.weight(now, time.Duration(p.cfg.WeightExpirationPeriod), time.Duration(p.cfg.BlackoutPeriod), recordMetrics)
slowStartScale := wpi.weightedEndpoint.slowStartScale(now, recordMetrics)
wp[i] = baseWeight * slowStartScale
}
return wp
}
Expand Down Expand Up @@ -519,6 +557,7 @@ type endpointWeight struct {
weightVal float64
nonEmptySince time.Time
lastUpdated time.Time
readySince time.Time // Time when endpoint transitioned to ready state (for slow start period).
cfg *lbConfig
}

Expand Down Expand Up @@ -634,3 +673,39 @@ func (w *endpointWeight) weight(now time.Time, weightExpirationPeriod, blackoutP

return w.weightVal
}

// slowStartScale returns the scaling factor for slow start.
// Returns 1.0 if slow start is not configured or endpoint is outside slow start window.
func (w *endpointWeight) slowStartScale(now time.Time, recordMetrics bool) float64 {
w.mu.Lock()
defer w.mu.Unlock()

// No config, slow start config, or readySince not set.
if w.cfg == nil || w.cfg.SlowStartConfig == nil || w.readySince.Equal(time.Time{}) {
return 1.0
}

ssc := w.cfg.SlowStartConfig
slowStartWindow := time.Duration(ssc.SlowStartWindow)
timeSinceReady := now.Sub(w.readySince)
// Outside slow start window.
if timeSinceReady >= slowStartWindow {
return 1.0
}

// We record metrics here to avoid incrementing the metrics
// when the slow start scale is 1.0.
if recordMetrics {
defer func() {
endpointsInSlowStartMetric.Record(w.metricsRecorder, 1, w.target, w.locality)
}()
}

// Calculate scaling factor using the formula from gRFC A100:
// scaled_weight = weight * max(min_weight_percent/100, time_factor ^ (1/aggression))
// where time_factor = max(time_since_ready_seconds, 1) / slow_start_window_seconds
timeFactor := math.Max(timeSinceReady.Seconds(), 1.0) / slowStartWindow.Seconds()
minScale := ssc.MinWeightPercent / 100.0
scale := math.Pow(timeFactor, 1.0/ssc.Aggression)
return math.Max(minScale, scale)
}
94 changes: 92 additions & 2 deletions balancer/weightedroundrobin/balancer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,28 +67,54 @@ func stringp(s string) *string { return &s }

var (
perCallConfig = iwrr.LBConfig{
EnableOOBLoadReport: boolp(false),
OOBReportingPeriod: stringp("0.005s"),
BlackoutPeriod: stringp("0s"),
WeightExpirationPeriod: stringp("60s"),
WeightUpdatePeriod: stringp(".050s"),
ErrorUtilizationPenalty: float64p(0.),
}
perCallConfigWithSlowStart = iwrr.LBConfig{
EnableOOBLoadReport: boolp(false),
OOBReportingPeriod: stringp("0.005s"),
BlackoutPeriod: stringp("0s"),
WeightExpirationPeriod: stringp("60s"),
WeightUpdatePeriod: stringp(".050s"),
ErrorUtilizationPenalty: float64p(0),
SlowStartConfig: &iwrr.SlowStartConfig{
SlowStartWindow: stringp("30s"),
Aggression: float64p(1.0),
MinWeightPercent: float64p(10.0),
},
}
oobConfig = iwrr.LBConfig{
EnableOOBLoadReport: boolp(true),
OOBReportingPeriod: stringp("0.005s"),
BlackoutPeriod: stringp("0s"),
WeightExpirationPeriod: stringp("60s"),
WeightUpdatePeriod: stringp(".050s"),
ErrorUtilizationPenalty: float64p(0),
ErrorUtilizationPenalty: float64p(0.),
}
oobConfigWithSlowStart = iwrr.LBConfig{
EnableOOBLoadReport: boolp(true),
OOBReportingPeriod: stringp("0.005s"),
BlackoutPeriod: stringp("0s"),
WeightExpirationPeriod: stringp("60s"),
WeightUpdatePeriod: stringp(".050s"),
ErrorUtilizationPenalty: float64p(0.),
SlowStartConfig: &iwrr.SlowStartConfig{
SlowStartWindow: stringp("30s"),
Aggression: float64p(1.0),
MinWeightPercent: float64p(10.0),
},
}
testMetricsConfig = iwrr.LBConfig{
EnableOOBLoadReport: boolp(false),
OOBReportingPeriod: stringp("0.005s"),
BlackoutPeriod: stringp("0s"),
WeightExpirationPeriod: stringp("60s"),
WeightUpdatePeriod: stringp("30s"),
ErrorUtilizationPenalty: float64p(0),
ErrorUtilizationPenalty: float64p(0.0),
}
)

Expand Down Expand Up @@ -176,8 +202,11 @@ func (s) TestBalancer_OneAddress(t *testing.T) {
cfg iwrr.LBConfig
}{
{rt: reportNone, cfg: perCallConfig},
{rt: reportCall, cfg: perCallConfigWithSlowStart},
{rt: reportCall, cfg: perCallConfig},
{rt: reportCall, cfg: perCallConfigWithSlowStart},
{rt: reportOOB, cfg: oobConfig},
{rt: reportOOB, cfg: oobConfigWithSlowStart},
}

for _, tc := range testCases {
Expand Down Expand Up @@ -652,6 +681,65 @@ func (s) TestBalancer_TwoAddresses_BlackoutPeriod(t *testing.T) {
}
}

func (s) TestBalancer_SlowStart(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()

srv1 := startServer(t, reportBoth)
srv2 := startServer(t, reportBoth)
srv3 := startServer(t, reportBoth)

// srv1, srv2, srv3 all start loaded equally.
srv1.oobMetrics.SetQPS(10.0)
srv1.oobMetrics.SetApplicationUtilization(1.0)

srv2.oobMetrics.SetQPS(10.0)
srv2.oobMetrics.SetApplicationUtilization(1.0)

srv3.oobMetrics.SetQPS(10.0)
srv3.oobMetrics.SetApplicationUtilization(1.0)

slowStartPeriod := 2 * time.Second
slowStartConfig := &iwrr.SlowStartConfig{
SlowStartWindow: stringp("2s"),
Aggression: float64p(1.0),
MinWeightPercent: float64p(10.0),
}
cfg := oobConfig
cfg.BlackoutPeriod = stringp("0.1s")
cfg.SlowStartConfig = slowStartConfig
sc := svcConfig(t, cfg)

if err := srv1.StartClient(grpc.WithDefaultServiceConfig(sc)); err != nil {
t.Fatalf("Error starting client: %v", err)
}

addrs := []resolver.Address{{Addr: srv1.Address}, {Addr: srv2.Address}}
srv1.R.UpdateState(resolver.State{Addresses: addrs})

// Call each backend once to ensure the weights have been received.
ensureReached(ctx, t, srv1.Client, 2)
time.Sleep(slowStartPeriod + weightUpdatePeriod)
checkWeights(ctx, t, srvWeight{srv1, 10}, srvWeight{srv2, 10})

// Add backend 3.
addrs = append(addrs, resolver.Address{Addr: srv3.Address})
srv1.R.UpdateState(resolver.State{Addresses: addrs})
ensureReached(ctx, t, srv1.Client, 3)

// validate that srv3 is in slow start.
time.Sleep(weightUpdatePeriod)
checkWeights(ctx, t, srvWeight{srv1, 10}, srvWeight{srv2, 10}, srvWeight{srv3, 5})

// validate that srv3 exits slow start.
time.Sleep(slowStartPeriod + weightUpdatePeriod)
checkWeights(ctx, t, srvWeight{srv1, 10}, srvWeight{srv2, 10}, srvWeight{srv3, 10})
if srv1.CC != nil {
srv1.CC.Close()
time.Sleep(weightUpdatePeriod) // Wait for cleanup
}
}

// Tests that the weight expiration period causes backends to use 0 as their
// weight (meaning to use the average weight) once the expiration period
// elapses.
Expand Down Expand Up @@ -789,7 +877,9 @@ func (s) TestBalancer_AddressesChanging(t *testing.T) {
func ensureReached(ctx context.Context, t *testing.T, c testgrpc.TestServiceClient, n int) {
t.Helper()
reached := make(map[string]struct{})
i := 0
for len(reached) != n {
i++
var peer peer.Peer
if _, err := c.EmptyCall(ctx, &testpb.Empty{}, grpc.Peer(&peer)); err != nil {
t.Fatalf("Error from EmptyCall: %v", err)
Expand Down
31 changes: 31 additions & 0 deletions balancer/weightedroundrobin/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,35 @@ type lbConfig struct {
// The multiplier used to adjust endpoint weights with the error rate
// calculated as eps/qps. Default is 1.0.
ErrorUtilizationPenalty float64 `json:"errorUtilizationPenalty,omitempty"`

// Configuration for slow start feature
SlowStartConfig *SlowStartConfig `json:"slowStartConfig,omitempty"`
}

// SlowStartConfig contains configuration for the slow start feature.
type SlowStartConfig struct {
// Represents the size of slow start window.
//
// The newly created endpoint remains in slow start mode starting from its creation time
// for the duration of slow start window.
SlowStartWindow iserviceconfig.Duration `json:"slowStartWindow,omitempty"`

// This parameter controls the speed of traffic increase over the slow start window. Defaults to 1.0,
// so that endpoint would get linearly increasing amount of traffic.
// When increasing the value for this parameter, the speed of traffic ramp-up increases non-linearly.
// The value of aggression parameter must be greater than 0.0.
// By tuning the parameter, it is possible to achieve polynomial or exponential shape of ramp-up curve.
//
// During slow start window, effective weight of an endpoint would be scaled with time factor and aggression:
// ``new_weight = weight * max(min_weight_percent / 100, time_factor ^ (1 / aggression))``,
// where ``time_factor = max(time_since_start_seconds, 1) / slow_start_window_seconds``.
//
// As time progresses, more and more traffic would be sent to endpoint, which is in slow start window.
// Once endpoint exits slow start, time_factor and aggression no longer affect its weight.
Aggression float64 `json:"aggression,omitempty"`

// Configures the minimum percentage of the original weight that will be used for an endpoint
// in slow start. This helps to avoid a scenario in which endpoints receive no traffic during the
// slow start window. Valid range is [0.0, 100.0]. If the value is not specified, the default is 10%.
MinWeightPercent float64 `json:"minWeightPercent,omitempty"`
}
21 changes: 15 additions & 6 deletions balancer/weightedroundrobin/internal/internal.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,21 @@ var AllowAnyWeightUpdatePeriod bool
// LBConfig allows tests to produce a JSON form of the config from the struct
// instead of using a string.
type LBConfig struct {
EnableOOBLoadReport *bool `json:"enableOobLoadReport,omitempty"`
OOBReportingPeriod *string `json:"oobReportingPeriod,omitempty"`
BlackoutPeriod *string `json:"blackoutPeriod,omitempty"`
WeightExpirationPeriod *string `json:"weightExpirationPeriod,omitempty"`
WeightUpdatePeriod *string `json:"weightUpdatePeriod,omitempty"`
ErrorUtilizationPenalty *float64 `json:"errorUtilizationPenalty,omitempty"`
EnableOOBLoadReport *bool `json:"enableOobLoadReport,omitempty"`
OOBReportingPeriod *string `json:"oobReportingPeriod,omitempty"`
BlackoutPeriod *string `json:"blackoutPeriod,omitempty"`
WeightExpirationPeriod *string `json:"weightExpirationPeriod,omitempty"`
WeightUpdatePeriod *string `json:"weightUpdatePeriod,omitempty"`
ErrorUtilizationPenalty *float64 `json:"errorUtilizationPenalty,omitempty"`
SlowStartConfig *SlowStartConfig `json:"slowStartConfig,omitempty"`
}

// SlowStartConfig allows tests to produce a JSON form
// of the nested slow start config.
type SlowStartConfig struct {
SlowStartWindow *string `json:"slowStartWindow,omitempty"`
Aggression *float64 `json:"aggression,omitempty"`
MinWeightPercent *float64 `json:"minWeightPercent,omitempty"`
}

// TimeNow can be overridden by tests to return a different value for the
Expand Down
2 changes: 1 addition & 1 deletion examples/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/envoyproxy/go-control-plane/envoy v1.35.0 // indirect
github.com/envoyproxy/go-control-plane/envoy v1.35.1-0.20251015221300-4138018a492b // indirect
github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-jose/go-jose/v4 v4.1.3 // indirect
Expand Down
12 changes: 6 additions & 6 deletions examples/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -3071,6 +3071,7 @@ github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78/go.mod h1:W+zGtBO5Y1Ig
github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
github.com/cncf/xds/go v0.0.0-20251014123835-2ee22ca58382/go.mod h1:HlzOvOjVBOfTGSRXRyY0OiCS/3J1akRGQQpRO/7zyF4=
github.com/cncf/xds/go v0.0.0-20251022180443-0feb69152e9f h1:Y8xYupdHxryycyPlc9Y+bSQAYZnetRJ70VMVKm5CKI0=
github.com/cncf/xds/go v0.0.0-20251022180443-0feb69152e9f/go.mod h1:HlzOvOjVBOfTGSRXRyY0OiCS/3J1akRGQQpRO/7zyF4=
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
Expand Down Expand Up @@ -3103,14 +3104,13 @@ github.com/envoyproxy/go-control-plane v0.11.1/go.mod h1:uhMcXKCQMEJHiAb0w+YGefQ
github.com/envoyproxy/go-control-plane v0.12.0/go.mod h1:ZBTaoJ23lqITozF0M6G4/IragXCQKCnYbmlmtHvwRG0=
github.com/envoyproxy/go-control-plane v0.13.0/go.mod h1:GRaKG3dwvFoTg4nj7aXdZnvMg4d7nvT/wl9WgVXn3Q8=
github.com/envoyproxy/go-control-plane v0.13.1/go.mod h1:X45hY0mufo6Fd0KW3rqsGvQMw58jvjymeCzBU3mWyHw=
github.com/envoyproxy/go-control-plane v0.13.4 h1:zEqyPVyku6IvWCFwux4x9RxkLOMUL+1vC9xUFv5l2/M=
github.com/envoyproxy/go-control-plane v0.13.4/go.mod h1:kDfuBlDVsSj2MjrLEtRWtHlsWIFcGyB2RMO44Dc5GZA=
github.com/envoyproxy/go-control-plane v0.13.5-0.20251024222203-75eaa193e329 h1:K+fnvUM0VZ7ZFJf0n4L/BRlnsb9pL/GuDG6FqaH+PwM=
github.com/envoyproxy/go-control-plane v0.13.5-0.20251024222203-75eaa193e329/go.mod h1:Alz8LEClvR7xKsrq3qzoc4N0guvVNSS8KmSChGYr9hs=
github.com/envoyproxy/go-control-plane/envoy v1.32.2/go.mod h1:eR2SOX2IedqlPvmiKjUH7Wu//S602JKI7HPC/L3SRq8=
github.com/envoyproxy/go-control-plane/envoy v1.32.3/go.mod h1:F6hWupPfh75TBXGKA++MCT/CZHFq5r9/uwt/kQYkZfE=
github.com/envoyproxy/go-control-plane/envoy v1.32.4/go.mod h1:Gzjc5k8JcJswLjAx1Zm+wSYE20UrLtt7JZMWiWQXQEw=
github.com/envoyproxy/go-control-plane/envoy v1.35.0 h1:ixjkELDE+ru6idPxcHLj8LBVc2bFP7iBytj353BoHUo=
github.com/envoyproxy/go-control-plane/envoy v1.35.0/go.mod h1:09qwbGVuSWWAyN5t/b3iyVfz5+z8QWGrzkoqm/8SbEs=
github.com/envoyproxy/go-control-plane/envoy v1.35.1-0.20251015221300-4138018a492b h1:rDyuvoLwwYo/TeYQF/6Ag7BG/LYbrnLp3qvXzzF5JRk=
github.com/envoyproxy/go-control-plane/envoy v1.35.1-0.20251015221300-4138018a492b/go.mod h1:ty89S1YCCVruQAm9OtKeEkQLTb+Lkz0k8v9W0Oxsv98=
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI=
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
Expand Down Expand Up @@ -3378,7 +3378,7 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFb
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1/go.mod h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90=
github.com/hamba/avro/v2 v2.17.2/go.mod h1:Q9YK+qxAhtVrNqOhwlZTATLgLA8qxG2vtvkhK8fJ7Jo=
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
Expand Down Expand Up @@ -3854,7 +3854,7 @@ go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqe
go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
go.opentelemetry.io/proto/otlp v1.7.0/go.mod h1:fSKjH6YJ7HDlwzltzyMj036AJ3ejJLCgCSHGj4efDDo=
go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
Expand Down
Loading
Loading