Skip to content

Support remote write v2 by converting request #6330

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

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

SungJin1212
Copy link
Member

@SungJin1212 SungJin1212 commented Nov 11, 2024

This PR supports Prometheus remote write 2.0 by converting the v2 request to v1 at the API.

Which issue(s) this PR fixes:
Fixes #6324

Checklist

  • Tests updated
  • Documentation added
  • CHANGELOG.md updated - the order of entries should be [CHANGE], [FEATURE], [ENHANCEMENT], [BUGFIX]

@SungJin1212 SungJin1212 force-pushed the Add-remote-write-v2-api branch 6 times, most recently from 07bbbee to 83d0ba6 Compare November 11, 2024 11:25
@yeya24
Copy link
Contributor

yeya24 commented Nov 14, 2024

Looks promising. Thanks!

FYI we have prometheus/client_golang#1658 which exports remote write handler. Not a blocker for this PR but we should keep it on our radar to switch to use the library

@SungJin1212
Copy link
Member Author

@yeya24
Thanks for letting me know.
Should we make the issue to track it?

@alanprot
Copy link
Member

Maybe we can open a issue for someone give a try to use the client_golang handler even before it get merged so we can give feedback on the open PR. Changing that handler after is merged probably will be more difficult as it could potentially break all projects that are already using it.

@SungJin1212
Copy link
Member Author

@alanprot
I added a comment here: #6324

@yeya24
Copy link
Contributor

yeya24 commented Nov 17, 2024

I took a breif look at prometheus/client_golang#1658. Left some comments there and we have some changes Cortex specific that might not make sense for Prometheus. I think we are ok to proceed with this PR first.

@SungJin1212
Copy link
Member Author

@yeya24
Thanks. I read it, and it would be good if we could reuse its functions!

}
case config.RemoteWriteProtoMsgV2:
var req writev2.Request
err := util.ParseProtoReader(ctx, r.Body, int(r.ContentLength), maxRecvMsgSize, &req, util.RawSnappy)
Copy link
Contributor

Choose a reason for hiding this comment

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

@alanprot @danielblando
I wonder if we want to introduce a feature flag to control the behavior for RW v2 request. We can either ignore the request or convert to v1 and in the future maybe just accept as is.

Copy link
Contributor

Choose a reason for hiding this comment

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

Let's add a feature flag for the purpose of rollout. If RW 2.0 conversion is enabled right away, then Ingesters need to be rolled out first because of the protocol change to return stats. If we want to rollout Ingester and Distributor the same time then things can go wrong without a feature flag.

return &cortexpb.WriteResponse{}, nil
writeResponse := &cortexpb.WriteResponse{
Samples: int64(succeededSamplesCount),
Histograms: int64(succeededSamplesCount), //TODO(Sungjin1212): Should we count histogram?
Copy link
Contributor

Choose a reason for hiding this comment

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

How is this implemented in Prometheus. Why we don't count histogram here?

Copy link
Member Author

Choose a reason for hiding this comment

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

I left TODO since we are counting histograms just as we count the sample.
But, the Prometheus is counting native histogram https://github.com./prometheus/prometheus/blob/main/storage/remote/write_handler.go#L424.

Copy link
Member Author

Choose a reason for hiding this comment

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

How about starting to count histogram when we introduce PushV2?

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't understand the concern here. There is nothing prevent us doing it. We should count native histograms

Copy link
Member Author

Choose a reason for hiding this comment

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

yes, I agree with counting native histograms by changing to Histograms: int64(nativeHistogramCount).
My concern is we are counting samples instead of native histograms
https://github.com./cortexproject/cortex/blob/master/pkg/ingester/ingester.go#L1269

Copy link
Contributor

Choose a reason for hiding this comment

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

I am fine to split but why it needs a separate PR? We can just add a new int64 variable to count succeeded histograms

Copy link
Member Author

@SungJin1212 SungJin1212 Nov 25, 2024

Choose a reason for hiding this comment

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

Maybe some changes are needed like we are tracking ingestionRate by calculating succeededSamplesCount + ingestedMetadata.
We should change the calculation to sustain existing behavior to succeededSamplesCount + succeededHistogramCount + ingestedMetadata
Also, we can introduce new metrics like cortex_ingester_ingested_native_histograms_total and cortex_ingester_ingested_histograms_failures_total.
WDYT?

Copy link
Contributor

@yeya24 yeya24 Nov 25, 2024

Choose a reason for hiding this comment

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

I am ok to add the new metrics. But they are not blocking this PR so can be done either now or after this change.

If we don't add new metrics just track succeeded histogram samples, it is a simple change.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ok, I will make PR soon!

Copy link
Member Author

Choose a reason for hiding this comment

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

@yeya24
I make the PR addressing it! (#6370)

@@ -816,7 +824,7 @@ func (d *Distributor) doBatch(ctx context.Context, req *cortexpb.WriteRequest, s
}
}

return d.send(localCtx, ingester, timeseries, metadata, req.Source)
return d.send(localCtx, ingester, timeseries, metadata, req.Source, stats)
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need to sum the samples pulled from stats? Now I see we just overwrite stats for every request.

Copy link
Member Author

Choose a reason for hiding this comment

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

If so, isn't there a good chance the returned header value (X-Prometheus-Remote-Write-Samples-Written) would be multiple of samples in a write request?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah. With replication factor it is expected to have more samples. I think this is fine.

}
case config.RemoteWriteProtoMsgV2:
var req writev2.Request
err := util.ParseProtoReader(ctx, r.Body, int(r.ContentLength), maxRecvMsgSize, &req, util.RawSnappy)
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's add a feature flag for the purpose of rollout. If RW 2.0 conversion is enabled right away, then Ingesters need to be rolled out first because of the protocol change to return stats. If we want to rollout Ingester and Distributor the same time then things can go wrong without a feature flag.

