-
Notifications
You must be signed in to change notification settings - Fork 176
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Metrics] Fix issue with collecting REST Metrics #4452
[Metrics] Fix issue with collecting REST Metrics #4452
Conversation
return func(next http.Handler) http.Handler { | ||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { | ||
// This is a custom metric being called on every http request | ||
restCollector.AddTotalRequests(req.Context(), req.Method, req.URL.Path) | ||
restCollector.AddTotalRequests(req.Context(), serviceID, req.Method) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what would be the method? is it GET/PUT/POST
etc?
I think the only value for req.Method
is just Get
, so since serviceID is empty, it will just be a single metrics for total Get requests?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would it be more useful to have metrics on total requests to different path?
Get /v1/events/*
Get / v1/collections/*
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what would be the method? is it
GET/PUT/POST
etc?I think the only value for
req.Method
is justGet
, so since serviceID is empty, it will just be a single metrics for total Get requests?
Yes, the method would be GET and POST, as only two types are present in routers.
Would it be more useful to have metrics on total requests to different paths?
Get /v1/events/*
Get / v1/collections/*
Yeah, I did consider that option. However, we have various types of URLs, such as /v1/blocks/{id}/payload or /v1/accounts/{address}, with arguments id and address in different positions. To determine which group each URL belongs to, we would definitely need to implement some pattern-matching mechanism or something similar. Since we can't predict the exact position of this floating argument in a URL, it would require some additional work. The proper fix could be part of the REST refactoring, but for now, we could use this approach as a quick solution.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tbh, if we're only collecting metrics for GET/POST, I don't think it's worth collecting the data.
How about adding something like this:
var routeUrlMap = map[string]string{}
var routeRE = regexp.MustCompile("/v1/([\\w]+)(/([\\w]+)(/([\\w]+))?)?")
func init() {
for _, r := range rest.Routes {
routeUrlMap[r.Pattern] = r.Name
}
}
func URLToRoute(url string) (string, error) {
normalized, err := normalizeURL(url)
if err != nil {
return "", err
}
name, ok := routeUrlMap[normalized]
if !ok {
return "", fmt.Errorf("invalid url")
}
return name, nil
}
func normalizeURL(url string) (string, error) {
matches := routeRE.FindAllStringSubmatch(url, -1)
if len(matches) != 1 || len(matches[0]) != 6 {
return "", fmt.Errorf("invalid url")
}
// given a URL like
// /v1/blocks/1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef/payload
// groups [ 1 ] [ 3 ] [ 5 ]
// normalized form like /v1/blocks/{id}/payload
parts := []string{matches[0][1]}
switch len(matches[0][3]) {
case 0:
// top level resource. e.g. /v1/blocks
case 64:
// id based resource. e.g. /v1/blocks/1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef
parts = append(parts, "{id}")
case 16:
// address based resource. e.g. /v1/accounts/1234567890abcdef
parts = append(parts, "{address}")
default:
// named resource. e.g. /v1/network/parameters
parts = append(parts, matches[0][3])
}
if matches[0][5] != "" {
parts = append(parts, matches[0][5])
}
return "/" + strings.Join(parts, "/"), nil
}
then using the return from URLToRoute
as the handler name?
It's not very generic but probably good enough in the medium term.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, sorry! I missed messages because my notifications from GitHub go to spam. Yes, it should work, as I see you add a parser.
Codecov Report
@@ Coverage Diff @@
## master #4452 +/- ##
==========================================
- Coverage 54.09% 51.73% -2.37%
==========================================
Files 563 726 +163
Lines 56220 65570 +9350
==========================================
+ Hits 30414 33922 +3508
- Misses 23459 29040 +5581
- Partials 2347 2608 +261
Flags with carried forward coverage won't be shown. Click here to find out more.
|
…o into guitarheroua/fix-3971-fix-rest-api-metrics
…eroua/fix-3971-fix-rest-api-metrics
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks good. Thanks for fixing this up @Guitarheroua !
@Guitarheroua can you take a look at those lint errors? |
Hey, could you please take a look again at my last change, there was cyclic dependency and I fix it by passing URLToRoute as an argument to MetricsMiddleware, to avoid new refactoring and moving in this part of code base. |
metricsMiddleware := middleware.New(middleware.Config{Recorder: restCollector}) | ||
return func(next http.Handler) http.Handler { | ||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { | ||
routeName, err := urlToRoute(req.URL.Path) | ||
if err != nil { | ||
return |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks like there are a few REST unittests failing with empty responses. I'm guessing it's related to this return since it effectively drops the request. Can you take a look?
most likely, the tests just need to be updated to use more valid urls/inputs, but not 100% sure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks like there are a few REST unit tests failing with empty responses. I'm guessing it's related to this return since it effectively drops the request. Can you take a look?
Most likely, the tests just need to be updated to use more valid urls/inputs, but not 100% sure.
Could I, for example, on error just fill routeName with an "unknown" or "invalid" string? Because this function anyway supposes to return http.Handler
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yea, that sounds good. Let's go with unknown
bors merge |
4452: [Metrics] Fix issue with collecting REST Metrics r=peterargue a=Guitarheroua This fix is implemented in response to [a comment made in PR 4288 ](#4288 (comment)). After enabling metrics for REST, the system now generates metrics for each HTTP request based on the request method and URL path. However, when a path includes an ID, such as '**/v1/collection/12345/**' or '**/v1/collection/67890/**', a unique value is created for each path, leading to an issue. To address this problem, I have modified the reporting logic to generate metrics based on the service ID and request method. Currently, the service ID is empty, but there is potential for improvement in future iterations. Co-authored-by: Andrii Slisarchuk <andriyslisarchuk@gmail.com> Co-authored-by: Andrii Slisarchuk <Guitarheroua@users.noreply.github.com> Co-authored-by: Peter Argue <89119817+peterargue@users.noreply.github.com>
4452: [Metrics] Fix issue with collecting REST Metrics r=peterargue a=Guitarheroua This fix is implemented in response to [a comment made in PR 4288 ](#4288 (comment)). After enabling metrics for REST, the system now generates metrics for each HTTP request based on the request method and URL path. However, when a path includes an ID, such as '**/v1/collection/12345/**' or '**/v1/collection/67890/**', a unique value is created for each path, leading to an issue. To address this problem, I have modified the reporting logic to generate metrics based on the service ID and request method. Currently, the service ID is empty, but there is potential for improvement in future iterations. Co-authored-by: Andrii Slisarchuk <andriyslisarchuk@gmail.com> Co-authored-by: Andrii Slisarchuk <Guitarheroua@users.noreply.github.com> Co-authored-by: Peter Argue <89119817+peterargue@users.noreply.github.com>
4452: [Metrics] Fix issue with collecting REST Metrics r=peterargue a=Guitarheroua This fix is implemented in response to [a comment made in PR 4288 ](#4288 (comment)). After enabling metrics for REST, the system now generates metrics for each HTTP request based on the request method and URL path. However, when a path includes an ID, such as '**/v1/collection/12345/**' or '**/v1/collection/67890/**', a unique value is created for each path, leading to an issue. To address this problem, I have modified the reporting logic to generate metrics based on the service ID and request method. Currently, the service ID is empty, but there is potential for improvement in future iterations. Co-authored-by: Andrii Slisarchuk <andriyslisarchuk@gmail.com> Co-authored-by: Andrii Slisarchuk <Guitarheroua@users.noreply.github.com> Co-authored-by: Peter Argue <89119817+peterargue@users.noreply.github.com>
4452: [Metrics] Fix issue with collecting REST Metrics r=peterargue a=Guitarheroua This fix is implemented in response to [a comment made in PR 4288 ](#4288 (comment)). After enabling metrics for REST, the system now generates metrics for each HTTP request based on the request method and URL path. However, when a path includes an ID, such as '**/v1/collection/12345/**' or '**/v1/collection/67890/**', a unique value is created for each path, leading to an issue. To address this problem, I have modified the reporting logic to generate metrics based on the service ID and request method. Currently, the service ID is empty, but there is potential for improvement in future iterations. Co-authored-by: Andrii Slisarchuk <andriyslisarchuk@gmail.com> Co-authored-by: Andrii Slisarchuk <Guitarheroua@users.noreply.github.com> Co-authored-by: Peter Argue <89119817+peterargue@users.noreply.github.com>
4571: [Access] Update REST metrics to use route name for all types r=peterargue a=peterargue The REST metrics handler passes the original route into the collector, causing high cardinality and performance issues on the prometheus servers. We attempted to fix this with #4452, but only updated 1 of 4 metrics that were reporting the URL. This PR updates all metrics to use the mapped route name. It also removes a bunch of unnecessary config and aligns the collector with the other metrics collectors in flow-go Co-authored-by: Peter Argue <89119817+peterargue@users.noreply.github.com>
4571: [Access] Update REST metrics to use route name for all types r=peterargue a=peterargue The REST metrics handler passes the original route into the collector, causing high cardinality and performance issues on the prometheus servers. We attempted to fix this with #4452, but only updated 1 of 4 metrics that were reporting the URL. This PR updates all metrics to use the mapped route name. It also removes a bunch of unnecessary config and aligns the collector with the other metrics collectors in flow-go Co-authored-by: Peter Argue <89119817+peterargue@users.noreply.github.com>
This fix is implemented in response to a comment made in PR 4288 .
After enabling metrics for REST, the system now generates metrics for each HTTP request based on the request method and URL path. However, when a path includes an ID, such as '/v1/collection/12345/' or '/v1/collection/67890/', a unique value is created for each path, leading to an issue.
To address this problem, I have modified the reporting logic to generate metrics based on the service ID and request method. Currently, the service ID is empty, but there is potential for improvement in future iterations.