Skip to content

Commit d61a152

Browse files
committed
feat: add structured histogram to status proto for up/down speeds
1 parent 2cc5d53 commit d61a152

File tree

13 files changed

+1199
-59
lines changed

13 files changed

+1199
-59
lines changed

pkg/api/api.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,32 @@ func (s *Service) contentLengthMetricMiddleware() func(h http.Handler) http.Hand
506506
}
507507
}
508508

509+
func (s *Service) downloadSpeedMetricMiddleware() func(h http.Handler) http.Handler {
510+
return func(h http.Handler) http.Handler {
511+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
512+
now := time.Now()
513+
h.ServeHTTP(w, r)
514+
515+
if rw, ok := w.(*responseWriter); ok {
516+
speed := float64(rw.size) / time.Since(now).Seconds()
517+
s.metrics.DownloadSpeed.Observe(speed)
518+
}
519+
})
520+
}
521+
}
522+
523+
func (s *Service) uploadSpeedMetricMiddleware() func(h http.Handler) http.Handler {
524+
return func(h http.Handler) http.Handler {
525+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
526+
now := time.Now()
527+
h.ServeHTTP(w, r)
528+
529+
speed := float64(r.ContentLength) / time.Since(now).Seconds()
530+
s.metrics.UploadSpeed.Observe(speed)
531+
})
532+
}
533+
}
534+
509535
// gasConfigMiddleware can be used by the APIs that allow block chain transactions to set
510536
// gas price and gas limit through the HTTP API headers.
511537
func (s *Service) gasConfigMiddleware(handlerName string) func(h http.Handler) http.Handler {

pkg/api/metrics.go

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111

1212
"github.com/ethersphere/bee/v2"
1313
m "github.com/ethersphere/bee/v2/pkg/metrics"
14+
"github.com/ethersphere/bee/v2/pkg/status"
1415
"github.com/prometheus/client_golang/prometheus"
1516
"github.com/prometheus/client_golang/prometheus/collectors"
1617
)
@@ -29,6 +30,8 @@ type metrics struct {
2930
ResponseCodeCounts *prometheus.CounterVec
3031

3132
ContentApiDuration prometheus.HistogramVec
33+
UploadSpeed prometheus.Histogram
34+
DownloadSpeed prometheus.Histogram
3235
}
3336

3437
func newMetrics() metrics {
@@ -64,6 +67,20 @@ func newMetrics() metrics {
6467
Help: "Histogram of file upload API response durations.",
6568
Buckets: []float64{0.5, 1, 2.5, 5, 10, 30, 60},
6669
}, []string{"filesize", "method"}),
70+
UploadSpeed: prometheus.NewHistogram(prometheus.HistogramOpts{
71+
Namespace: m.Namespace,
72+
Subsystem: subsystem,
73+
Name: "upload_speed",
74+
Help: "Histogram of upload speed in MiB/s.",
75+
Buckets: []float64{0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2, 2.5, 3, 4, 5},
76+
}),
77+
DownloadSpeed: prometheus.NewHistogram(prometheus.HistogramOpts{
78+
Namespace: m.Namespace,
79+
Subsystem: subsystem,
80+
Name: "download_speed",
81+
Help: "Histogram of download speed in MiB/s.",
82+
Buckets: []float64{0.5, 1, 1.5, 2, 2.5, 3, 4, 5, 6, 7, 8, 9},
83+
}),
6784
}
6885
}
6986

@@ -102,6 +119,13 @@ func (s *Service) responseCodeMetricsHandler(h http.Handler) http.Handler {
102119
})
103120
}
104121