@SungJin1212 SungJin1212 force-pushed the Add-remote-write-v2-api branch from 83d0ba6 to 6ce5027 Compare November 27, 2024 01:57
@SungJin1212
Copy link
Member Author

SungJin1212 commented Nov 27, 2024

@yeya24
I added -distributor.remote-write2-enabled flags to configure whether the Distributor can accept PRW2.0.
I added the TestIngesterRollingUpdate e2e test, where the Distributor can accept PRW2.0 and the Ingester uses the v1.18.1 image.
The result is PRW2.0 push is a success, but the response header (X-Prometheus-Remote-Write-xxx) values are all "0".
Is it expecting behavior?

@SungJin1212 SungJin1212 force-pushed the Add-remote-write-v2-api branch from 6ce5027 to 5344155 Compare November 28, 2024 09:28
@yeya24
Copy link
Contributor

yeya24 commented Dec 3, 2024

The result is PRW2.0 push is a success, but the response header (X-Prometheus-Remote-Write-xxx) values are all "0".
Is it expecting behavior?

This doesn't sound like the right behavior. Is that what you got with this PR?

@SungJin1212
Copy link
Member Author

SungJin1212 commented Dec 3, 2024

@yeya24
Yes, the test condition is that Ingester uses a v1.18.1 image and Distributor uses a PRW2.0-implemented one. The PRW2.0 push then gets that result.
If the Ingester and Distributor use the same images (PRW 2.0 implemented), we can get the expected response header.

@yeya24
Copy link
Contributor

yeya24 commented Dec 3, 2024

Yes, the test condition is that Ingester uses a v1.18.1 image and Distributor uses a PRW2.0-implemented one. The PRW2.0 push then gets that result.

I see what you meant. Then it is expected to get that result if you use Ingester of old version and Distributor of new version.
That's why we introduce the PRW 2.0 feature flag in distributor to only enable PRW 2.0 request if backend Ingester is running the newer version.

@SungJin1212
Copy link
Member Author

SungJin1212 commented Dec 3, 2024

@yeya24
Should I add comments to -distributor.remote-write2-enabled so that the user can do a rolling update of the Ingesters first and then update the Distributor afterward?

@yeya24
Copy link
Contributor

yeya24 commented Dec 3, 2024

We can mention it in the flag description but I doubt users really look at it.
I prefer to create a dedicated doc/guide for users to migrate to Prometheus 3.0

@SungJin1212
Copy link
Member Author

@yeya24
Yes, the guide docs would be more good.

@SungJin1212 SungJin1212 force-pushed the Add-remote-write-v2-api branch from 5344155 to a9231e4 Compare December 18, 2024 11:02
@SungJin1212 SungJin1212 force-pushed the Add-remote-write-v2-api branch from a9231e4 to 8ec7204 Compare January 14, 2025 06:34
@CharlieTLe
Copy link
Member

Hello @SungJin1212, thank you for opening this PR.

There is a release in progress. As such, please rebase your CHANGELOG entry on top of the master branch and move the CHANGELOG entry to the top under ## master / unreleased.

Thanks,
Charlie

@SungJin1212 SungJin1212 force-pushed the Add-remote-write-v2-api branch from 8ec7204 to 521ece7 Compare January 31, 2025 01:42
@SungJin1212 SungJin1212 force-pushed the Add-remote-write-v2-api branch 2 times, most recently from f269bc8 to 090be16 Compare February 4, 2025 06:19
@yeya24
Copy link
Contributor

yeya24 commented Mar 3, 2025

I think it is time to revisit this now : )

@SungJin1212 SungJin1212 force-pushed the Add-remote-write-v2-api branch 6 times, most recently from d562e33 to c1e3e04 Compare March 10, 2025 02:02
@SungJin1212 SungJin1212 force-pushed the Add-remote-write-v2-api branch from c1e3e04 to cd9d024 Compare April 8, 2025 07:11
@yeya24 yeya24 requested review from danielblando and alanprot April 8, 2025 23:32
@yeya24
Copy link
Contributor

yeya24 commented Apr 8, 2025

Ping for review. @danielblando @alanprot @friedrichg

Comment on lines 56 to 72
contentType := r.Header.Get("Content-Type")
if contentType == "" {
contentType = appProtoContentType
}

