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

API client doesn't support POST method for Query/QueryRange #428

Closed
jacksontj opened this issue Jul 11, 2018 · 25 comments · Fixed by #557
Closed

API client doesn't support POST method for Query/QueryRange #428

jacksontj opened this issue Jul 11, 2018 · 25 comments · Fixed by #557

Comments

@jacksontj
Copy link
Contributor

POST support was added to query/query_range API endpoints in prometheus with prometheus/prometheus#3322. This client currently only sends GET requests, which means that it isn't won't work for large query string values. As the API stands today:

Query(ctx context.Context, query string, ts time.Time) (model.Value, error)
QueryRange(ctx context.Context, query string, r Range) (model.Value, error)

There isn't really a place to put it, so I see a couple options:

  1. add a NewPOSTAPI method to return an httpAPI instance with a flag to use POST for both query and query_range (this would be done by setting an option in the httpAPI struct-- to be added).
  2. add QueryPost and QueryRangePost to the API interface
  3. add METHOD argument to Query and QueryRange

I'm not sure what the compatibility guarantees are on this client interface, so I'm not sure how we want to proceed. I'm leaning towards 1 or 2 (probably in that order)

cc @juliusv

@juliusv
Copy link
Member

juliusv commented Jul 11, 2018

What about a fourth option: the user intent is already clear (query a given PromQL expression with a given time range), so can we just either determine automatically whether we need GET or POST (from some maximum length that GET args can have?), or alternatively always use POST?

@juliusv
Copy link
Member

juliusv commented Jul 11, 2018

(the one thing against always using POST is that POST support was only introduced <1y ago, so probably some auto-detection would be best that just goes to POST for long queries)

@jacksontj
Copy link
Contributor Author

I think that's a great idea. How about I add an option to the client Config which is MaxHeaderSize, if that is set it'll switch to POST for queries that would be larger. That way if you don't define the max size in the config, it will always do GET. This also means the interface can remain the same.

@juliusv
Copy link
Member

juliusv commented Jul 11, 2018

Is it the HTTP headers in total that are size-limited, or the URL (that contains GET parameters) specifically?

@jacksontj
Copy link
Contributor Author

the HTTP headers themselves. I looked into adding it there, but the issue is that the client.Config struct isn't exposed to the API client since it takes a Client (which is an interface). So this config option would need to be at the API client level, or we'd have to add a Config() method to the Client interface to access it.

@juliusv
Copy link
Member

juliusv commented Jul 11, 2018

I want to understand this better. prometheus/prometheus#3072 was about URL size limits, not about HTTP headers in general. Browsers have URL size limits (like https://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers), but here already the client is not a browser anymore, but a Go HTTP client. So we only ever need to switch to POST in case there are also size limitations in either the Go HTTP client (used in this client lib) or in the Go HTTP server (used by the Prometheus server that handles the requests generated here). I just tried it out and I only seem to be hitting a limit (that causes a connection reset by peer) around the 1MB URL size, which I don't expect any PromQL query to ever hit. So maybe we actually never need to use POST here?

Of course there might also be HTTP proxies in the middle, but not sure what the common URL size limits are on those...

@jacksontj
Copy link
Contributor Author

The issue reported was about browser limits, but those don't apply too much to this client interface since we are talking from go. In this case the main limitation is the server itself. Go's server struct has an option called MaxHeaderBytes which determines the max total size of headers it will allow. By default this is 1MB -- so as you point out this might be fine to punt on for now, because a >1MB header likely means a MONSTROUS promql query ;)

Assuming this is still something we want, I think having an option for setting a limit and then having the client work around that limit would be good. That way if you do have proxies in the middle we can swap to using POST etc.

@juliusv
Copy link
Member

juliusv commented Jul 12, 2018

I'm just unsure about the situation where an older server doesn't support POST yet and the client lib still switches to it because the query size is too large. It would result in a Method not Allowed HTTP status code where previously queries were working, and that could be more confusing than getting a more topical HTTP response code. Instead of having a user worry about configuring max header sizes, how about letting them specify the minimal Prometheus version they are targeting and always use POST if the version supports it, and always GET otherwise?

@beorn7
Copy link
Member

beorn7 commented Jul 12, 2018

