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

PromQL: Improve space complexity by streaming more; avoid series pre-lookup. #7326

Closed
bwplotka opened this issue Jun 2, 2020 · 18 comments
Closed

Comments

@bwplotka
Copy link
Member

bwplotka commented Jun 2, 2020

Hi 👋

While changing SeriesSet interfaces, we found with @brian-brazil that our usage of SerieSet in PromQL might cause extra unnecessary allocations for certain implementations and cases. Let me describe the problem in two diagrams:

Current PromQL logic for Range Query with Metric Selection:

Prometheus PromQL v2 19 Flow
Google Draw Link

All function names should match the current master: https://github.com/prometheus/prometheus/tree/18d9ebf0ffc26b8bd0e136f552c8e9886d29ade4

Issues:

Prometheus PromQL v2 19 Flow_ Unnecessary PreLookup of Series (1)
Google Draw Link

Problem: We are accumulating all series first, to gather all iterators.

With the current interfaces (StoreSet, Remote Read, Thanos StoreAPI) “Series by Series” means that we stream Series labels together with labels.

Current Flow Issues:

  1. To finish this eval PromQL stage, we have to buffer & receive all data. Especially for remote data & Thanos, this means literally all. (: Realistic space complexity is now O(2 * series * chunks), for the biggest result, so ~ O(2 series * chunks) B
  2. Current space complexity is also not great further with multi arg functions, as we build a hash map. For large cardinality queries, we end up hashing millions of Series potentially.

Solution: Stream more!

The idea would be to follow really exactly the same algorithm as everywhere else (vertical dedup, merge series etc): Implement Heap / MergeSort that will iterate over "sorted" series from two SeriesSets.

  • Open Question: Good point that @brian-brazil mentioned, was that we don't know then how big matrix we should allocate. I wonder if we can workaround this somehow. To me it sounds we don't really want to operate on matrixes at all, rather on top two series from each SeriesSets... 🤔

This sounds like a big task but really needed, especially for Thanos community. Even with the work we are doing to make PromQL more concurrent for Prometheus Ecosystem (#6878), this issue might be impacting resource (memory) consumption significantly, especially for concurrent runs. I am happy to do this at some point, but I won't have time in near ~2weeks for this. so.. help wanted (:

Any feedback on this to make it easier to improve this? (: cc @slrtbtfs @brian-brazil @brancz @kakkoyun @cstyan @tomwilkie @gouthamve @cstyan

@brian-brazil
Copy link
Contributor

This is actually quite a small change in removing the Expand from CheckAndExpand. It's really just cleanup from the big reworking of promql I did a few years back.

Current space complexity is also not great further with multi arg functions, as we build a hash map. For large cardinality queries, we end up hashing millions of Series potentially.

I don't see how this is related to what's being discussed here. Hashing is cheap, and we'll need to do it anyway.

I'm not sure why you've marked this as a P1 feature. It's a small performance thing that's not really something user visible, and it's not urgent. It's a P3 cleanup or enhancement.

@bwplotka
Copy link
Member Author

bwplotka commented Jun 2, 2020

Happy to hear it's easy =D let's try it out then.

@allenmqcymp
Copy link

Hi there, first time contributor looking to help with low hanging-fruit issues. Could someone point me to the relevant code files so I can better understand the issue?

@brian-brazil
Copy link
Contributor

Basically in

func checkAndExpandSeriesSet(ctx context.Context, expr parser.Expr) (storage.Warnings, error) {
we should not be expanding the whole series in advance - that should happen during evaluation one series at a time.

@allenmqcymp
Copy link

Hi, sorry for the inactivity. I would still like to pick this issue up. Currently looking through how engine.go works, is there any good documentation about the internals of the PromQL engine?

My current understanding of the issue is that it is inefficient to gather all the series data before rangeEval. I am still unsure of what the proposed fix might involve. Could someone please enlighten?

Thanks!

@brian-brazil
Copy link
Contributor

I'm not sure there's much in general docs.

Could someone please enlighten?

In essence eliminate ExpandSeriesSet, and then fix up anything that breaks due to that. The only place things will happen to be fully expanded is subqueries, as that's what the output is.

@aryan9600
Copy link

Hey everyone, a first time contributor here. I would like to work on this issue if no one else is already working on it.

Having gone through this issue and some of the related code, I have a few doubts I was hoping to get cleared.

As far as I understand, we want to avoid using checkAndExpandSeriesSet and consequently expandSeriesSet in eval().
If we don't expand the SeriesSet, how do we get access to the iterator of each Series in order to use the buffer? Also how do we determine the length of our output matrix?

Unfortunately, @bwplotka's solution regarding Heap/MergeSort and streaming more made little sense to me, so it would be great if someone could elaborate on that.

Thanks!

@brian-brazil
Copy link
Contributor

how do we get access to the iterator of each Series in order to use the buffer?

We do it as needed one by one, rather than in one big batch.

@aryan9600
Copy link

We do it as needed one by one.

I think I understand the issue, but can't really seem to get a grasp on the solution. Where exactly do we determine that we need to move to the next Series?

@brian-brazil
Copy link
Contributor

Anywhere we iterate over the slice today, instead we call next.

@aryan9600
Copy link

Sorry for the inactivity, I have been trying to work on a patch, but can't get a few tests to pass (almost all of them pass), specifically some in functions.test and subquery.test. I replaced all for loops iterating over the slice, with a for loop which would expand the SeriesSet during eval time, but due to this a few Matrices (and consequently Vectors) are coming up empty, due to this change, and I can't really seem to figure out why exactly. Would you have any idea as to why this change would result in this error? Thanks :)

@brian-brazil
Copy link
Contributor

That's a bit weird, can you make a PR so we can have a look?

@sthaha
Copy link
Contributor

sthaha commented Jun 14, 2021

@bwplotka @brian-brazil , assuming the issue still exists, I would love to take a crack at this if @aryan9600 isn't keen on progressing with #8009

@bwplotka
Copy link
Member Author

Totally, go for it. It's still available, but benchmarks might be necessary to assess results

@bwplotka
Copy link
Member Author

Just to give status on it.

It's still needed for Thanos and (and potentially Cortex purposes). See: thanos-io/thanos#4780

Looks like @darshanime did a good job on this with #9071, so we can close #8009 (thanks for your hard initial work @aryan9600 !).

@keremgocen
Copy link

happy to take a look or collab if no one else is on it

@beorn7
Copy link
Member

beorn7 commented Apr 30, 2024

Hello from the bug scrub.

@bwplotka , our impression is that this is not required anymore (since #9071 was closed as "not required anymore"). Could you confirm? Please close if that's the case.

@bwplotka bwplotka closed this as not planned Won't fix, can't repro, duplicate, stale Apr 30, 2024
@bwplotka
Copy link
Member Author

Correct, not needed anymore, we can reopen if there will be a need!

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.

7 participants