-
Notifications
You must be signed in to change notification settings - Fork 25.5k
Centralize telemetry listener wrapping in TransportSearchAction #134625
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
Centralize telemetry listener wrapping in TransportSearchAction #134625
Conversation
private class SearchResponseActionListener extends DelegatingActionListener<SearchResponse, SearchResponse> | ||
implements | ||
TelemetryListener { | ||
private static class SearchTelemetryListener extends DelegatingActionListener<SearchResponse, SearchResponse> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I made this static, it feels much better to be able to reason about its dependencies and not have it be tied to TransportSearchAction's lifecycle.
|
||
private interface TelemetryListener { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given I moved the wrapping inside of executeSearch, this interface is no longer required. It was there for open PIT only as it provides a different type of listener.
SearchRequest original, | ||
ActionListener<SearchResponse> originalListener, | ||
Function<ActionListener<SearchResponse>, SearchPhaseProvider> searchPhaseProvider, | ||
boolean collectSearchTelemetry |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't perfect, but it gets the job done. Ideally, open PIT would have its own transport action, but I am not even sure that's a change worth making given its complexity.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Before this change this flag was not needed because the listener had all the telemetry logic encapsulated in it. The downside was that we needed to do some instanceof checks on it. Another issue I found with that is that it does not allow to store resolved indices (without carrying a reference to the search request in the listener).
@@ -2069,7 +2072,7 @@ public void onResponse(SearchResponse searchResponse) { | |||
} | |||
searchResponseMetrics.incrementResponseCount(responseCountTotalStatus); | |||
|
|||
if (collectTelemetry()) { | |||
if (collectCCSTelemetry()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I renamed this to avoid confusion, as this flag controls only whether we record CCS specific telemetry.
Pinging @elastic/es-search-foundations (Team:Search Foundations) |
server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java
Outdated
Show resolved
Hide resolved
if (ccsCheckCompatibility) { | ||
checkCCSVersionCompatibility(rewritten); | ||
} | ||
|
||
final CCSUsage.Builder ccsUsageBuilder = new CCSUsage.Builder(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like we always create and update ccsUsageBuilder
but only use it if collectSearchTelemetry
is true?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes I was a bit lazy here, I will iterate over this once more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was not so different from how things already were: a CCS usage builder always got created despite we may not be collecting CCS telemetry. I did not like that my change made things even harder to read. I tied the creation of the listener to the telemetry collection now, so that we only create the usage builder when necessary, and the code should be easier to follow.
// We set the keep alive to -1 to indicate that we don't need the pit id in the response. | ||
// This is needed since we delete the pit prior to sending the response so the id doesn't exist anymore. | ||
source.pointInTimeBuilder(new PointInTimeBuilder(resp.getPointInTimeId()).setKeepAlive(TimeValue.MINUS_ONE)); | ||
var pitListener = new SearchResponseActionListener(delegate) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One thing I notice here that SearchResponseActionListener
also collects SearchResponseMetrics
. But if we convert this to just ActionListener
this doesn't happen anymore? Is that intended?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that's a good observation. Yet onResponse
is entirely rewritten here, hence SearchReponseActionListener in this case does not do anything anyways. Using a plain ActionListener does not cause any change in behaviour. I added a test around this though that was missing: this is triggered e.g. when a compound retriever is used, which opens a PIT under the hood and performs a search against it (executed as an async action).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also moved up the shouldOpenPIT conditional branch, as it can quickly shortcut execution as opposed to creating the rewriteListener for nothing when a PIT needs to be opened. I think this improved readability, let me know.
The approach looks fine to me but have some question about particular details. |
}) | ||
); | ||
return; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the only change in this block is no longer using SearchResponseActionListener
, which was not necessary (beyond my changes - added a test to verify that). This block can shortcut execution hence I moved it up.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for working on this Luca.
The context comments helped the review ! 🚀
@@ -85,6 +90,29 @@ public void testSimpleQuery() { | |||
assertEquals(searchResponse.getTook().millis(), measurements.getFirst().getLong()); | |||
} | |||
|
|||
public void testCompoundRetriever() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
very nice !
TransportSearchAction collects telemetry around CCS features used as well as took time of a search request. It does that via a action listener wrapping. The open PIT API uses TransportSearchAction for its execution, yet it does not wrap the listener in the same way, as search telemetry should not be affected by it. As a result, we have a
TelemetryListener
interface, with some instanceof checks in the code and a single implementation of such interface that is the action listener wrapper itself, which allows to record the telemetry on response.This can be simplified by moving the listener wrapping to the TransportSearchAction code, and specialize a little the open PIT path so that telemetry can still be disabled for it. Along with the simplifications, this change allows us to delay the wrapping of the listener, which will allow us to grab the resolved indices in #134232 .