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 <chfen...@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 <chinmayskulka...@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