-
Notifications
You must be signed in to change notification settings - Fork 25.2k
Add an index->step cache to the PolicyStepsRegistry #82316
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
Add an index->step cache to the PolicyStepsRegistry #82316
Conversation
3126e40
to
ed38398
Compare
@@ -305,7 +332,9 @@ public Step getStep(final IndexMetadata indexMetadata, final Step.StepKey stepKe | |||
+ phaseSteps; | |||
|
|||
// Return the step that matches the given stepKey or else null if we couldn't find it | |||
return phaseSteps.stream().filter(step -> step.getKey().equals(stepKey)).findFirst().orElse(null); | |||
final Step s = phaseSteps.stream().filter(step -> step.getKey().equals(stepKey)).findFirst().orElse(null); |
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.
Just a thought from looking at the profiling you posted: This might be happier as a normal loop :) The phaseSteps
list is mostly (always?) very short and setting up a stream and doing all the dance around it doesn't even come close to outweighing the slightly faster iteration the stream provides. I'm good with this either way though, just a thought since this line was changed anyway :)
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.
Ehhhhhhh, I don't think it's worth it. This reads pretty clearly as-is, and I don't think it's enough of a bottleneck anymore to warrant unrolling to a normal loop (due to this PR's optimization resulting in the code in question just being called a bajillion times less frequently).
Pinging @elastic/es-data-management (Team:Data Management) |
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.
LGTM If we add some JavaDoc :)
@@ -64,6 +66,8 @@ | |||
private final Map<String, Map<Step.StepKey, Step>> stepMap; | |||
private final NamedXContentRegistry xContentRegistry; | |||
|
|||
private final Map<Index, Tuple<IndexMetadata, Step>> cachedSteps = new ConcurrentHashMap<>(); |
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.
Lets add a line or two of Javadoc explaining what this is for please. This is far from obvious for anybody not involved in this work.
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.
How does 5f48e35 sparkle with you?
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.
👍
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.
Would it be possible to key the "changed state" key check that is based on IndexMetadata
to use a combination of LifecycleExecutionState
and the policy name instead (perhaps encapsulated into an actual Key class)? IndexMetadata
feels really heavyweight for this, and the step itself can only differ based on either the policy name or the execution state.
It feels strange to me to hold on to things like the mappings inside this cache, when those should have no bearing on the current ILM step.
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.
IndexMetadata feels really heavyweight for this
We only use it for instance equality and clear it out of the cache if the index gets removed => memory wise it's free, instance equality comparison is cheap/free and this seems like the safest way of checking whether or not index metadata has changed.
We didn't just create a compound key here because we need/want the ability to quickly cleanup the cache if an index is removed without having to iterate a cache. Since we only care about the by index state this seemed fastest/easiest.
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 has the side effect however, of any inconsequential change to the index metadata for the index (a dynamic field introduced, for example) invalidating the cache, whereas it doesn't actually need to be invalidated in that case?
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 think that's ok. This is just supposed to help us with not running the expensive step computation on unchanged indices (which will always be the majority in almost any CS update). No need to super optimize this beyond that I'd say. Updating index metadata results in expensive operations anyway that even go as far as resulting in disk IO, the step calculation is probably trivial relatively speaking in all cases.
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.
Alright, that's fine then, my preference is still to make the keyed part as small as possible, but this is okay for now I think.
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 left a few comments, I think IndexMetadata
is a bit big to hold on to for a cache like this, and I'd prefer to keep the map as small as possible, what do you think?
@@ -155,6 +166,22 @@ public LifecyclePolicyMetadata read(StreamInput in, String key) { | |||
} | |||
} | |||
|
|||
public void delete(Index deleted) { |
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.
Can you add javadoc to this too please, since it isn't as clear when this is intended to be used?
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.
👍, 68161e8
for (Index index : event.indicesDeleted()) { | ||
policyRegistry.delete(index); | ||
} |
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.
Can you wrap this in if (this.isMaster)
since we don't care about removing the Index
entries if we aren't master since the policyRegistry is cleared when transitioning away from master? We might as well avoid iterating through the deleted indices on every node.
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.
huh come to think of it, can't we just return if isMaster
is false, no need to load the ILM metadata below either? :)
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 works also, we can return after the cancelJob()
and policyRegistry.clear()
call above.
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.
👍, 4954725
I didn't end up going with an early return
but the logic is essentially the same as if I did.
@@ -64,6 +66,8 @@ | |||
private final Map<String, Map<Step.StepKey, Step>> stepMap; | |||
private final NamedXContentRegistry xContentRegistry; | |||
|
|||
private final Map<Index, Tuple<IndexMetadata, Step>> cachedSteps = new ConcurrentHashMap<>(); |
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.
Would it be possible to key the "changed state" key check that is based on IndexMetadata
to use a combination of LifecycleExecutionState
and the policy name instead (perhaps encapsulated into an actual Key class)? IndexMetadata
feels really heavyweight for this, and the step itself can only differ based on either the policy name or the execution state.
It feels strange to me to hold on to things like the mappings inside this cache, when those should have no bearing on the current ILM step.
@@ -267,6 +294,11 @@ public LifecyclePolicyMetadata read(StreamInput in, String key) { | |||
|
|||
@Nullable | |||
public Step getStep(final IndexMetadata indexMetadata, final Step.StepKey stepKey) { | |||
final Tuple<IndexMetadata, Step> cachedStep = cachedSteps.get(indexMetadata.getIndex()); | |||
if (cachedStep != null && cachedStep.v1() == indexMetadata && cachedStep.v2().getKey().equals(stepKey)) { |
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.
Can you add a comment about why instance equality is used here and okay rather than object equality?
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.
👍, 68161e8
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.
Just a drive-by-comment.
} | ||
} | ||
|
||
for (Index index : event.indicesDeleted()) { | ||
policyRegistry.delete(index); |
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 wonder if there is an (benign for sure) risk of a leak here: if a policy adds a step to the cache concurrently with it being deleted here, I think we may leave it there until next clear or master failover. Ideally we would retain only those that remain (thus curing this), but I can see how that is more expensive. A comment could be good though.
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.
Sure thing, 19ecdd3
Dropped the |
@joegallo fyi we got the green-light to backport this to 7.17 after all! |
Hi @joegallo, I've created a changelog YAML for you. |
Promoting the isMaster check around triggerPolicies up to include both these blocks.
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.
LGTM
* upstream/master: (100 commits) Avoid duplicate _type fields in v7 compat layer (elastic#83239) Bump bundled JDK to 17.0.2+8 (elastic#83243) [DOCS] Correct header syntax (elastic#83275) Add unit tests for indices.recovery.max_bytes_per_sec default values (elastic#83261) [DOCS] Add note that write indices are not replicated (elastic#82997) Add notes on indexing to kNN search guide (elastic#83188) Fix get-snapshot-api :docs:integTest (elastic#83273) FilterPathBasedFilter support match fieldname with dot (elastic#83178) Fix compilation issues in example-plugins (elastic#83258) fix ClusterStateListener javadoc (elastic#83246) Speed up Building Indices Lookup in Metadata (elastic#83241) Mute whole suite for elastic#82502 (elastic#83252) Make PeerFinder log messages happier (elastic#83222) [Docs] Add supported _terms_enum field types (elastic#83244) Add an aggregator for IPv4 and IPv6 subnets (elastic#82410) [CI] Fix 70_time_series/default sort yaml test failures (elastic#83217) Update test-failure Issue Template to include "needs:triage" label elastic#83226 Add an index->step cache to the PolicyStepsRegistry (elastic#82316) Improve support for joda datetime to java datetime transition in Painless (elastic#83099) Fix joda migration for week based methods in Painless (elastic#83232) ... # Conflicts: # x-pack/plugin/rollup/src/main/java/org/elasticsearch/xpack/rollup/v2/TransportRollupAction.java
Related to #77466. Extracted and cleaned up from #79692.
Adds a Index->Step cache (well, really Index->IndexMetadata+Step cache) that saves us from having to recalculate the step each time. It's keyed off the index, but if the IndexMetadata has changed, then the cache is bypassed and updated. Crucially, though, we also need to clear the cache in the event that the policies themselves change (because a policy change can result in the next step being different even if the IndexMetadata itself didn't change).