This is an automated email from the ASF dual-hosted git repository.

tkhurana pushed a commit to branch PHOENIX-7562-feature-new
in repository https://gitbox.apache.org/repos/asf/phoenix.git


The following commit(s) were added to refs/heads/PHOENIX-7562-feature-new by 
this push:
     new c0ed9893d6 PHOENIX-7983 Key ReplicationLogGroup cache on (port, 
startcode) instead of host-inclusive ServerName (#2595)
c0ed9893d6 is described below

commit c0ed9893d67d0d38dac726bc9c18840de1664655
Author: tkhurana <[email protected]>
AuthorDate: Tue Aug 11 13:52:06 2026 -0700

    PHOENIX-7983 Key ReplicationLogGroup cache on (port, startcode) instead of 
host-inclusive ServerName (#2595)
    
    The same physical RegionServer can be observed under two ServerName
    spellings that differ only in host -- a declared FQDN (from
    hbase.unsafe.regionserver.hostname) versus the pod IP the master hands
    back at reportForDuty when reverse-DNS is enabled. Different coprocessor
    environments surface different spellings for the same server (prewarm
    PhoenixRegionServerEndpoint vs write-path IndexRegionObserver), so keying
    INSTANCES on serverName.getServerName() split one logical group into two
    independent instances, each with its own Disruptor and sequence counter.
    
    Key on (port, startcode) instead, which excludes the host: two distinct
    live RegionServers can never share a port on the same host at the same
    time, so mini-cluster ITs running several RS in one JVM stay distinct
    (each binds its own ephemeral port), while the single-RS case collapses
    the FQDN/IP spellings to one entry. closeAllForServer() matches on the
    same canonical identity.
---
 .../phoenix/replication/ReplicationLogGroup.java   | 29 +++++++++++++++--
 .../apache/phoenix/jdbc/HAGroupStoreManagerIT.java |  4 +--
 .../replication/ReplicationLogGroupTest.java       | 37 ++++++++++++++++++++++
 3 files changed, 66 insertions(+), 4 deletions(-)

diff --git 
a/phoenix-core-server/src/main/java/org/apache/phoenix/replication/ReplicationLogGroup.java
 
b/phoenix-core-server/src/main/java/org/apache/phoenix/replication/ReplicationLogGroup.java
index 845c181e70..d4218d69da 100644
--- 
a/phoenix-core-server/src/main/java/org/apache/phoenix/replication/ReplicationLogGroup.java
+++ 
b/phoenix-core-server/src/main/java/org/apache/phoenix/replication/ReplicationLogGroup.java
@@ -218,8 +218,29 @@ public class ReplicationLogGroup {
   protected static final ConcurrentHashMap<String, ReplicationLogGroup> 
INSTANCES =
     new ConcurrentHashMap<>();
 
+  /**
+   * Canonical, DNS-independent identity of a RegionServer within this JVM's 
{@link #INSTANCES}
+   * cache. Uses {@code (port, startcode)} and deliberately EXCLUDES the 
hostname.
+   * <p>
+   * The same physical RS can be observed under two {@link ServerName} 
spellings that differ only in
+   * host — e.g. a declared FQDN (from {@code 
hbase.unsafe.regionserver.hostname}) versus the pod IP
+   * the master hands back at reportForDuty when reverse-DNS is enabled. 
Different coprocessor
+   * environments then surface different spellings for the same server (prewarm
+   * {@code PhoenixRegionServerEndpoint} vs write-path {@code 
IndexRegionObserver}). Keying on
+   * {@code serverName.getServerName()} (host-inclusive) split one logical 
group into two
+   * independent instances, each with its own Disruptor and sequence counter.
+   * <p>
+   * {@code (port, startcode)} is a safe discriminator: two distinct live 
RegionServers can never
+   * share a port on the same host at the same time, so in the mini-cluster 
ITs that run several
+   * RegionServers in one JVM they remain distinct (each binds its own 
ephemeral port), while the
+   * single-RS production/kind case collapses the FQDN/IP spellings to one 
entry.
+   */
+  private static String serverIdentity(ServerName serverName) {
+    return serverName.getPort() + "," + serverName.getStartcode();
+  }
+
   private static String instanceKey(ServerName serverName, String haGroupName) 
{
-    return serverName.getServerName() + "|" + haGroupName;
+    return serverIdentity(serverName) + "|" + haGroupName;
   }
 
   protected final Configuration conf;
@@ -1008,8 +1029,12 @@ public class ReplicationLogGroup {
     LOG.info("Closing all ReplicationLogGroup instances for server {}, total 
count={}", serverName,
       INSTANCES.size());
     List<ReplicationLogGroup> groups = new ArrayList<>(INSTANCES.values());
+    String identity = serverIdentity(serverName);
     for (ReplicationLogGroup group : groups) {
-      if (group.serverName.equals(serverName)) {
+      // Match on the canonical (port, startcode) identity, not the 
host-inclusive ServerName, so a
+      // caller passing one host spelling still purges a group cached under 
the other (see
+      // serverIdentity).
+      if (serverIdentity(group.serverName).equals(identity)) {
         try {
           group.close();
         } catch (Exception e) {
diff --git 
a/phoenix-core/src/it/java/org/apache/phoenix/jdbc/HAGroupStoreManagerIT.java 
b/phoenix-core/src/it/java/org/apache/phoenix/jdbc/HAGroupStoreManagerIT.java
index 18e83aa9d5..912ac32ce4 100644
--- 
a/phoenix-core/src/it/java/org/apache/phoenix/jdbc/HAGroupStoreManagerIT.java
+++ 
b/phoenix-core/src/it/java/org/apache/phoenix/jdbc/HAGroupStoreManagerIT.java
@@ -700,8 +700,8 @@ public class HAGroupStoreManagerIT extends HABaseIT {
         HAGroupStoreRecord.HAGroupState.STANDBY);
 
       HRegionServer regionServer = 
CLUSTERS.getHBaseCluster1().getHBaseCluster().getRegionServer(0);
-      replicationLogGroup = ReplicationLogGroup.get(cluster1Conf, 
regionServer.getServerName(),
-        haGroupName, cluster1HAManager).get();
+      replicationLogGroup = ReplicationLogGroup
+        .get(cluster1Conf, regionServer.getServerName(), haGroupName, 
cluster1HAManager).get();
       replicationLogReplay = ReplicationLogReplay.get(cluster2Conf, 
haGroupName);
       replicationLogReplay.startReplay();
 
diff --git 
a/phoenix-core/src/test/java/org/apache/phoenix/replication/ReplicationLogGroupTest.java
 
b/phoenix-core/src/test/java/org/apache/phoenix/replication/ReplicationLogGroupTest.java
index 22db791b73..4ebb8c3c07 100644
--- 
a/phoenix-core/src/test/java/org/apache/phoenix/replication/ReplicationLogGroupTest.java
+++ 
b/phoenix-core/src/test/java/org/apache/phoenix/replication/ReplicationLogGroupTest.java
@@ -1348,6 +1348,43 @@ public class ReplicationLogGroupTest extends 
ReplicationLogBaseTest {
     g2_1.close();
   }
 
+  /**
+   * The instance cache is keyed on (port, startcode), NOT the host-inclusive 
ServerName string. The
+   * same physical RegionServer can be observed under two ServerName spellings 
that differ only in
+   * host — e.g. a declared FQDN vs the pod IP the master hands back at 
reportForDuty — from
+   * different coprocessor environments. Both must resolve to ONE 
ReplicationLogGroup; a
+   * host-inclusive key split it into two independent instances (two 
Disruptors / sequence
+   * counters).
+   */
+  @Test
+  public void testCachingIsHostSpellingIndependent() throws Exception {
+    final String haGroupId = "testHAGroupHostSpelling";
+    // Two spellings of the SAME physical server: same port + startcode, host 
differs (FQDN vs IP).
+    ServerName asFqdn = ServerName.valueOf("rs-0.rs.ns.svc.cluster.local", 
serverName.getPort(),
+      serverName.getStartcode());
+    ServerName asIp =
+      ServerName.valueOf("10.244.1.25", serverName.getPort(), 
serverName.getStartcode());
+
+    ReplicationLogGroup viaFqdn =
+      ReplicationLogGroup.get(conf, asFqdn, haGroupId, 
haGroupStoreManager).get();
+    ReplicationLogGroup viaIp =
+      ReplicationLogGroup.get(conf, asIp, haGroupId, 
haGroupStoreManager).get();
+
+    assertSame("Two host spellings of the same (port, startcode) must share 
one instance", viaFqdn,
+      viaIp);
+
+    // A genuinely different server (distinct port — two live RS can never 
share a port on a host,
+    // e.g. mini-cluster ITs) must still get its own instance.
+    ServerName otherServer =
+      ServerName.valueOf(asIp.getHostname(), serverName.getPort() + 1, 
serverName.getStartcode());
+    ReplicationLogGroup viaOther =
+      ReplicationLogGroup.get(conf, otherServer, haGroupId, 
haGroupStoreManager).get();
+    assertNotEquals("A distinct port must yield a distinct instance", viaFqdn, 
viaOther);
+
+    viaFqdn.close();
+    viaOther.close();
+  }
+
   /**
    * Tests that close() removes the instance from the cache. Verifies that 
after closing, a new call
    * to get() creates a new instance.

Reply via email to