13647: Use cluster config instead of custom keepstore config.
[arvados.git] / sdk / go / httpserver / request_limiter.go
index e7192d5b4f407560d1545ac02264c1f11c70684d..23e6e016d303bbc78abefcc39bfa0fb65b8ef0fe 100644 (file)
@@ -6,6 +6,9 @@ package httpserver
 
 import (
        "net/http"
+       "sync/atomic"
+
+       "github.com/prometheus/client_golang/prometheus"
 )
 
 // RequestCounter is an http.Handler that tracks the number of
@@ -24,19 +27,45 @@ type RequestCounter interface {
 type limiterHandler struct {
        requests chan struct{}
        handler  http.Handler
+       count    int64 // only used if cap(requests)==0
 }
 
 // NewRequestLimiter returns a RequestCounter that delegates up to
 // maxRequests at a time to the given handler, and responds 503 to all
 // incoming requests beyond that limit.
-func NewRequestLimiter(maxRequests int, handler http.Handler) RequestCounter {
-       return &limiterHandler{
+//
+// "concurrent_requests" and "max_concurrent_requests" metrics are
+// registered with the given reg, if reg is not nil.
+func NewRequestLimiter(maxRequests int, handler http.Handler, reg *prometheus.Registry) RequestCounter {
+       h := &limiterHandler{
                requests: make(chan struct{}, maxRequests),
                handler:  handler,
        }
+       if reg != nil {
+               reg.MustRegister(prometheus.NewGaugeFunc(
+                       prometheus.GaugeOpts{
+                               Namespace: "arvados",
+                               Name:      "concurrent_requests",
+                               Help:      "Number of requests in progress",
+                       },
+                       func() float64 { return float64(h.Current()) },
+               ))
+               reg.MustRegister(prometheus.NewGaugeFunc(
+                       prometheus.GaugeOpts{
+                               Namespace: "arvados",
+                               Name:      "max_concurrent_requests",
+                               Help:      "Maximum number of concurrent requests",
+                       },
+                       func() float64 { return float64(h.Max()) },
+               ))
+       }
+       return h
 }
 
 func (h *limiterHandler) Current() int {
+       if cap(h.requests) == 0 {
+               return int(atomic.LoadInt64(&h.count))
+       }
        return len(h.requests)
 }
 
@@ -45,6 +74,11 @@ func (h *limiterHandler) Max() int {
 }
 
 func (h *limiterHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
+       if cap(h.requests) == 0 {
+               atomic.AddInt64(&h.count, 1)
+               h.handler.ServeHTTP(resp, req)
+               atomic.AddInt64(&h.count, -1)
+       }
        select {
        case h.requests <- struct{}{}:
        default: