github.com/grafana/pyroscope@v1.18.0/pkg/querybackend/concurrency.go (about)

     1  package querybackend
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"github.com/go-kit/log"
     8  	"github.com/go-kit/log/level"
     9  	"github.com/platinummonkey/go-concurrency-limits/core"
    10  	gclGrpc "github.com/platinummonkey/go-concurrency-limits/grpc"
    11  	"github.com/platinummonkey/go-concurrency-limits/limit"
    12  	"github.com/platinummonkey/go-concurrency-limits/limiter"
    13  	"github.com/platinummonkey/go-concurrency-limits/strategy"
    14  	"google.golang.org/grpc"
    15  	"google.golang.org/grpc/codes"
    16  )
    17  
    18  const (
    19  	// gradient2 limit
    20  	minLimit     = 50
    21  	maxLimit     = 100
    22  	initialLimit = 50
    23  	smoothing    = 0.2
    24  	longWindow   = 600
    25  
    26  	// limiter
    27  	minWindowTime   = 1
    28  	maxWindowTime   = 1000
    29  	minRTTThreshold = 1e6
    30  	windowSize      = 100
    31  )
    32  
    33  var (
    34  	queueSizeFn = func(limit int) int {
    35  		return 10
    36  	}
    37  )
    38  
    39  func CreateConcurrencyInterceptor(logger log.Logger) (grpc.UnaryServerInterceptor, error) {
    40  	gclLog := newGclLogger(logger)
    41  	// TODO(aleks-p): Implement metric registry
    42  	serverLimit, err := limit.NewGradient2Limit("query-backend-concurrency-limit", minLimit, maxLimit, initialLimit, queueSizeFn, smoothing, longWindow, gclLog, nil)
    43  	if err != nil {
    44  		return nil, err
    45  	}
    46  
    47  	serverLimiter, err := limiter.NewDefaultLimiter(serverLimit, minWindowTime, maxWindowTime, minRTTThreshold, windowSize, strategy.NewSimpleStrategy(initialLimit), gclLog, nil)
    48  	if err != nil {
    49  		return nil, err
    50  	}
    51  
    52  	options := []gclGrpc.InterceptorOption{
    53  		gclGrpc.WithName("gcl-interceptor"),
    54  		gclGrpc.WithLimiter(serverLimiter),
    55  		gclGrpc.WithLimitExceededResponseClassifier(func(ctx context.Context, method string, req interface{}, l core.Limiter) (interface{}, codes.Code, error) {
    56  			return nil, codes.ResourceExhausted, fmt.Errorf("concurrency limit exceeded")
    57  		})}
    58  	gclInterceptor := gclGrpc.UnaryServerInterceptor(options...)
    59  
    60  	return gclInterceptor, err
    61  }
    62  
    63  type gclLogger struct {
    64  	logger log.Logger
    65  }
    66  
    67  func (g gclLogger) Debugf(msg string, params ...interface{}) {
    68  	level.Debug(g.logger).Log("msg", fmt.Sprintf(msg, params...))
    69  }
    70  
    71  func (g gclLogger) IsDebugEnabled() bool {
    72  	return true
    73  }
    74  
    75  func newGclLogger(logger log.Logger) *gclLogger {
    76  	return &gclLogger{logger: logger}
    77  }