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

added streaming remote read client side functionality #7354

Closed
wants to merge 1 commit into from

Conversation

Sudhar287
Copy link

@Sudhar287 Sudhar287 commented Jun 6, 2020

For #5926
Thank you to @YaoZengzeng for his initial design. I've implemented his comments from here.
FYI @bwplotka

Copy link
Member

@bwplotka bwplotka left a comment

Choose a reason for hiding this comment

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

This actually looks quite amazing (: I suggested couple of things but with some more work this will work well 💪 Thanks!

@@ -168,8 +172,7 @@ func (c *Client) Read(ctx context.Context, query *prompb.Query) (*prompb.QueryRe
httpReq.Header.Set("User-Agent", userAgent)
httpReq.Header.Set("X-Prometheus-Remote-Read-Version", "0.1.0")

ctx, cancel := context.WithTimeout(ctx, c.timeout)
defer cancel()
httpResp, err := c.client.Do(httpReq)
Copy link
Member

Choose a reason for hiding this comment

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

why we use request before we put context into it? (:

storage/remote/client.go Outdated Show resolved Hide resolved
storage/remote/codec.go Outdated Show resolved Hide resolved
storage/remote/codec.go Show resolved Hide resolved
storage/remote/codec.go Outdated Show resolved Hide resolved
Copy link
Member

@bwplotka bwplotka left a comment

Choose a reason for hiding this comment

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

I think you asumption is right, I commented where you can find the answer (just giving you an answer would not teach you anything ❤️ )

One suggestion: Please do not leave dead or unfinished code you don't want to merge in a PR. Add your comments as GitHub comment. Otherwise it's hard to review something which is .. not ready. So there is nothing to approve / review really (: Making sure you keep PR reviewable / ready to go even if blocked, makes sure you will get it merged sooner! (:

I think assumption of @wwformat was totally alright. Did you try that before reaching me on slack? 😉

storage/remote/codec.go Outdated Show resolved Hide resolved
storage/remote/codec.go Outdated Show resolved Hide resolved
@Sudhar287
Copy link
Author

Sorry I didn't try @wwformat suggestion before reaching you on slack because I thought using &prompb.ChunkedSeries is right as no objections about it were raised on my previous PR. But now I think I should have done it. Sorry!

@Sudhar287
Copy link
Author

Actively working on the fix and some issues with TestEndpoints. Will try to get the revision within a week. :-)

@Sudhar287 Sudhar287 force-pushed the remoteReadClient branch 7 times, most recently from 752c227 to 81ba12f Compare July 14, 2020 04:51
Copy link
Member

@bwplotka bwplotka left a comment

Choose a reason for hiding this comment

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

Wow!

This is actually high-quality code now 💪 Thanks for this. 🎉

I am approving, but let's merge when last comments will be addressed 🤗

storage/remote/client.go Show resolved Hide resolved
tsdb/tsdbutil/buffer.go Outdated Show resolved Hide resolved
Copy link
Author

@Sudhar287 Sudhar287 left a comment

Choose a reason for hiding this comment

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

Thanks. PTAL.


if httpResp.StatusCode/100 != 2 {
io.Copy(ioutil.Discard, httpResp.Body)
httpResp.Body.Close()
Copy link
Author

Choose a reason for hiding this comment

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

I hope this is what you meant.

Copy link
Member

Choose a reason for hiding this comment

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

Why not having:

	defer func() {
		io.Copy(ioutil.Discard, httpResp.Body)
		httpResp.Body.Close()
}()

here?

Copy link
Author

Choose a reason for hiding this comment

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

Really sorry Bartek. Missed the notification.

Copy link
Member

Choose a reason for hiding this comment

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

Still not addressed (:

Copy link
Member

Choose a reason for hiding this comment

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

still not addressed, in case below with unsupported contentType the response body will be not closed/exhausted leaving the connection alive without good reason.

Copy link
Author

Choose a reason for hiding this comment

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

To solve the problem with the unsupported contentType, is the additional line here enough to solve the issue?

	if !strings.HasPrefix(contentType, "application/x-streamed-protobuf; proto=prometheus.ChunkedReadResponse") {
-->		httpResp.Body.Close()
		return nil, errors.Errorf("not supported remote read content type: %s", contentType)
	}

Copy link
Member

Choose a reason for hiding this comment

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

There is one easy fix here. Just defer all (exhaust + close body) once (: and that's it. And don't double close in handleStreamedResponse (: WDYT?

Copy link
Author

Choose a reason for hiding this comment

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

Hmm, I considered the above option but won't it be incorrect?
for eg: We're going to return return &chunkedResponseSeriesSet{stream: stream, httpResp: httpResp}, nil in StartRead() and thus if we have

defer func() {
		io.Copy(ioutil.Discard, httpResp.Body)
		httpResp.Body.Close()
        }()

before the return &chunkedResponseSeriesSet{stream: stream, httpResp: httpResp}, nil we will end up closing the http connection that is needed for stream.NextProto(res).
From my understanding, the most straightforward option seems to be to close the HttpRest individually/separately depending on the use case.

Copy link
Member

Choose a reason for hiding this comment

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

well there is better way. You are right that we cannot do it for the case when we return iterator. So in this case I would do this.:

defer func() {
            if err != nil {
		io.Copy(ioutil.Discard, httpResp.Body)
		httpResp.Body.Close()
              }
        }()

Then make sure the function returns (<something>, err error) and it will only close on error (:

Copy link
Author

@Sudhar287 Sudhar287 Sep 27, 2020

Choose a reason for hiding this comment

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

I think you mean return streamedResponse, streamedResponse.err This makes sense to me. (I have a small clarifying question though)
But, I can see that won't be returning an error when we see an io.EOF in the chunkedResponseSeriesSet Next(). The connection needs to be closed in this case too, right?
Should the Next() be replaced from

if err := c.stream.NextProto(res); err != nil {
		if err != io.EOF {
			c.err = err
		}
		return false
	}

Replaced to

if err := c.stream.NextProto(res); err != nil {
		c.err = err
		return false
	}

This above approach produces some errors like:
api_test.go:1784: Unexpected error: execution: EOF
Or is this the best version:

if err := c.stream.NextProto(res); err != nil {
                httpResp.Body.Close()		
                if err != io.EOF {
			c.err = err
		}
		return false
	}

Copy link
Member

@bwplotka bwplotka left a comment

Choose a reason for hiding this comment

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

Some important comments not addressed, and still, there is a way this code will leak resources.
):

But super close!


if httpResp.StatusCode/100 != 2 {
io.Copy(ioutil.Discard, httpResp.Body)
httpResp.Body.Close()
Copy link
Member

Choose a reason for hiding this comment

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

Why not having:

	defer func() {
		io.Copy(ioutil.Discard, httpResp.Body)
		httpResp.Body.Close()
}()

here?

storage/remote/codec_test.go Outdated Show resolved Hide resolved
@Sudhar287 Sudhar287 force-pushed the remoteReadClient branch 2 times, most recently from 32a4803 to e6b4942 Compare August 2, 2020 00:50
Copy link
Member

@bwplotka bwplotka left a comment

Choose a reason for hiding this comment

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

Stil potential leak place let's fix (:

}

if httpResp.StatusCode/100 != 2 {
defer func() {
Copy link
Member

Choose a reason for hiding this comment

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

this does not help.

Please look on this function and think about what are the cases when we need to close the body and exhaust? I think you are missing one.

I think because we run functions it's totally ok to defer this in all cases JUST after we get response. and let's make sure nothing closes twice inside functions WDYT?

Copy link
Author

Choose a reason for hiding this comment

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

Thanks for taking a look at my code Bartek.
I think you are referring to closing the httpRest.Body over here:

func (c *client) handleStreamedResponse(httpResp *http.Response) (storage.SeriesSet, error) {
	stream := NewChunkedReader(httpResp.Body, DefaultChunkedReadLimit, nil)
	return &chunkedResponseSeriesSet{stream: stream}, nil
}

So I have to close() the bufio.Reader in ChunkedReader?

Copy link
Author

Choose a reason for hiding this comment

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

PLTA! :-)

Copy link
Member

Choose a reason for hiding this comment

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

Suggested something here: #7354 (comment)

@Sudhar287 Sudhar287 force-pushed the remoteReadClient branch 2 times, most recently from 5b62052 to b846c93 Compare August 9, 2020 09:42
Copy link
Member

@bwplotka bwplotka left a comment

Choose a reason for hiding this comment

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

I see two things missing. Otherwise LGTM!

storage/remote/codec.go Outdated Show resolved Hide resolved
storage/remote/codec.go Outdated Show resolved Hide resolved
Copy link
Member

@bwplotka bwplotka left a comment

Choose a reason for hiding this comment

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

👋

Let's get back to this if you want (: Added a few comments, otherwise good 👍 Also some old comments are still not addressed (again). Just letting you know that it's natural that if maintainers see that their comments are not addressed (nothing done, nothing responded, nothing adjusted, just ignored) it might mean that ther time is being wasted. If you wish to contribute and have PR merged fasted, you need to keep attention to details like this. (:


if httpResp.StatusCode/100 != 2 {
io.Copy(ioutil.Discard, httpResp.Body)
httpResp.Body.Close()
Copy link
Member

Choose a reason for hiding this comment

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

still not addressed, in case below with unsupported contentType the response body will be not closed/exhausted leaving the connection alive without good reason.


return resp.Results[0], nil
func (c *Client) handleStreamedResponse(httpResp *http.Response) (storage.SeriesSet, error) {
stream := NewChunkedReader(httpResp.Body, DefaultChunkedReadLimit, nil)
Copy link
Member

Choose a reason for hiding this comment

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

This method might be too shallow I would just inline this.

Copy link
Author

Choose a reason for hiding this comment

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

Done.

storage/remote/codec.go Outdated Show resolved Hide resolved
Copy link
Member

@bwplotka bwplotka left a comment

Choose a reason for hiding this comment

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

Also it's ok to say that you are busy and cannot complete this PR. Let's just close it and allow others to finish this if they have time 👍 (:

storage/remote/codec.go Outdated Show resolved Hide resolved
Copy link
Author

@Sudhar287 Sudhar287 left a comment

Choose a reason for hiding this comment

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

Hey Bartek, I have some small queries. Can you please check them out when you're available? :)

Copy link
Member

@bwplotka bwplotka left a comment

Choose a reason for hiding this comment

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

Nice, responded for missed comment, sorry for missing it.

storage/remote/codec.go Outdated Show resolved Hide resolved
storage/remote/codec.go Outdated Show resolved Hide resolved
Copy link
Member

@bwplotka bwplotka left a comment

Choose a reason for hiding this comment

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

Nice, just few comments and LGTM! (: Thanks for patience 👍

storage/remote/client.go Show resolved Hide resolved
@@ -229,3 +234,160 @@ func TestMergeLabels(t *testing.T) {
testutil.Equals(t, tc.expected, MergeLabels(tc.primary, tc.secondary))
}
}

type TestSample struct {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
type TestSample struct {
type sample struct {

Copy link
Author

Choose a reason for hiding this comment

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

This is not possible as it collides with this

type sample struct {

Anyway, I have replaced it to testSample

@bwplotka
Copy link
Member

Can you also resolve comments which are addressed to keep this PR discussion clear?

@bwplotka
Copy link
Member

Tests are definitely failing because of this PR. I can see the endpoint tests are failing with context.Cancelled. Maybe we close connection sooner than we should?

Signed-off-by: Sudharshann D <sudhar287@gmail.com>
@stale stale bot added the stale label Dec 16, 2020
Base automatically changed from master to main February 23, 2021 19:36
@stale stale bot removed the stale label Feb 23, 2021
@tomwilkie
Copy link
Member

This has been idle for a couple of months now, and @wwformat wants to continue work on it in #8351 - I suggest we close this and continue there?

@roidelapluie
Copy link
Member

We have looked at this pull request during our bug scrub.

This is now a duplicate of #11379.

Thank you for your contribution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants