-
Notifications
You must be signed in to change notification settings - Fork 2.5k
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
Jaeger Query can OOM when retrieving large traces #1051
Comments
Related issue to ES, although its related to overloading ES not query #960 |
And this is why it's a good idea to have the collector in a different instance for production environments :) |
Same here with ES. One failed microservice caused retry loop and we got giant trace with thousands spans. During request jaeger query eats all avaliable memory and get killed by OOM. |
We probably want to add this scenario to our test cases cc @jkandasa |
We may consider adding LIMIT to Cassandra queries by trace ID. |
Limit on the number of spans in a trace? |
Well there are two separate things. We're seeing some traces in production with many millions of spans so we want to implement a limit on ingestion. But separately, there can be a limit on reading side to avoid loading too much even if that many spans were saved. |
The olivere/elastic library supports sending a range query using Would it make sense to pass this range as a param in MultiSearch here - https://github.com/jaegertracing/jaeger/blob/master/plugin/storage/es/spanstore/reader.go#L256 ? |
Perhaps yes, I am not 100% percent sure how the current search works. Would you like to submit a patch? |
Sure, I'll give it a shot soon. |
@pavolloffay could you review this? annanay25@2d167b8 |
I think fetching in batches is not the issue here. If a trace has 1mm spans, whether you fetch in batches or all at once, the query service still needs to store all of them in memory before returning to the client, since we do not support streaming of the results (something to think about when designing the protobuf API for query). I am not sure what impact using fetching has on the ES itself. At least with Cassandra we're getting a result set that we iterate through, so the driver & storage have the opportunity to load data in a streaming fashion, without Cassandra node holding everything in memory. |
@yurishkuro the idea was that the query nodes would hold data of only one batch at a time in-memory, and would send this batch to the client (assuming the UI). The client could choose to read and then discard the spans in batches. But as you have mentioned that jaeger does not support streaming of results, what approach do you recommend? |
I think the simplest fix is what is proposed in the description of this ticket. It's not ideal since it will truncate the data, but we can revisit that later when/if we support a streaming API from the query service. At least this fix is fairly straightforward and will protect the query svc from OOM. Most of the analytics will choke on a trace with >100k spans anyway. The limits will be an optional configuration. |
@yurishkuro I agree. Maybe should just mark trace in web interface that it is truncated due span count limit. |
@yurishkuro truncated search results (truncated span-count as well, for ES) implemented for ES and Cassandra - annanay25@9eb7590 We were waiting on a test case for this? Is someone working on that? |
Not afaik |
Not 100% related but i have a question: Edge case: For a use case, i need to use in mem storage, was wondering about edge case where one huge trace takes up whole memory, will this cause any OOM errors or will the trace be discard?
cc: @annanay25 |
Encountering OOM issues while fetching traces from jaeger-UI. We are encountering OOM kill upon fetching 20 traces(each trace consists of ~20K spans of 1kb size each) from UI. This is causing frequent process restarts. |
@Sreevani871 yes, we even had tickets somewhere for that, but no volunteers to implement it. |
@yurishkuro Any specific reason for fetching all info in FindTraces call itself from UI? |
If you mean the internal implementation of FindTraces, then yes, the reason is because there is no summary information about the full trace available anywhere else, since collectors receive spans one at a time. Storage is the only place that can provide a full trace, but you need to read that full trace in order to extract summary. It's possible to build a post-processing, but it will require additional infrastructure/coordination. |
Can you share the tickets will have a look? |
There is context here: jaegertracing/jaeger-ui#247 |
@yurishkuro Any progress on this? |
To overcome frequent OOM kills of jaeger-query service (Due to traces having a large number of spans), we need to avoid fetching of all spans data of respective TopN traces. Here listing out the approaches we thought of to solve the issue. Approach - I:
Here old span of trace refers to parent span ideally. To avoid cases like parent span missing or parent span not indexed at the time fo search, considering fetching old span(sort by startTime) of a trace available at the time of the search. Code Changes Required at UI:
Pros:
Cons:
Note: FindTraces page has a Deep Dependency Graph option, Since we are avoiding fetching all span data in the FindTraces API call, Need to have a separate API which will fetch the required fields of spans for traces to construct DDG which will overlap with our idea of Approach-2. Approach - II:
Changes Required At Backend:
Changes Required At Frontend:
Cons:
|
That's a very astute observation, since DDG is built entirely by the UI and requires loading all spans of the trace (but not all data in the spans). A more flexible variation of approach 2 is to build support for GraphQL. It's going to be difficult to support partial field retrieval at the storage level (some storage implementations may only allow retrieving the whole trace as a blob), so the whole data is still going to be loaded in memory from the database. The memory savings can come from refactoring the storage API to be more like stream, i.e. instead of synchronous: FindTraces(query) ([]*model.Span, error) implement the streaming version (incidentally, the grpc-plugin storage API already uses streaming under the hood): FindTracesStreaming(query, handler func(*model.Span) error) error The handler function can immediately convert model.Span to JSON output object, AND trim it down by filtering only the fields required by GraphQL query. This will not reduce the number of memory allocations, but will allow query service not to hold onto every single span of the whole result set. Later this can be further extended into approach 1, which will reduce memory requirements even further (but DDG from search will likely require a second query, which is probably ok since not every search is used to display DDG). |
Using json.Unmarshal() func instead of json.Decode() func here https://github.com/jaegertracing/jaeger/blob/master/plugin/storage/es/spanstore/reader.go#L233 shows improvement on memory usage because json.Decode() func is buffering entire json value in memory again. Since here the data passed to unmarshalJSON() method is already in memory so using json.Unmarshal() is improving performance in terms of memory usage.
|
Interesting query benchmark numbers from a post on Slack:
|
One possible solution here would be to limit the number of spans returned when pulling up a trace to 50k and allow this to be configurable. |
Many of the issues discussed in this issue will be alleviated by Storage v2 API (#5079) which will support streaming of the results. It will not address the issue if a single trace has millions of spans since it still needs to be loaded by the query service in order to apply adjustments and conversion, but the query service will be in control of pulling as much data as it wants to it can put a limit on the number of spans it's willing to receive and terminate the load after that. |
Requirement - what kind of business use case are you trying to solve?
Problem - what in Jaeger blocks you from solving the requirement?
Jaeger Query OOMs on retrieval of large traces on Cassandra.
If someone is crafty, they can easily create a trace with millions of spans, and attempt to retrieve it to systematically bring down all jaeger-query instances.
Proposed Solution - Cassandra
We might do some combination of the following:
Any open questions to address
The text was updated successfully, but these errors were encountered: