GEODE-10526 - IndexTrackingQueryObserver.afterIndexLookup() throws Nu… #7960
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
IndexTrackingQueryObserver.afterIndexLookup() throws a NullPointerException when executing queries on partitioned regions during application startup. The issue occurs because the method attempts to access a ThreadLocal
indexMap that was never initialized, causing the application to fail.
Root Cause
In org.apache.geode.cache.query.internal.IndexTrackingQueryObserver.java, the afterIndexLookup() method (line 107-110) retrieves a Map from a ThreadLocal without checking for null
The ThreadLocal indexMap is initialized in beforeIndexLookup() (lines 49-52), but in certain scenarios involving partitioned region queries, afterIndexLookup() can be called without beforeIndexLookup() having successfully
completed, leaving the ThreadLocal uninitialized.
Expected Behavior
- afterIndexLookup() should handle cases where beforeIndexLookup() did not successfully initialize the ThreadLocal
- Query execution should complete successfully
- Application startup should not fail due to query observer issues
Actual Behavior
Application fails with:
Suggested Fix
Add a null check in afterIndexLookup() method to handle uninitialized ThreadLocal:
File: geode-core/src/main/java/org/apache/geode/cache/query/internal/IndexTrackingQueryObserver.java
Change at lines 97-120:
Rationale for Fix
1. Defensive Coding: Other methods like getUsedIndexes() (line 150-154) already check for null before using the ThreadLocal
2. Consistency: The pattern if (results == null) return; is already used at the method start
3. Minimal Impact: The fix is a simple guard clause that doesn't change behavior when ThreadLocal is properly initialized
4. Safe Degradation: When the ThreadLocal is null, gracefully skip tracking instead of crashing
Workaround
Until this bug is fixed, users can work around it by disabling query verbose mode:
Option 1: JVM System Property
-Dgemfire.Query.VERBOSE=false
Setting gemfire.Query.VERBOSE=false completely disables IndexTrackingQueryObserver creation and uses QueryObserverAdapter (with no-op methods) instead, avoiding the bug entirely.
Additional Notes
1. Related Code Locations:
- IndexTrackingQueryObserver.java: Lines 42 (ThreadLocal declaration), 107-110 (bug location)
- CompiledComparison.java: Lines 383-387 (beforeIndexLookup call), 500-504 (afterIndexLookup call in finally)
- DefaultQuery.java: Line 781 (IndexTrackingQueryObserver instantiation)
2. Similar Patterns: The getUsedIndexes(String fullPath) method already implements the null check pattern at line 209-212, which should be mirrored in afterIndexLookup()
For all changes, please confirm:
develop)? Yesgradlew buildrun cleanly? Yes