PHOENIX-7898 :- Cloned PhoenixConnection loses HA group, breaking _HAGroupName tagging on UPSERT SELECT and DELETE#2515
Merged
lokiore merged 3 commits intoJun 12, 2026
Conversation
…GroupName tagging on UPSERT SELECT and DELETE The four PhoenixConnection copy constructors that take an existing PhoenixConnection as the first argument all delegate to the 9-arg private constructor with `null` passed for the cloned connection's haGroup field. Cloned connections produced by MutatingParallelIteratorFactory.newIterator(...) (server-side coprocessor for UPSERT SELECT and DELETE) therefore evaluate the MutationState.annotateMutationWithMetadata guard `if (connection.getHAGroup() != null && ...)` to false, the _HAGroupName HBase attribute is not attached to those mutations, and the consistent_failover (CCF) server's mutation-block contract is not enforced for the affected write paths. Three localized changes in phoenix-core-client: 1. PhoenixConnection.java -- four copy constructors propagate connection.haGroup instead of null. Restores HA-group inheritance for cloned connections produced by MutatingParallelIteratorFactory and other clone paths. 2. PhoenixConnection.java -- split duplicated `// Only available for connection which is part of HA Connections` Javadoc on haGroupName / haGroup into field-specific descriptions that document each field's distinct role. 3. ConnectionQueryServices.java -- convert the 3-arg connect(String, Properties, HighAvailabilityGroup) overload from abstract to default, delegating to the 2-arg connect(url, info). Preserves source-compatibility for any 3rd-party ConnectionQueryServices implementor that has not yet adopted the 3-arg overload. Adds testUpsertSelectReplicatesViaCloneConnection in ReplicationLogGroupIT to lock the regression. The test exercises an UPSERT SELECT of 250 rows (above the default MUTATE_BATCH_SIZE = 100) so the in-loop flush at UpsertCompiler.upsertSelect fires through the cloned connection's MutationState.send() path -- the only path where the missing haGroup field is observable. Pre-fix: target table receives 50 of 250 rows (assertion fails). Post-fix: target table receives 250 of 250 rows (assertion passes). Generated-by: Claude Code (Opus 4.7)
…py constructors Replace direct `connection.haGroup` field access with the public getter `connection.getHAGroup()` in the four PhoenixConnection copy constructors. Preserves encapsulation and matches the existing MutationState.annotateMutationWithMetadata guard which already uses `connection.getHAGroup() != null`. Generated-by: Claude Code (Opus 4.7)
Removes the redundant PhoenixConnection.haGroupName field; HA group name is now derived through connection.getHAGroup().getName(). Adds HighAvailabilityGroup.getName() convenience accessor that delegates to info.getName() so callers in org.apache.phoenix.util and org.apache.phoenix.execute can access the HA group name without needing visibility into the package-private HAGroupInfo. Collapses the paired-gate getHAGroup() != null && StringUtils.isNotBlank(getHAGroupName()) to single-gate getHAGroup() != null at both production callsites (ScanUtil.setScanAttributeForHAForPointLookups, MutationState.annotateMutationWithMetadata). The blank check is impossible in practice: HAGroupInfo's ctor calls Preconditions.checkNotNull(name) and HighAvailabilityGroup.getUrlInfo throws HA_INVALID_PROPERTIES for empty names before any HAGroupInfo is constructed, so a non-null HAGroup with a blank/null name is structurally impossible. Removes the now-unused StringUtils import from ScanUtil and MutationState. Generated-by: Claude Code (Opus 4.7)
114d5df to
7a8771a
Compare
tkhurana
approved these changes
Jun 12, 2026
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?
Three localized changes in
phoenix-core-clientthat propagate the HA group through clonedPhoenixConnections, plus an integration test that locks the regression:PhoenixConnection.java— copy-constructorhaGrouppropagation. The four copy constructors that take an existingPhoenixConnectionas the first argument all delegate to the 9-arg private constructor withnullpassed for the cloned connection'shaGroupfield. Replaced withconnection.haGroupso cloned connections inherit the parent's HA group. Affected ctors:(PhoenixConnection, boolean, boolean),(PhoenixConnection, MutationState),(PhoenixConnection, Properties), and(PhoenixConnection, ConnectionQueryServices, Properties). The 1-arg(PhoenixConnection)and the(PhoenixConnection, long scn)ctors delegate transitively and inherit the fix.PhoenixConnection.java— Javadoc disambiguation on the two HA fields. ThehaGroupName(String) andhaGroup(HighAvailabilityGroup) fields share an identical// Only available for connection which is part of HA Connectionscomment. Replaced with field-specific descriptions:haGroupNameis the wire-tag string used for the_HAGroupNameHBase attribute and is set on every connection that resolved through an HA route (including cloned connections);haGroupis the resolved group object used for failover orchestration and cluster-role lookups, set on root HA connections and inherited by clones via the copy constructors.ConnectionQueryServices.java— interface ergonomics. The 3-argconnect(String url, Properties info, HighAvailabilityGroup haGroup)overload is currently abstract. Convert todefaultdelegating to the 2-argconnect(url, info). The three internal implementors (ConnectionQueryServicesImpl,ConnectionlessQueryServicesImpl,DelegateConnectionQueryServices) already override the 3-arg form with ahaGroup-using body. The default is for source-compatibility with any 3rd-partyConnectionQueryServicesimplementor that has not yet adopted the 3-arg overload — those implementors get the pre-existing 2-arg behavior (haGroup discarded) instead of aNoSuchMethodError.ReplicationLogGroupIT.java— addstestUpsertSelectReplicatesViaCloneConnectionto lock the regression. The test exercises anUPSERT SELECTof 250 rows (above the defaultMUTATE_BATCH_SIZE = 100) so the in-loop flush atUpsertCompiler.upsertSelectfires through the cloned connection'sMutationState.send()path — the only path where the missinghaGroupfield is observable.JIRA: https://issues.apache.org/jira/browse/PHOENIX-7898
Why are the changes needed?
Cloned
PhoenixConnections are produced byMutatingParallelIteratorFactory.newIterator(...)(the server-side coprocessor path forUPSERT SELECTandDELETE), which callsnew PhoenixConnection(this.connection)per parallel iterator scope. Because each clone receiveshaGroup = null, the guard insideMutationState.annotateMutationWithMetadata:evaluates
falseon every mutation flushed through the cloned connection. The_HAGroupNameHBase attribute therefore is not attached to those mutations, and the consistent_failover (CCF) server's mutation-block contract — which gates ACTIVE→STANDBY transitions on the presence of that attribute — is not enforced for the affected write paths.Threshold sensitivity: with
MUTATE_BATCH_SIZE = 100(default), anUPSERT SELECTof N rows triggersfloor(N/100)in-loop flushes through the cloned connection'sMutationState.send()path. The trailingN mod 100rows are joined back to the parent connection and committed via the parent's path (parent'shaGroupis non-null), so those rows DO get tagged. For N=250: 200 mutations dropped (no_HAGroupName), 50 mutations tagged.This is a correctness gap on the
PHOENIX-7562-feature-newline and must be fixed before that line merges to master.Does this PR introduce any user-facing change?
No
This is a pre-launch CCF feature branch. No customer-visible behavior change; the fix corrects an internal contract that has not shipped to users. The interface change converts an abstract method to a
default, which is source-compatible.How was this patch tested?
New integration test method
testUpsertSelectReplicatesViaCloneConnectioninReplicationLogGroupIT.Local commands run on
PHOENIX-7562-feature-newHEAD8f9655a69e:Empirical pre/post-fix
dumpTableLogCounton the same test:Was this patch authored or co-authored using generative AI tooling?
Generated-by: Claude Code (Opus 4.7)