Thanks, @juliusv for chiming in. Assigning this to @krasi-georgiev as per https://github.com/prometheus/client_golang/blob/master/MAINTAINERS.md for shepherding.

@jacksontj
Copy link
Contributor Author

jacksontj commented Jul 12, 2018

I'm just unsure about the situation where an older server doesn't support POST yet and the client lib still switches to it because the query size is too large.

This is why I'd leave the default config option for MaxHeaderBytes to 0, and default to GET. This would mean the client caller would need to specifically set a limit that would tell us to swap to POST requests. I think that'll be less of a maintenance headache as we don't have anysort of prom version discovery stuff built into this client, nor do I think we really want to :)

@juliusv
Copy link
Member

juliusv commented Jul 12, 2018

I don't think it'd be unreasonable to make the client lib explicitly aware of the version of the Prometheus server on the other side. The API user will have to know the Prometheus version in any case to make use of the new POST features.

It's a question of how high- or low-level we want this control knob to be, and whether we'll want to do more future version-dependent switches. From the user's perspective, which explanation would be more straightforward: how MaxHeaderBytes interplays with different Prometheus versions and query sizes and how it will still lead to misleading error messages if set incorrectly, or just explaining how setting the Prometheus version >X enables support for larger queries?

But honestly, I would love to hear someone else's opinion on this too.

@jacksontj
Copy link
Contributor Author

I prefer the more direct control (in this case the MaxHeaderBytes). The main reason being we don't know how they'll use the client. For example, if they where to talk to prom behind some proxy they'd need to set their own header limit which is independent of the prom version. I'm also fine to get some other opinions in here, but to me it seems that we at lest want this option. IMO if we're going down the route of differing the client based on version we may want to create separate interfaces for that -- as APIs come and go across prom versions (instead of a single API interface, have the various sub-versions).

@krasi-georgiev
Copy link
Contributor

what about using compatbility matrix , similar to the k8s client
https://github.com/kubernetes/client-go#compatibility-matrix

@krasi-georgiev
Copy link
Contributor

so nothing special in code , just always use post and in the readme mention that client version 0.9.1 and above works with prometheus version ....

@jacksontj
Copy link
Contributor Author

That would work assuming the maintainers are okay with managing N versions for support, otherwise this client will immediately kick off anyone <2.2 (and similarly for other changes in the future).

That is really a question of what the support and compatibility guarantees are on this library.

@juliusv
Copy link
Member

juliusv commented Jul 13, 2018

I don't think anyone here is up for managing client library versions to that degree and so I think just dropping support for older 2.x Proms in newer versions of this library is definitely not a good idea.

The case about proxy servers requiring a smaller MaxHeaderBytes is a good one. Ok, but now I set it to a particular value and I have an older Prom and get back a Method not Allowed error on larger queries, while smaller queries work just fine. That is confusing and unexpected. So I think yes, we might need to control the MaxHeaderBytes, but just unconditionally switching to POST when a query is larger than allowed doesn't seem right either, as it requires the right Prometheus version to work. If we knew that the right Prometheus version was on the other end, we could just always use POST to begin with, which solves any header size issues also for proxy servers (and otherwise POST won't help anyway, even with proxies). So I still think letting the library user specify the version on a client is a better way to go.

@krasi-georgiev
Copy link
Contributor

I don't think anyone here is up for managing client library versions to that degree and so I think just dropping support for older 2.x Proms in newer versions of this library is definitely not a good idea.

I see what you mean. Basically if there is a bug in the client lib and the user is still using a Prom version that doesn't support POST we would need to backport the fix.

I agree that few more if statements based on provided Prom version to the client would require least maintenance.

@jacksontj I am happy to review a PR if you have the time.

@jacksontj
Copy link
Contributor Author

now I set it to a particular value and I have an older Prom and get back a Method not Allowed error on larger queries, while smaller queries work just fine. That is confusing and unexpected

My thinking would be that the default of this off stays GETs forever, and we document that setting this option will only work for prom v2.x and up. So by you the client setting that option you are saying you are at least that version. This seems like the least maintenance to me, especially since the difference goes away after prom 1.x support is dropped.

If we want to add switching based on the prom version, the idea is that we'd unconditionally do GET/POST based soely on the version?

