Skip to content

Commit

Permalink
feat: add first_block_get_latency_seconds
Browse files Browse the repository at this point in the history
This adds a new,
generic metric that aims to replace unixfs-specific unixfs_get_latency_seconds
  • Loading branch information
lidel committed Mar 8, 2022
1 parent 9fbfb0b commit fa78402
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 9 deletions.
41 changes: 34 additions & 7 deletions core/corehttp/gateway_handler.go
Expand Up @@ -64,8 +64,8 @@ type gatewayHandler struct {
config GatewayConfig
api coreiface.CoreAPI

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

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

func newGatewayHandler(c GatewayConfig, api coreiface.CoreAPI) *gatewayHandler {
unixfsGetMetric := prometheus.NewSummaryVec(
// TODO: deprecated, use first_block_get_latency_seconds instead
prometheus.SummaryOpts{
Namespace: "ipfs",
Subsystem: "http",
Name: "unixfs_get_latency_seconds",
Help: "The time till the first block is received when 'getting' a file from the gateway.",
Help: "The time till the first block is received when 'getting' a unixfs file from the gateway.",
},
[]string{"gateway"},
)
Expand All @@ -106,10 +107,28 @@ func newGatewayHandler(c GatewayConfig, api coreiface.CoreAPI) *gatewayHandler {
}
}

firstBlockGetMetric := prometheus.NewSummaryVec(
prometheus.SummaryOpts{
Namespace: "ipfs",
Subsystem: "http",
Name: "first_block_get_latency_seconds",
Help: "The time till the first block is successfully received when reading data from the gateway.",
},
[]string{"gateway"},
)
if err := prometheus.Register(firstBlockGetMetric); err != nil {
if are, ok := err.(prometheus.AlreadyRegisteredError); ok {
firstBlockGetMetric = are.ExistingCollector.(*prometheus.SummaryVec)
} else {
log.Errorf("failed to register firstBlockGetMetric: %v", err)
}
}

i := &gatewayHandler{
config: c,
api: api,
unixfsGetMetric: unixfsGetMetric,
config: c,
api: api,
unixfsGetMetric: unixfsGetMetric,
firstBlockGetMetric: firstBlockGetMetric,
}
return i
}
Expand Down Expand Up @@ -305,6 +324,15 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request
return
}

// Update the global metric of the time it takes to read the final root block of the requested resource
// NOTE: this needs to happen before we go into content-type specific code paths
_, err = i.api.Block().Get(r.Context(), resolvedPath)
if err != nil {
webError(w, "ipfs block get "+resolvedPath.Cid().String(), err, http.StatusServiceUnavailable)
return
}
i.firstBlockGetMetric.WithLabelValues(parsedPath.Namespace()).Observe(time.Since(begin).Seconds())

// HTTP Headers
i.addUserHeaders(w) // ok, _now_ write user's headers.
w.Header().Set("X-Ipfs-Path", urlPath)
Expand Down Expand Up @@ -348,7 +376,6 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request
webError(w, "ipfs cat "+escapedURLPath, err, http.StatusNotFound)
return
}
// TODO: do we want to reuse unixfsGetMetric for block/car, or should we have separate ones?
i.unixfsGetMetric.WithLabelValues(parsedPath.Namespace()).Observe(time.Since(begin).Seconds())
defer dr.Close()

Expand Down
4 changes: 2 additions & 2 deletions core/corehttp/gateway_handler_block.go
Expand Up @@ -13,12 +13,12 @@ import (
func (i *gatewayHandler) serveRawBlock(w http.ResponseWriter, r *http.Request, blockCid cid.Cid, contentPath ipath.Path) {
blockReader, err := i.api.Block().Get(r.Context(), contentPath)
if err != nil {
webError(w, "failed to get block", err, http.StatusInternalServerError)
webError(w, "ipfs block get "+blockCid.String(), err, http.StatusInternalServerError)
return
}
block, err := ioutil.ReadAll(blockReader)
if err != nil {
webError(w, "failed to read block", err, http.StatusInternalServerError)
webError(w, "ipfs block get "+blockCid.String(), err, http.StatusInternalServerError)
return
}
content := bytes.NewReader(block)
Expand Down
1 change: 1 addition & 0 deletions core/corehttp/gateway_handler_car.go
Expand Up @@ -45,6 +45,7 @@ func (i *gatewayHandler) serveCar(w http.ResponseWriter, r *http.Request, rootCi

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

Expand Down

0 comments on commit fa78402

Please sign in to comment.