122+
func (s *Service) StatusMetrics() status.Metrics {
123+
return status.Metrics{
124+
UploadSpeed: s.metrics.UploadSpeed,
125+
DownloadSpeed: s.metrics.DownloadSpeed,
126+
}
127+
}
128+
105129
// UpgradedResponseWriter adds more functionality on top of ResponseWriter
106130
type UpgradedResponseWriter interface {
107131
http.ResponseWriter
@@ -114,18 +138,25 @@ type responseWriter struct {
114138
UpgradedResponseWriter
115139
statusCode int
116140
wroteHeader bool
141+
size int
117142
}
118143

119144
func newResponseWriter(w http.ResponseWriter) *responseWriter {
120145
// StatusOK is called by default if nothing else is called
121146
uw := w.(UpgradedResponseWriter)
122-
return &responseWriter{uw, http.StatusOK, false}
147+
return &responseWriter{uw, http.StatusOK, false, 0}
123148
}
124149

125150
func (rw *responseWriter) Status() int {
126151
return rw.statusCode
127152
}
128153

154+
func (rr *responseWriter) Write(b []byte) (int, error) {
155+
size, err := rr.UpgradedResponseWriter.Write(b)
156+
rr.size += size
157+
return size, err
158+
}
159+
129160
func (rw *responseWriter) WriteHeader(code int) {
130161
if rw.wroteHeader {
131162
return

pkg/api/router.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,7 @@ func (s *Service) mountAPI() {
301301
"POST": web.ChainHandlers(
302302
s.contentLengthMetricMiddleware(),
303303
s.newTracingHandler("bzz-upload"),
304+
s.uploadSpeedMetricMiddleware(),
304305
web.FinalHandlerFunc(s.bzzUploadHandler),
305306
),
306307
})
@@ -325,6 +326,7 @@ func (s *Service) mountAPI() {
325326
s.contentLengthMetricMiddleware(),
326327
s.newTracingHandler("bzz-download"),
327328
s.actDecryptionHandler(),
329+
s.downloadSpeedMetricMiddleware(),
328330
web.FinalHandlerFunc(s.bzzDownloadHandler),
329331
),
330332
"HEAD": web.ChainHandlers(

pkg/api/status.go

Lines changed: 65 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,26 +12,29 @@ import (
1212
"time"
1313

1414
"github.com/ethersphere/bee/v2/pkg/jsonhttp"
15+
"github.com/ethersphere/bee/v2/pkg/status"
1516
"github.com/ethersphere/bee/v2/pkg/swarm"
1617
"github.com/ethersphere/bee/v2/pkg/topology"
1718
)
1819

1920
type statusSnapshotResponse struct {
20-
Overlay string `json:"overlay"`
21-
Proximity uint `json:"proximity"`
22-
BeeMode string `json:"beeMode"`
23-
ReserveSize uint64 `json:"reserveSize"`
24-
ReserveSizeWithinRadius uint64 `json:"reserveSizeWithinRadius"`
25-
PullsyncRate float64 `json:"pullsyncRate"`
26-
StorageRadius uint8 `json:"storageRadius"`
27-
ConnectedPeers uint64 `json:"connectedPeers"`
28-
NeighborhoodSize uint64 `json:"neighborhoodSize"`
29-
RequestFailed bool `json:"requestFailed,omitempty"`
30-
BatchCommitment uint64 `json:"batchCommitment"`
31-
IsReachable bool `json:"isReachable"`
32-
LastSyncedBlock uint64 `json:"lastSyncedBlock"`
33-
CommittedDepth uint8 `json:"committedDepth"`
34-
IsWarmingUp bool `json:"isWarmingUp"`
21+
Overlay string `json:"overlay"`
22+
Proximity uint `json:"proximity"`
23+
BeeMode string `json:"beeMode"`
24+
ReserveSize uint64 `json:"reserveSize"`
25+
ReserveSizeWithinRadius uint64 `json:"reserveSizeWithinRadius"`
26+
PullsyncRate float64 `json:"pullsyncRate"`
27+
StorageRadius uint8 `json:"storageRadius"`
28+
ConnectedPeers uint64 `json:"connectedPeers"`
29+
NeighborhoodSize uint64 `json:"neighborhoodSize"`
30+
RequestFailed bool `json:"requestFailed,omitempty"`
31+
BatchCommitment uint64 `json:"batchCommitment"`
32+
IsReachable bool `json:"isReachable"`
33+
LastSyncedBlock uint64 `json:"lastSyncedBlock"`
34+
CommittedDepth uint8 `json:"committedDepth"`
35+
IsWarmingUp bool `json:"isWarmingUp"`
36+
UploadSpeed *statusHistogramResponse `json:"uploadSpeed,omitempty"`
37+
DownloadSpeed *statusHistogramResponse `json:"downloadSpeed,omitempty"`
3538
}
3639

3740
type statusResponse struct {
@@ -48,6 +51,51 @@ type neighborhoodsResponse struct {
4851
Neighborhoods []statusNeighborhoodResponse `json:"neighborhoods"`
4952
}
5053

54+
func newStatusHistogramResponse(h *status.Histogram) *statusHistogramResponse {
55+
if h == nil {
56+
return nil
57+
}
58+
labels := make([]statusLabelResponse, 0, len(h.Labels))
59+
for _, l := range h.Labels {
60+
labels = append(labels, statusLabelResponse{
61+
Name: l.Name,
62+
Value: l.Value,
63+
})
64+
}
65+
66+
buckets := make([]statusHistogramBucketResponse, 0, len(h.Buckets))
67+
for _, b := range h.Buckets {
68+
buckets = append(buckets, statusHistogramBucketResponse{
69+
CumulativeCount: b.CumulativeCount,
70+
UpperBound: b.UpperBound,
71+
})
72+
}
73+
74+
return &statusHistogramResponse{
75+
Labels: labels,
76+
SampleSum: h.SampleSum,
77+
SampleCount: h.SampleCount,
78+
Buckets: buckets,
79+
}
80+
}
81+
82+
type statusHistogramResponse struct {
83+
Labels []statusLabelResponse `json:"labels,omitempty"`
84+
SampleSum float64 `json:"sampleSum,omitempty"`
85+
SampleCount uint64 `json:"sampleCount,omitempty"`
86+
Buckets []statusHistogramBucketResponse `json:"buckets,omitempty"`
87+
}
88+
89+
type statusLabelResponse struct {
90+
Name string `json:"name,omitempty"`
91+
Value string `json:"value,omitempty"`
92+
}
93+
94+
type statusHistogramBucketResponse struct {
95+
CumulativeCount uint64 `json:"cumulativeCount,omitempty"`
96+
UpperBound float64 `json:"upperBound,omitempty"`
97+
}
98+
5199
// statusAccessHandler is a middleware that limits the number of simultaneous
52100
// status requests.
53101
func (s *Service) statusAccessHandler(h http.Handler) http.Handler {
@@ -98,6 +146,8 @@ func (s *Service) statusGetHandler(w http.ResponseWriter, _ *http.Request) {
98146
LastSyncedBlock: ss.LastSyncedBlock,
99147
CommittedDepth: uint8(ss.CommittedDepth),
100148
IsWarmingUp: s.isWarmingUp,
149+
UploadSpeed: newStatusHistogramResponse(ss.UploadSpeed),
150+
DownloadSpeed: newStatusHistogramResponse(ss.DownloadSpeed),
101151
})
102152
}
103153

pkg/api/status_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ func TestGetStatus(t *testing.T) {
6060
mode.String(),
6161
ssMock,
6262
ssMock,
63+
status.Metrics{},
6364
)
6465

6566
statusSvc.SetSync(ssMock)
@@ -86,6 +87,7 @@ func TestGetStatus(t *testing.T) {
8687
"",
8788
nil,
8889
nil,
90+
status.Metrics{},
8991
),
9092
})
9193

pkg/node/node.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -910,7 +910,7 @@ func NewBee(
910910

911911
validStamp := postage.ValidStamp(batchStore)
912912

913-
nodeStatus := status.NewService(logger, p2ps, kad, beeNodeMode.String(), batchStore, localStore)
913+
nodeStatus := status.NewService(logger, p2ps, kad, beeNodeMode.String(), batchStore, localStore, apiService.StatusMetrics())
914914
if err = p2ps.AddProtocol(nodeStatus.Protocol()); err != nil {
915915
return nil, fmt.Errorf("status service: %w", err)
916916
}

pkg/status/export_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,5 @@ const (
99
ProtocolVersion = protocolVersion
1010
StreamName = streamName
1111
)
12+
13+
var NewHistogram = newHistogram

0 commit comments

Comments
 (0)