You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@phoenix.apache.org by ch...@apache.org on 2020/03/02 22:32:47 UTC

[phoenix] branch 4.x-HBase-1.3 updated: PHOENIX-5636: Improve the error message when client connects to server with higher major version

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

chinmayskulkarni pushed a commit to branch 4.x-HBase-1.3
in repository https://gitbox.apache.org/repos/asf/phoenix.git


The following commit(s) were added to refs/heads/4.x-HBase-1.3 by this push:
     new 4522286  PHOENIX-5636: Improve the error message when client connects to server with higher major version
4522286 is described below

commit 45222868179cde5d2e58849eb9ac2a23a1c073e6
Author: Christine Feng <ch...@gmail.com>
AuthorDate: Fri Jan 31 14:33:24 2020 -0800

    PHOENIX-5636: Improve the error message when client connects to server with higher major version
    
    Signed-off-by: Chinmay Kulkarni <ch...@apache.org>
---
 .../phoenix/query/ConnectionQueryServicesImpl.java | 57 ++++++++++++++--------
 .../java/org/apache/phoenix/util/MetaDataUtil.java | 49 +++++++++++++++++--
 .../org/apache/phoenix/util/MetaDataUtilTest.java  | 38 +++++++++++----
 3 files changed, 110 insertions(+), 34 deletions(-)

diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java b/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
index 49a1405..b708b75 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
@@ -1479,20 +1479,10 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
         return MetaDataUtil.decodeHasIndexWALCodec(serverVersion);
     }
 
-    private static boolean isCompatible(Long serverVersion) {
-        if (serverVersion == null) {
-            return false;
-        }
-        return MetaDataUtil.areClientAndServerCompatible(serverVersion);
-    }
 
     private void checkClientServerCompatibility(byte[] metaTable) throws SQLException,
             AccessDeniedException {
-        StringBuilder buf = new StringBuilder("Newer Phoenix clients can't communicate with older "
-                + "Phoenix servers. The following servers require an updated "
-                + QueryConstants.DEFAULT_COPROCESS_JAR_NAME
-                + " to be put in the classpath of HBase: ");
-        boolean isIncompatible = false;
+        StringBuilder errorMessage = new StringBuilder();
         int minHBaseVersion = Integer.MAX_VALUE;
         boolean isTableNamespaceMappingEnabled = false;
         long systemCatalogTimestamp = Long.MAX_VALUE;
@@ -1545,11 +1535,26 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
                 long serverJarVersion = versionResponse.getVersion();
                 isTableNamespaceMappingEnabled |= MetaDataUtil.decodeTableNamespaceMappingEnabled(serverJarVersion);
 
-                if (!isCompatible(serverJarVersion)) {
-                    isIncompatible = true;
-                    HRegionLocation name = regionMap.get(result.getKey());
-                    buf.append(name);
-                    buf.append(';');
+                MetaDataUtil.ClientServerCompatibility compatibility = MetaDataUtil.areClientAndServerCompatible(serverJarVersion);
+                if (!compatibility.getIsCompatible()) {
+                    if (compatibility.getErrorCode() == SQLExceptionCode.OUTDATED_JARS.getErrorCode()) {
+                        HRegionLocation name = regionMap.get(result.getKey());
+                        errorMessage.append("Newer Phoenix clients can't communicate with older "
+                                + "Phoenix servers. Client version: "
+                                + MetaDataProtocol.CURRENT_CLIENT_VERSION
+                                + "; Server version: "
+                                + getServerVersion(serverJarVersion)
+                                + " The following servers require an updated "
+                                + QueryConstants.DEFAULT_COPROCESS_JAR_NAME
+                                + " to be put in the classpath of HBase: ");
+                        errorMessage.append(name);
+                        errorMessage.append(';');
+                    } else if (compatibility.getErrorCode() == SQLExceptionCode.INCOMPATIBLE_CLIENT_SERVER_JAR.getErrorCode()) {
+                        errorMessage.append("Major version of client is less than that of the server. Client version: "
+                                + MetaDataProtocol.CURRENT_CLIENT_VERSION
+                                + "; Server version: "
+                                + getServerVersion(serverJarVersion));
+                    }
                 }
                 hasIndexWALCodec &= hasIndexWALCodec(serverJarVersion);
                 if (minHBaseVersion > MetaDataUtil.decodeHBaseVersion(serverJarVersion)) {
@@ -1562,6 +1567,15 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
                     systemCatalogTimestamp = systemCatalogTimestamp < versionResponse.getSystemCatalogTimestamp() ?
                       systemCatalogTimestamp: versionResponse.getSystemCatalogTimestamp();
                 }
+
+                if (compatibility.getErrorCode() != 0) {
+                    if (compatibility.getErrorCode() == SQLExceptionCode.OUTDATED_JARS.getErrorCode()) {
+                        errorMessage.setLength(errorMessage.length()-1);
+                        throw new SQLExceptionInfo.Builder(SQLExceptionCode.OUTDATED_JARS).setMessage(errorMessage.toString()).build().buildException();
+                    } else if (compatibility.getErrorCode() == SQLExceptionCode.INCOMPATIBLE_CLIENT_SERVER_JAR.getErrorCode()) {
+                        throw new SQLExceptionInfo.Builder(SQLExceptionCode.INCOMPATIBLE_CLIENT_SERVER_JAR).setMessage(errorMessage.toString()).build().buildException();
+                    }
+                }
             }
             if (isTableNamespaceMappingEnabled != SchemaUtil.isNamespaceMappingEnabled(PTableType.TABLE,
                     getProps())) { throw new SQLExceptionInfo.Builder(
@@ -1580,15 +1594,18 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
                 }
             }
         }
