PHOENIX-7965 :- ConnectionQueryServicesImpl.getMetaDataCache() returns null after connection close, causing a bare NPE during concurrent query compilation#2580
Open
lokiore wants to merge 1 commit into
Conversation
…against a closed connection
getMetaDataCache() returned the volatile latestMetaData field directly, with no
null guard. When a connection is closed concurrently with query compilation
(for example connection pool teardown or cluster failover), close() sets
latestMetaData = null. An in-flight compile then reaches the cache via
PhoenixConnection.getTableRef()/getFunction()/getSchema(), which call
getMetaDataCache().getTableRef(key) (and friends) on the null return, producing
a bare, causeless NullPointerException with no message.
Every other cache accessor/mutator on this class (addTable, removeTable,
pruneTables, the internal getTable path, etc.) already calls
throwConnectionClosedIfNullMetaData() first, which throws a descriptive
IllegalStateException("Connection to the cluster is closed"). getMetaDataCache()
was the one accessor missing that guard. This change adds the same guard so a
closed connection surfaces the descriptive exception instead of a bare NPE.
No caller relies on a null return: every production caller of
ConnectionQueryServices.getMetaDataCache() immediately dereferences the result,
so this only upgrades the failure mode from an opaque NPE to a descriptive
exception; it does not change behavior on an open connection.
Tests: added unit tests asserting getMetaDataCache() throws the descriptive
IllegalStateException when the cache has been nulled (closed-connection state),
and returns the cache unchanged when present.
Generated-by: Claude Code (Opus 4.8 (1M context))
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
What changes were proposed in this pull request?
ConnectionQueryServicesImpl.getMetaDataCache()returned thevolatile latestMetaDatafield directly, with no null guard:This PR adds a guard so a closed connection surfaces a descriptive exception instead of a bare NPE. The read is done once into a local so the null-check and the returned reference are guaranteed to be the same value (no time-of-check/time-of-use re-read window):
throwConnectionClosedException()is the same private helper that the existingthrowConnectionClosedIfNullMetaData()(used byaddTable,removeTable,pruneTables, the internalgetTablepath, etc.) delegates to — it throwsIllegalStateException("Connection to the cluster is closed").getMetaDataCache()was the one cache accessor missing this guard.Why are the changes needed?
close()setslatestMetaData = null. When a connection is closed concurrently with query compilation (for example, connection-pool teardown or a cluster role transition), an in-flight compile reaches the cache viaPhoenixConnection.getTableRef()/getFunction()/getSchema(), which callgetMetaDataCache().getTableRef(key)(and friends) on thenullreturn. The result is a bare, causelessNullPointerExceptionwith no message — difficult to attribute to a closed connection during triage.Every other accessor/mutator on this class already guards this state and throws the descriptive
IllegalStateException;getMetaDataCache()was inconsistent. This change makes the failure mode uniform and diagnosable.Does this PR introduce any user-facing change?
No behavioral change on an open connection: when
latestMetaDatais present, the method returns exactly the same cache reference as before. The only change is the failure mode when the connection has been closed — an opaqueNullPointerExceptionis upgraded to a descriptiveIllegalStateException("Connection to the cluster is closed"). No caller relies on anullreturn (every production caller immediately dereferences the result), so no caller is regressed.How was this patch tested?
Added unit tests in
ConnectionQueryServicesImplTestexercising a realConnectionQueryServicesImplinstance (not a mock, so the real guard executes):testGetMetaDataCacheThrowsWhenClosed— reflectively nullslatestMetaData(closed-connection state) and assertsgetMetaDataCache()throwsIllegalStateExceptionwith message"Connection to the cluster is closed".testGetMetaDataCacheReturnsCacheWhenOpen— asserts the method returns the cache unchanged when present.Both pass (
Tests run: 2, Failures: 0, Errors: 0) via a reactor build (mvn -pl phoenix-core -am test).mvn spotless:applyreports the tree clean.Was this patch authored or co-authored using generative AI tooling?
Generated-by: Claude Code (Opus 4.8 (1M context))