Skip to content

Commit fa78402

Browse files
committed
feat: add first_block_get_latency_seconds
This adds a new, generic metric that aims to replace unixfs-specific unixfs_get_latency_seconds
1 parent 9fbfb0b commit fa78402

File tree

3 files changed

+37
-9
lines changed

3 files changed

+37
-9
lines changed

core/corehttp/gateway_handler.go

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ type gatewayHandler struct {
6464
config GatewayConfig
6565
api coreiface.CoreAPI
6666

67-
// TODO: add metrics for non-unixfs responses (block, car)
68-
unixfsGetMetric *prometheus.SummaryVec
67+
unixfsGetMetric *prometheus.SummaryVec
68+
firstBlockGetMetric *prometheus.SummaryVec
6969
}
7070

7171
// StatusResponseWriter enables us to override HTTP Status Code passed to
@@ -90,11 +90,12 @@ func (sw *statusResponseWriter) WriteHeader(code int) {
9090

9191
func newGatewayHandler(c GatewayConfig, api coreiface.CoreAPI) *gatewayHandler {
9292
unixfsGetMetric := prometheus.NewSummaryVec(
93+
// TODO: deprecated, use first_block_get_latency_seconds instead
9394
prometheus.SummaryOpts{
9495
Namespace: "ipfs",
9596
Subsystem: "http",
9697
Name: "unixfs_get_latency_seconds",
97-
Help: "The time till the first block is received when 'getting' a file from the gateway.",
98+
Help: "The time till the first block is received when 'getting' a unixfs file from the gateway.",
9899
},
99100
[]string{"gateway"},
100101
)
@@ -106,10 +107,28 @@ func newGatewayHandler(c GatewayConfig, api coreiface.CoreAPI) *gatewayHandler {
106107
}
107108
}
108109

110+
firstBlockGetMetric := prometheus.NewSummaryVec(
111+
prometheus.SummaryOpts{
112+
Namespace: "ipfs",
113+
Subsystem: "http",
114+
Name: "first_block_get_latency_seconds",
115+
Help: "The time till the first block is successfully received when reading data from the gateway.",
116+
},
117+
[]string{"gateway"},
118+
)
119+
if err := prometheus.Register(firstBlockGetMetric); err != nil {
120+
if are, ok := err.(prometheus.AlreadyRegisteredError); ok {
121+
firstBlockGetMetric = are.ExistingCollector.(*prometheus.SummaryVec)
122+
} else {
123+
log.Errorf("failed to register firstBlockGetMetric: %v", err)
124+
}
125+
}
126+
109127
i := &gatewayHandler{
110-
config: c,
111-
api: api,
112-
unixfsGetMetric: unixfsGetMetric,
128+
config: c,
129+
api: api,
130+
unixfsGetMetric: unixfsGetMetric,
131+
firstBlockGetMetric: firstBlockGetMetric,
113132
}
114133
return i
115134
}
@@ -305,6 +324,15 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request
305324
return
306325
}
307326

327+
// Update the global metric of the time it takes to read the final root block of the requested resource
328+
// NOTE: this needs to happen before we go into content-type specific code paths
329+
_, err = i.api.Block().Get(r.Context(), resolvedPath)
330+
if err != nil {
331+
webError(w, "ipfs block get "+resolvedPath.Cid().String(), err, http.StatusServiceUnavailable)
332+
return
333+
}
334+
i.firstBlockGetMetric.WithLabelValues(parsedPath.Namespace()).Observe(time.Since(begin).Seconds())
335+
308336
// HTTP Headers
309337
i.addUserHeaders(w) // ok, _now_ write user's headers.
310338
w.Header().Set("X-Ipfs-Path", urlPath)
@@ -348,7 +376,6 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request
348376
webError(w, "ipfs cat "+escapedURLPath, err, http.StatusNotFound)
349377
return
350378
}
351-
// TODO: do we want to reuse unixfsGetMetric for block/car, or should we have separate ones?
352379
i.unixfsGetMetric.WithLabelValues(parsedPath.Namespace()).Observe(time.Since(begin).Seconds())
353380
defer dr.Close()
354381

core/corehttp/gateway_handler_block.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@ import (
1313
func (i *gatewayHandler) serveRawBlock(w http.ResponseWriter, r *http.Request, blockCid cid.Cid, contentPath ipath.Path) {
1414
blockReader, err := i.api.Block().Get(r.Context(), contentPath)
1515
if err != nil {
16-
webError(w, "failed to get block", err, http.StatusInternalServerError)
16+
webError(w, "ipfs block get "+blockCid.String(), err, http.StatusInternalServerError)
1717
return
1818
}
1919
block, err := ioutil.ReadAll(blockReader)
2020
if err != nil {
21-
webError(w, "failed to read block", err, http.StatusInternalServerError)
21+
webError(w, "ipfs block get "+blockCid.String(), err, http.StatusInternalServerError)
2222
return
2323
}
2424
content := bytes.NewReader(block)

core/corehttp/gateway_handler_car.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ func (i *gatewayHandler) serveCar(w http.ResponseWriter, r *http.Request, rootCi
4545

4646
if err := car.Write(w); err != nil {
4747
// TODO: can we do any error handling here?
48+
// TODO: idea: add best-effort proxy reader which will set http.StatusOK only if the first block is yielded correctly
4849
}
4950
}
5051

0 commit comments

Comments
 (0)