-        if (isIncompatible) {
-            buf.setLength(buf.length()-1);
-            throw new SQLExceptionInfo.Builder(SQLExceptionCode.OUTDATED_JARS).setMessage(buf.toString()).build().buildException();
-        }
+
         if (systemCatalogTimestamp < MIN_SYSTEM_TABLE_TIMESTAMP) {
             throw new UpgradeRequiredException(systemCatalogTimestamp);
         }
     }
 
+    private String getServerVersion(long serverJarVersion) {
+        return (VersionUtil.decodeMajorVersion(MetaDataUtil.decodePhoenixVersion(serverJarVersion)) + "."
+                + VersionUtil.decodeMinorVersion(MetaDataUtil.decodePhoenixVersion(serverJarVersion)) + "."
+                + VersionUtil.decodePatchVersion(MetaDataUtil.decodePhoenixVersion(serverJarVersion)));
+    }
+
     /**
      * Invoke the SYSTEM.CHILD_LINK metadata coprocessor endpoint
      * @param parentTableKey key corresponding to the parent of the view
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/util/MetaDataUtil.java b/phoenix-core/src/main/java/org/apache/phoenix/util/MetaDataUtil.java
index ae0d2ca..c2a82e5 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/util/MetaDataUtil.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/util/MetaDataUtil.java
@@ -32,6 +32,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.NavigableMap;
 
+import com.google.common.annotations.VisibleForTesting;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.hbase.Cell;
 import org.apache.hadoop.hbase.CellUtil;
@@ -59,6 +60,8 @@ import org.apache.hadoop.hbase.util.Bytes;
 import org.apache.hadoop.ipc.RemoteException;
 import org.apache.phoenix.coprocessor.MetaDataEndpointImpl;
 import org.apache.phoenix.coprocessor.MetaDataProtocol;
+import org.apache.phoenix.exception.SQLExceptionCode;
+import org.apache.phoenix.hbase.index.util.GenericKeyValueBuilder;
 import org.apache.phoenix.hbase.index.util.ImmutableBytesPtr;
 import org.apache.phoenix.hbase.index.util.IndexManagementUtil;
 import org.apache.phoenix.hbase.index.util.KeyValueBuilder;
@@ -122,7 +125,33 @@ public class MetaDataUtil {
             HColumnDescriptor.KEEP_DELETED_CELLS,
             HColumnDescriptor.REPLICATION_SCOPE);
 
-    public static boolean areClientAndServerCompatible(long serverHBaseAndPhoenixVersion) {
+    public static class ClientServerCompatibility {
+
+        private int errorCode;
+        private boolean isCompatible;
+
+        ClientServerCompatibility() {
+            this.errorCode = 0;
+        }
+
+        public int getErrorCode() {
+            return this.errorCode;
+        }
+
+        void setErrorCode(int errorCode) {
+            this.errorCode = errorCode;
+        }
+
+        public boolean getIsCompatible() {
+            return this.isCompatible;
+        }
+
+        void setCompatible(boolean isCompatible) {
+            this.isCompatible = isCompatible;
+        }
+    }
+
+    public static ClientServerCompatibility areClientAndServerCompatible(long serverHBaseAndPhoenixVersion) {
         // As of 3.0, we allow a client and server to differ for the minor version.
         // Care has to be taken to upgrade the server before the client, as otherwise
         // the client may call expressions that don't yet exist on the server.
@@ -132,12 +161,24 @@ public class MetaDataUtil {
     }
 
     // Default scope for testing
-    static boolean areClientAndServerCompatible(int serverVersion, int clientMajorVersion, int clientMinorVersion) {
+    @VisibleForTesting
+    static ClientServerCompatibility areClientAndServerCompatible(int serverVersion, int clientMajorVersion, int clientMinorVersion) {
         // A server and client with the same major and minor version number must be compatible.
         // So it's important that we roll the PHOENIX_MAJOR_VERSION or PHOENIX_MINOR_VERSION
         // when we make an incompatible change.
-        return VersionUtil.encodeMinPatchVersion(clientMajorVersion, clientMinorVersion) <= serverVersion && // Minor major and minor cannot be ahead of server
-                VersionUtil.encodeMaxMinorVersion(clientMajorVersion) >= serverVersion; // Major version must at least be up to server version
+        ClientServerCompatibility compatibility = new ClientServerCompatibility();
+        if (VersionUtil.encodeMinPatchVersion(clientMajorVersion, clientMinorVersion) > serverVersion) { // Client major and minor cannot be ahead of server
+            compatibility.setErrorCode(SQLExceptionCode.OUTDATED_JARS.getErrorCode());
+            compatibility.setCompatible(false);
+            return compatibility;
+        } else if (VersionUtil.encodeMaxMinorVersion(clientMajorVersion) < serverVersion) { // Client major version must at least be up to server major version
+            compatibility.setErrorCode(SQLExceptionCode.INCOMPATIBLE_CLIENT_SERVER_JAR.getErrorCode());
+            compatibility.setCompatible(false);
+            return compatibility;
+        }
+        compatibility.setCompatible(true);
+        return compatibility;
+
     }
 
     // Given the encoded integer representing the phoenix version in the encoded version value.
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/util/MetaDataUtilTest.java b/phoenix-core/src/test/java/org/apache/phoenix/util/MetaDataUtilTest.java
index f2557e8..57f4bf1 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/util/MetaDataUtilTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/util/MetaDataUtilTest.java
@@ -34,6 +34,7 @@ import org.apache.hadoop.hbase.util.ByteBufferUtils;
 import org.apache.hadoop.hbase.util.Bytes;
 import org.apache.hadoop.hbase.util.VersionInfo;
 import org.apache.phoenix.coprocessor.MetaDataProtocol;
+import org.apache.phoenix.exception.SQLExceptionCode;
 import org.apache.phoenix.hbase.index.util.GenericKeyValueBuilder;
 import org.apache.phoenix.hbase.index.util.ImmutableBytesPtr;
 import org.apache.phoenix.hbase.index.util.KeyValueBuilder;
@@ -77,16 +78,33 @@ public class MetaDataUtilTest {
 
     @Test
     public void testCompatibility() {
-        assertTrue(MetaDataUtil.areClientAndServerCompatible(VersionUtil.encodeVersion(1,2,1), 1, 2));
-        assertTrue(MetaDataUtil.areClientAndServerCompatible(VersionUtil.encodeVersion(1,2,10), 1, 1));
-        assertTrue(MetaDataUtil.areClientAndServerCompatible(VersionUtil.encodeVersion(1,2,0), 1, 2));
-        assertTrue(MetaDataUtil.areClientAndServerCompatible(VersionUtil.encodeVersion(1,2,255), 1, 2));
-        assertTrue(MetaDataUtil.areClientAndServerCompatible(VersionUtil.encodeVersion(2,2,0), 2, 0));
-        assertTrue(MetaDataUtil.areClientAndServerCompatible(VersionUtil.encodeVersion(2,10,36), 2, 9));
-        assertFalse(MetaDataUtil.areClientAndServerCompatible(VersionUtil.encodeVersion(3,1,10), 4, 0));
-        assertFalse(MetaDataUtil.areClientAndServerCompatible(VersionUtil.encodeVersion(3,1,10), 2, 0));
-        assertFalse(MetaDataUtil.areClientAndServerCompatible(VersionUtil.encodeVersion(3,1,10), 3, 2));
-        assertFalse(MetaDataUtil.areClientAndServerCompatible(VersionUtil.encodeVersion(3,1,10), 3, 5));
+        assertTrue(MetaDataUtil.areClientAndServerCompatible(VersionUtil.encodeVersion(1,2,1), 1, 2).getIsCompatible());
+        assertTrue(MetaDataUtil.areClientAndServerCompatible(VersionUtil.encodeVersion(1,2,10), 1, 1).getIsCompatible());
+        assertTrue(MetaDataUtil.areClientAndServerCompatible(VersionUtil.encodeVersion(1,2,0), 1, 2).getIsCompatible());
+        assertTrue(MetaDataUtil.areClientAndServerCompatible(VersionUtil.encodeVersion(1,2,255), 1, 2).getIsCompatible());
+        assertTrue(MetaDataUtil.areClientAndServerCompatible(VersionUtil.encodeVersion(2,2,0), 2, 0).getIsCompatible());
+        assertTrue(MetaDataUtil.areClientAndServerCompatible(VersionUtil.encodeVersion(2,10,36), 2, 9).getIsCompatible());
+        assertFalse(MetaDataUtil.areClientAndServerCompatible(VersionUtil.encodeVersion(3,1,10), 4, 0).getIsCompatible());
+        assertFalse(MetaDataUtil.areClientAndServerCompatible(VersionUtil.encodeVersion(3,1,10), 2, 0).getIsCompatible());
+        assertFalse(MetaDataUtil.areClientAndServerCompatible(VersionUtil.encodeVersion(3,1,10), 3, 2).getIsCompatible());
+        assertFalse(MetaDataUtil.areClientAndServerCompatible(VersionUtil.encodeVersion(3,1,10), 3, 5).getIsCompatible());
+    }
+
+    @Test
+    public void testCompatibilityNewerClient() {
+        MetaDataUtil.ClientServerCompatibility compatibility1 = MetaDataUtil.areClientAndServerCompatible(VersionUtil.encodeVersion(3,1,10), 4, 0);
+        assertFalse(compatibility1.getIsCompatible());
+        assertEquals(compatibility1.getErrorCode(), SQLExceptionCode.OUTDATED_JARS.getErrorCode());
+        MetaDataUtil.ClientServerCompatibility compatibility2 = MetaDataUtil.areClientAndServerCompatible(VersionUtil.encodeVersion(3,1,10), 3, 2);
+        assertFalse(compatibility2.getIsCompatible());
+        assertEquals(compatibility2.getErrorCode(), SQLExceptionCode.OUTDATED_JARS.getErrorCode());
+    }
+
+    @Test
+    public void testCompatibilityMismatchedMajorVersions() {
+        MetaDataUtil.ClientServerCompatibility compatibility = MetaDataUtil.areClientAndServerCompatible(VersionUtil.encodeVersion(3,1,10), 2, 0);
+        assertFalse(compatibility.getIsCompatible());
+        assertEquals(compatibility.getErrorCode(), SQLExceptionCode.INCOMPATIBLE_CLIENT_SERVER_JAR.getErrorCode());
     }
 
     @Test