Skip to content
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

Fixes #119. Update http handlers for api and zipper to use gorilla/mux #207

Merged
merged 19 commits into from
Jul 15, 2020

Conversation

unjuli
Copy link
Contributor

@unjuli unjuli commented Sep 10, 2019

This provides functionality to add implicit redirect for endpoints with
optional forward slash and avoid adding redundant endpoints

Update:

  • Add middleware to trim trailing slash from the request path (& unit tests)
  • Update app tests to include testing for routes
  • Refactor app tests to run subtests in parallel
  • Refactor app tests to use httptest package for creating test requests

@unjuli unjuli requested a review from grzkv September 10, 2019 16:35
@unjuli unjuli self-assigned this Sep 10, 2019
@azhiltsov
Copy link
Contributor

Since this is changing the http library we are using for a while I would like to understand a performance implications of this change.

@unjuli
Copy link
Contributor Author

unjuli commented Sep 11, 2019

@azhiltsov @grzkv Any suggestions which tests to perform around this?

@unjuli unjuli requested a review from azhiltsov September 11, 2019 08:51
@unjuli
Copy link
Contributor Author

unjuli commented Sep 11, 2019

As per this benchmark (https://github.com/julienschmidt/go-http-routing-benchmark), we can try using https://github.com/julienschmidt/httprouter which seems to have the best performance vs memory consumption. Though, it doesn't seem to be maintained that frequently.

@unjuli unjuli requested review from jwkohnen and paskal September 11, 2019 11:15
Copy link
Member

@jwkohnen jwkohnen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The introduction of gorilla mux changes the behaviour. Before a request to e.g. /block-headers and /block-headers/ behaved indistinguishable. With this change a request to /block-headers is redirected with HTTP code 301 to /block-headers/. That's an extra round trip for the client and has some side effects:

The re-direct is a HTTP 301 (Moved Permanently). Note that when this is set for routes with a non-idempotent method (e.g. POST, PUT), the subsequent re-directed request will be made as a GET by most clients. Use middleware or client settings to modify this behaviour as needed.

and

Special case: when a route sets a path prefix using the PathPrefix() method, strict slash is ignored for that route because the redirect behavior can't be determined from a prefix alone. However, any subrouters created from that route inherit the original StrictSlash setting.

I suppose all incoming requests are already idempotent and all clients out there do automatic redirect following and the mentioned special case doesn't apply. But is that confirmed and the changes behaviour desired?

@grzkv
Copy link
Member

grzkv commented Sep 11, 2019

@azhiltsov Performance is not a concern here. Here's a benchmark (can't say it's very authoritative though). Even in the very worst case, we are looking at 1ms delay max. My guess is that delay will be in microseconds.

@grzkv
Copy link
Member

grzkv commented Sep 11, 2019

Regarding the muxer to use: I'd go with gorilla/mux. It has the most established reputation.

Copy link
Contributor

@azhiltsov azhiltsov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

301 HTTP redirects couldn't be handled by all kind of used external clients. I recall grafana was unable to handle this properly if handled from the client side (js in a browser)
If instead of doing internal redirect we would issue an external one than we most probably would cut-off some consumers. What make it worse - we would not even notice this.

@grzkv
Copy link
Member

grzkv commented Sep 11, 2019

I think, 301s is exactly what we are trying to fix with this PRs.

@grzkv
Copy link
Member

grzkv commented Sep 11, 2019

I would be nice to have routing tests with this PR.

Copy link
Member

@grzkv grzkv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Civil
Copy link
Contributor

Civil commented Sep 11, 2019

@grzkv main problem with 301s is what jwkohnen mentioned.

Grafana uses POST requests to send it's requests (as this allows arbitrary large requests).

If webserver replies with 301, the behavior of most of the HTTP Clients (including browsers) is to change the request to GET and drop the body[1].

So your request to /render with body target=foo will suddenly become just a /render with no parameters.

The correct behavior would be to return 308 instead[1], but then you might still encounter some HTTP Clients that won't understand 308. That's the reason why gorilla/mux doesn't do it by default[3] and that was original reason for having same handler function defined for both URL with and without / in the end.

[1] https://golang.org/pkg/net/http/#Client.Do - paragraph that starts with "If the server replies with a redirect"
[2] https://tools.ietf.org/html/rfc7538
[3] gorilla/mux#285

@azhiltsov
Copy link
Contributor

I think, 301s is exactly what we are trying to fix with this PRs.

$ curl -v 127.0.0.1:8091/render/?format=json

GET /render/?format=json HTTP/1.1
< HTTP/1.1 200 OK

$ curl -v 127.0.0.1:8091/render?format=json

GET /render?format=json HTTP/1.1
< HTTP/1.1 200 OK

@grzkv
Copy link
Member

grzkv commented Sep 12, 2019

@Civil Thanks for the additional info. Bottomline: 301 should be avoided. So, everything is OK. We are trying to avoid them. We should return 200, this is what we want to do.

@azhiltsov Yes, everything is correct. But this is only achieved by having duplicate routing paths, see

r.HandleFunc("/render/", httputil.TimeHandler(app.validateRequest(http.HandlerFunc(app.renderHandler), "render"), app.bucketRequestTimes))

This can be achieved by a single route with mux.

@paskal paskal removed their request for review October 29, 2019 13:40
@grzkv
Copy link
Member

grzkv commented Jan 22, 2020

Hey all! Let's move this PR to some final conclustion.

@grzkv
Copy link
Member

grzkv commented Jan 27, 2020

@avereha Can you please give some feedback?

@grzkv grzkv requested a review from avereha January 27, 2020 11:50
@avereha
Copy link
Contributor

avereha commented Jan 27, 2020

Graphite-web seem to be doing the same trick with "/?", for example:

https://github.com/graphite-project/graphite-web/blob/master/webapp/graphite/render/urls.py

I would look there for other examples/URLs and make sure we don't diverge too much.

Another approach that I noticed was to add middleware that removes tailing shashes. Not sure if this will add too many cases that would be different from graphite-web. Or we could make small muxer that wraps original muxer and supports registering two paths.
I think all approaches would work.

No matter what way we choose, I would still add tests for the next time we want to replace the muxer.

@unjuli unjuli force-pushed the anjsharma/refactor_mux branch from 19221c1 to 9a768ad Compare January 27, 2020 16:45
@unjuli unjuli changed the title [WIP] Fixes #119. Update http handlers for api and zipper to use gorilla/mux Fixes #119. Update http handlers for api and zipper to use gorilla/mux Jan 28, 2020
app/carbonapi/app_test.go Outdated Show resolved Hide resolved
app/carbonapi/app_test.go Outdated Show resolved Hide resolved
app/carbonapi/app_test.go Outdated Show resolved Hide resolved
app/carbonapi/app_test.go Outdated Show resolved Hide resolved
app/carbonapi/app_test.go Outdated Show resolved Hide resolved
app/carbonapi/app_test.go Show resolved Hide resolved
app/carbonapi/app_test.go Outdated Show resolved Hide resolved
app/carbonapi/routes.go Outdated Show resolved Hide resolved
app/carbonapi/routes_test.go Outdated Show resolved Hide resolved
@grzkv
Copy link
Member

grzkv commented Jan 29, 2020

@azhiltsov There should be not 301 redirect responses anymore. Could you please re-evaluate your review?

I'm trying to switch to go modules.

What we noticed in #190
is that after switching to go modules, this module gets updated.

In order to have a clean 'vendor' directory when switching to modules,
I'm first updating this library.

There are two functions using this library: linearRegression and polyfit.
@avereha avereha mentioned this pull request Jul 3, 2020
avereha added a commit that referenced this pull request Jul 6, 2020
What issue is this change attempting to solve?

Add support for sending traces in Jaeger format from carbonzipper and carbonapi.
Carbonapi is also able to read b3 trace headers that come from grafana.
How does this change solve the problem? Why is this the best approach?

I'm using this new dependency to send traces:

https://github.com/open-telemetry/opentelemetry-go
and
https://github.com/open-telemetry/opentelemetry-go-contrib/tree/master/instrumentation/github.com/gorilla/mux
for hooking up to gorilla mux.

Since we don't use gorilla mux yet, information in traces is missing route information, for example:

 HTTP POST route not found

but this should be fixed after #207

Also updated golang version in the travis tests to 1.14 to fix:

vendor/go.opentelemetry.io/otel/api/standard/http.go:242:25: undefined: http.StatusEarlyHints

How can we be sure this works as expected?

I ran multiple tests:

    tested that we can send traces to opentelemetry-collector and that we are receiving them on the other side.
    tested on one production server, there was no change in performance:
        with tracing disabled
        with tracing enabled
            when the collector is working
            when the collector is not responding/timing out
@grzkv grzkv requested review from jwkohnen, grzkv and azhiltsov July 9, 2020 10:39
@avereha
Copy link
Contributor

avereha commented Jul 9, 2020

The integration test is failing because go-carbon got moved to go-graphite. Will try to fix this is a separate PR because it is not related to this change.

Tested the latest version with production traffic and found there was no 3xx answer, which means we are handling tailing / OK.
Issues that I found:

  • curl -L -v localhost:8091/ goes in infinite redirect loop: curl: (47) Maximum (50) redirects followed
  • curl -L -v localhost:8091/qwfqewf answers with 404(better than before where we answered with 200). But the usage body is missing.

@grzkv
Copy link
Member

grzkv commented Jul 9, 2020

@avereha @anjali-sharma Is this a WIP or ready for review?

@unjuli unjuli added the wip label Jul 14, 2020
@unjuli
Copy link
Contributor Author

unjuli commented Jul 14, 2020

@grzkv It's a WIP. I'm looking into the issues reported by @avereha.

@unjuli
Copy link
Contributor Author

unjuli commented Jul 15, 2020

@avereha Added fix for the edge case (thanks for reporting!) and test for it. Also refactored the tests a bit to handle test App instance setup for the package.

@avereha avereha removed the wip label Jul 15, 2020
@avereha
Copy link
Contributor

avereha commented Jul 15, 2020

Tested again and everything works as expected.
Again, there was no 3xx answer.

@avereha avereha merged commit 6318bb0 into master Jul 15, 2020
@avereha avereha deleted the anjsharma/refactor_mux branch July 15, 2020 09:47
@avereha
Copy link
Contributor

avereha commented Jul 15, 2020

Thank you @anjali-sharma !

@unjuli unjuli linked an issue Dec 11, 2020 that may be closed by this pull request
6 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Improve HTTP muxer
6 participants