msgType, err := parseProtoMsg(contentType)
if err != nil {
level.Error(logger).Log("err", err.Error())
http.Error(w, err.Error(), http.StatusBadRequest)
level.Error(logger).Log("Error decoding remote write request", "err", err)
http.Error(w, err.Error(), http.StatusUnsupportedMediaType)
return
}

req.SkipLabelNameValidation = false
if req.Source == 0 {
req.Source = cortexpb.API
if msgType != config.RemoteWriteProtoMsgV1 && msgType != config.RemoteWriteProtoMsgV2 {
level.Error(logger).Log("Not accepted msg type", "msgType", msgType, "err", err)
http.Error(w, err.Error(), http.StatusUnsupportedMediaType)
return
}
Copy link
Member

Choose a reason for hiding this comment

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

Thinking out loud:
Should we be in the safe side and fallback to v1 if the content-type is not expected?

I guess if right now u send a weird content type cortex will acccept? and after this change we would return "StatusUnsupportedMediaType"?

Copy link
Member Author

@SungJin1212 SungJin1212 Apr 11, 2025

Choose a reason for hiding this comment

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

The PRW2.0 spec says the receiver should return StatusUnsupportedMediaType.

Would it be more nicer if we fallback to v1 when the StatusUnsupportedMediaType case?

Copy link
Member

@alanprot alanprot Apr 11, 2025

Choose a reason for hiding this comment

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

I think so? we don’t wanna to break v1 clients I guess ? Maybe we should check what v1 protocol says in this case instead v2 … cause we cannot assume v2 protocol behavior if we don’t know what version the request is as the content type is “unexpected” ?

Copy link
Member Author

Choose a reason for hiding this comment

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

How about enabling check content-type if remoteWrite2Enabled is true?

Copy link
Member Author

Choose a reason for hiding this comment

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

I changed not to break existing behavior. Could you take a look?

}
case config.RemoteWriteProtoMsgV2:
if remoteWrite2Enabled {
var req writev2.Request
Copy link
Member

Choose a reason for hiding this comment

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

Do you think we should also pool the "writev2.Request" like we do with the "PreallocWriteRequest", so we avoid lots of GC?

Maybe for this PR its ok to not do that but i imagine the GC will be quite high with v2 if we dont do that.

Copy link
Member Author

Choose a reason for hiding this comment

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

yeah, we need to introduce pool like v1. Let's update it to #6324.

Copy link
Member Author

Choose a reason for hiding this comment

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

I added benchmarks and here are the results:

Benchmark_Handler
Benchmark_Handler/PRW1_with_10_series
Benchmark_Handler/PRW1_with_10_series-10         	  109134	     10406 ns/op	   24970 B/op	     246 allocs/op
Benchmark_Handler/PRW2_with_10_series
Benchmark_Handler/PRW2_with_10_series-10         	   73774	     16280 ns/op	   33920 B/op	     374 allocs/op
Benchmark_Handler/PRW1_with_100_series
Benchmark_Handler/PRW1_with_100_series-10        	   13016	     90841 ns/op	  233753 B/op	    2319 allocs/op
Benchmark_Handler/PRW2_with_100_series
Benchmark_Handler/PRW2_with_100_series-10        	    9264	    131270 ns/op	  297826 B/op	    3350 allocs/op
Benchmark_Handler/PRW1_with_500_series
Benchmark_Handler/PRW1_with_500_series-10        	    2686	    442439 ns/op	 1148513 B/op	   11523 allocs/op
Benchmark_Handler/PRW2_with_500_series
Benchmark_Handler/PRW2_with_500_series-10        	    1904	    619988 ns/op	 1423212 B/op	   16554 allocs/op
Benchmark_Handler/PRW1_with_1000_series
Benchmark_Handler/PRW1_with_1000_series-10       	    1360	    875548 ns/op	 2305732 B/op	   23027 allocs/op
Benchmark_Handler/PRW2_with_1000_series
Benchmark_Handler/PRW2_with_1000_series-10       	     952	   1241676 ns/op	 3047640 B/op	   33057 allocs/op
Benchmark_Handler/PRW1_with_2000_series
Benchmark_Handler/PRW1_with_2000_series-10       	     674	   1815843 ns/op	 4627024 B/op	   46032 allocs/op
Benchmark_Handler/PRW2_with_2000_series
Benchmark_Handler/PRW2_with_2000_series-10       	     478	   2528554 ns/op	 6302383 B/op	   66062 allocs/op

@SungJin1212
Copy link
Member Author

SungJin1212 commented Apr 14, 2025

@yeya24
Should we automatically enable -blocks-storage.tsdb.enable-native-histograms if the -distributor.remote-write2-enabled is true?
If we set the protobuf_message to io.prometheus.write.v2.Request on the Prometheus, the Prometheus sends CT and NH by default. (remote write config)

@SungJin1212 SungJin1212 force-pushed the Add-remote-write-v2-api branch 2 times, most recently from 6e75881 to 715d9c9 Compare April 15, 2025 04:36
@SungJin1212 SungJin1212 force-pushed the Add-remote-write-v2-api branch from 715d9c9 to e5b85ee Compare April 22, 2025 09:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: No status
Development

Successfully merging this pull request may close these issues.

Prometheus Remote Write v2 Implementation
4 participants