@brian-brazil
Copy link
Contributor

How about using POST and falling back to GET if we get that the method isn't supported?

@krasi-georgiev
Copy link
Contributor

that is a good idea as well.
I am ok with both ideas.

@juliusv
Copy link
Member

juliusv commented Jul 14, 2018

POST first and GET as fallback sounds good to me too, slight preference from my side to do it unconditionally, then we don't even need the MaxHeaderBytes option.

@krasi-georgiev krasi-georgiev removed their assignment Oct 29, 2018
@jranson
Copy link

jranson commented Feb 9, 2019

Any progress on this issue?

@beorn7
Copy link
Member

beorn7 commented Feb 9, 2019

Assigning to @krasi-georgiev for tracking.

@krasi-georgiev
Copy link
Contributor

krasi-georgiev commented Feb 10, 2019

Happy to review a PR , but don't think I will have time to do the actual implementation so contributions are welcome.

@krasi-georgiev
Copy link
Contributor

@jacksontj Any chance for a PR? I think we have a pretty good plan for action here so I can review it quickly.

jacksontj added a commit to jacksontj/client_golang that referenced this issue Apr 23, 2019
jacksontj added a commit to jacksontj/client_golang that referenced this issue Apr 23, 2019
Signed-off-by: Thomas Jackson <jacksontj.89@gmail.com>

Fixes prometheus#428
jacksontj added a commit to jacksontj/client_golang that referenced this issue Apr 24, 2019
Signed-off-by: Thomas Jackson <jacksontj.89@gmail.com>

Fixes prometheus#428
jacksontj added a commit to jacksontj/client_golang that referenced this issue Apr 24, 2019
Signed-off-by: Thomas Jackson <jacksontj.89@gmail.com>

Fixes prometheus#428
jacksontj added a commit to jacksontj/client_golang that referenced this issue Apr 24, 2019
Signed-off-by: Thomas Jackson <jacksontj.89@gmail.com>

Fixes prometheus#428
jacksontj added a commit to jacksontj/client_golang that referenced this issue Apr 24, 2019
Signed-off-by: Thomas Jackson <jacksontj.89@gmail.com>

Fixes prometheus#428
jacksontj added a commit to jacksontj/client_golang that referenced this issue Apr 25, 2019
Signed-off-by: Thomas Jackson <jacksontj.89@gmail.com>

Fixes prometheus#428
jacksontj added a commit to jacksontj/client_golang that referenced this issue Apr 25, 2019
Signed-off-by: Thomas Jackson <jacksontj.89@gmail.com>

Fixes prometheus#428
jacksontj added a commit to jacksontj/client_golang that referenced this issue Apr 26, 2019
Signed-off-by: Thomas Jackson <jacksontj.89@gmail.com>

Fixes prometheus#428
jacksontj added a commit to jacksontj/client_golang that referenced this issue Apr 29, 2019
Signed-off-by: Thomas Jackson <jacksontj.89@gmail.com>

Fixes prometheus#428
jacksontj added a commit to jacksontj/client_golang that referenced this issue Apr 16, 2023
The upstream prometheus HTTP API supports POSTS for these methods (the
same as Query and QueryRange). Similar to the original issue
(prometheus#428) we can hit 414
errors with these other APIs. This change simply duplicates the logic to
these other endpoints

Related to: jacksontj/promxy#588

Signed-off-by: Thomas Jackson <jacksontj.89@gmail.com>
jacksontj added a commit to jacksontj/client_golang that referenced this issue Apr 16, 2023
The upstream prometheus HTTP API supports POSTS for these methods (the
same as Query and QueryRange). Similar to the original issue
(prometheus#428) we can hit 414
errors with these other APIs. This change simply duplicates the logic to
these other endpoints
bwplotka pushed a commit that referenced this issue Apr 16, 2023
…lback (#1252)

The upstream prometheus HTTP API supports POSTS for these methods (the
same as Query and QueryRange). Similar to the original issue
(#428) we can hit 414
errors with these other APIs. This change simply duplicates the logic to
these other endpoints

Related to: jacksontj/promxy#588

Signed-off-by: Thomas Jackson <jacksontj.89@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants