Remove redundant synchronized from ShardCoreKeyMap#getShardId - #22803
Remove redundant synchronized from ShardCoreKeyMap#getShardId#22803uchiha-asha wants to merge 3 commits into
Conversation
The backing coreKeyToShard map is a ConcurrentHashMap, so reading it does not need the monitor. The getter is reached once per (query, segment) on every query cache lookup via IndicesQueryCache.OpenSearchLRUQueryCache onHit/onMiss, where it is a node-global contention point on search threads. Removing the monitor does not weaken the invariants: add() already does its fast-path containsKey check outside the lock, publishes the map entry as the last operation of the critical section (after the closed listener is registered), and the closed listener removes the entry first. Readers therefore never observe a half-built entry. getShardId does not touch indexToCoreKey, which remains HashMap-backed and guarded by the monitor in every method that reads or mutates it. A reader racing an in-flight add() may now observe null where it would previously have blocked and then seen the registered entry. That is already the documented contract of the method, which returns null for any segment it does not track. Adds a JMH benchmark exercising the getter across thread counts, with per-thread cursors seeded from the JMH thread index so threads walk distinct core keys. Ported from elastic/elasticsearch#156902. Signed-off-by: Asharam Meena <asharam1234meena@gmail.com>
PR Code Analyzer ❗AI-powered 'Code-Diff-Analyzer' found issues on commit d8f7725. ⛔ Hard block: Issues at Medium severity or above will block this PR from merging.
The table above displays the top 10 most important findings. Pull Requests Author(s): Please update your Pull Request according to the report above. Repository Maintainer(s): You can Thanks. |
PR Reviewer Guide 🔍(Review updated until commit 4863222)Here are some key observations to aid the review process:
|
PR Code Suggestions ✨Latest suggestions up to 4863222
Previous suggestionsSuggestions up to commit e8819cb
|
|
❌ Gradle check result for e8819cb: null Please examine the workflow log, locate, and copy-paste the failure(s) below, then iterate to green. Is the failure a flaky test unrelated to your change? |
|
Persistent review updated to latest commit 4863222 |
|
❌ Gradle check result for 4863222: FAILURE Please examine the workflow log, locate, and copy-paste the failure(s) below, then iterate to green. Is the failure a flaky test unrelated to your change? |
| * if this segment is not tracked. | ||
| */ | ||
| public synchronized ShardId getShardId(Object coreKey) { | ||
| public ShardId getShardId(Object coreKey) { |
There was a problem hiding this comment.
since the safety of dropping synchronized here depends on CacheKey using identity-based equals/hashCode (which it does, Lucene documents this), would it make sense to change the parameter type from Object to IndexReader.CacheKey?
that way the compiler enforces that only CacheKey instances get passed in, instead of relying on callers to do the right thing. right now any Object with overridden equals/hashCode could sneak in and break the assumptions.
There was a problem hiding this comment.
Hi, Thanks for suggestion. Lucene’s LRUQueryCache callbacks expose readerCoreKey as Object, so changing this parameter to CacheKey would require casts. Also, removing synchronized is safe because the backing map is a ConcurrentHashMap; identity semantics only affect key matching. I think we can retain Object here.
Description
Against
ShardCoreKeyMap#getShardId, thesynchronizedmodifier is redundant: the only state it reads,coreKeyToShard, is aConcurrentHashMap.This is not a cold path. The map is node-global (one per
IndicesQueryCache) and the getter is reached once per (query, segment) on every query-cache lookup, viaIndicesQueryCache.OpenSearchLRUQueryCache#onHit/#onMiss, so the monitor serialises search threads across the whole node.Why this is safe
add()already performs its fast-pathcontainsKeyread ofcoreKeyToShardoutside the monitor, and has done since the class was introduced. Lock-free reads of this map are not new behaviour.coreKeyToShard.put(...)as the last operation of its critical section, deliberately and with an in-source comment saying so, after the closed-listener registration. A reader that observes a key is therefore guaranteed to see a fully-registered entry.coreKeyToShardfirst, so a lock-free reader never sees an entry whose reader has already been torn down.getShardIdnever touchesindexToCoreKey, which remainsHashMap-backed and monitor-guarded in every method that reads or mutates it.getCoreKeysForIndex,sizeandassertSizeare unchanged and still synchronized.Behavioural note: a reader racing an in-flight
add()may now observenullwhere it would previously have blocked and then seen the entry. That is already the method's documented contract — it returnsnullfor any segment it does not track.Testing:
:server:compileJava,:benchmarks:compileJava,:server:spotlessJavaCheck,:benchmarks:spotlessJavaCheckandShardCoreKeyMapTests(3/3) all pass locally.Ported from elastic/elasticsearch#156902.
Related Issues
N/A — no tracking issue. Upstream equivalent: elastic/elasticsearch#156902
Check List
ShardCoreKeyMapTestscovers the changed class and passes; a JMH benchmark for the affected path is added in this PR.)By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.