This is an automated email from the ASF dual-hosted git repository. chinmayskulkarni pushed a commit to branch 4.x-HBase-1.4 in repository https://gitbox.apache.org/repos/asf/phoenix.git
The following commit(s) were added to refs/heads/4.x-HBase-1.4 by this push: new d5becfa PHOENIX-5302: Different isNamespaceMappingEnabled for server / client causes TableNotFoundException d5becfa is described below commit d5becfa987a3b9ff2dc401fcfce6ae593bca42d9 Author: Chinmay Kulkarni <chinmayskulka...@gmail.com> AuthorDate: Tue Jul 16 16:24:30 2019 -0700 PHOENIX-5302: Different isNamespaceMappingEnabled for server / client causes TableNotFoundException --- .../SystemCatalogCreationOnConnectionIT.java | 117 +++++++++------------ .../phoenix/query/ConnectionQueryServicesImpl.java | 70 +++++++++--- 2 files changed, 109 insertions(+), 78 deletions(-) diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/SystemCatalogCreationOnConnectionIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/SystemCatalogCreationOnConnectionIT.java index 9f12d39..d42ea28 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/SystemCatalogCreationOnConnectionIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/SystemCatalogCreationOnConnectionIT.java @@ -17,6 +17,7 @@ */ package org.apache.phoenix.end2end; +import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.SYSTEM_CATALOG_SCHEMA; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -26,6 +27,7 @@ import static org.apache.phoenix.query.BaseTest.generateUniqueName; import java.io.IOException; import java.sql.Connection; import java.sql.DriverManager; +import java.sql.ResultSet; import java.sql.SQLException; import java.util.Arrays; import java.util.HashMap; @@ -39,6 +41,7 @@ import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.HBaseTestingUtility; import org.apache.hadoop.hbase.HConstants; import org.apache.hadoop.hbase.HTableDescriptor; +import org.apache.hadoop.hbase.NamespaceNotFoundException; import org.apache.hadoop.hbase.TableName; import org.apache.phoenix.coprocessor.MetaDataProtocol; import org.apache.phoenix.exception.SQLExceptionCode; @@ -89,7 +92,7 @@ public class SystemCatalogCreationOnConnectionIT { private static class PhoenixSysCatCreationServices extends ConnectionQueryServicesImpl { - public PhoenixSysCatCreationServices(QueryServices services, PhoenixEmbeddedDriver.ConnectionInfo connectionInfo, Properties info) { + PhoenixSysCatCreationServices(QueryServices services, PhoenixEmbeddedDriver.ConnectionInfo connectionInfo, Properties info) { super(services, connectionInfo, info); } @@ -123,7 +126,7 @@ public class SystemCatalogCreationOnConnectionIT { private ConnectionQueryServices cqs; private final ReadOnlyProps overrideProps; - public PhoenixSysCatCreationTestingDriver(ReadOnlyProps props) { + PhoenixSysCatCreationTestingDriver(ReadOnlyProps props) { overrideProps = props; } @@ -140,7 +143,7 @@ public class SystemCatalogCreationOnConnectionIT { // used ConnectionQueryServices instance. This is used only in cases where we need to test server-side // changes and don't care about client-side properties set from the init method. // Reset the Connection Query Services instance so we can create a new connection to the cluster - public void resetCQS() { + void resetCQS() { cqs = null; } } @@ -180,7 +183,7 @@ public class SystemCatalogCreationOnConnectionIT { driver.getConnectionQueryServices(getJdbcUrl(), propsDoNotUpgradePropSet); hbaseTables = getHBaseTables(); assertFalse(hbaseTables.contains(PHOENIX_SYSTEM_CATALOG) || hbaseTables.contains(PHOENIX_NAMESPACE_MAPPED_SYSTEM_CATALOG)); - assertTrue(hbaseTables.size() == 0); + assertEquals(0, hbaseTables.size()); assertEquals(1, countUpgradeAttempts); } @@ -282,36 +285,25 @@ public class SystemCatalogCreationOnConnectionIT { assertEquals(0, countUpgradeAttempts); } - // Conditions: server-side namespace mapping is enabled, the first connection to the server will create only SYSTEM.CATALOG, - // the second connection has client-side namespace mapping disabled - // Expected: Throw Inconsistent namespace mapping exception when you check client-server compatibility - // - // A third connection has client-side namespace mapping enabled - // Expected: We will migrate SYSTEM.CATALOG to SYSTEM namespace and create all other SYSTEM:.* tables + // Conditions: server-side namespace mapping is enabled, the first connection to the server will not create any + // SYSTEM tables. The second connection has client-side namespace mapping enabled + // Expected: We create SYSTEM:.* tables @Test - public void testUnmappedSysCat() throws Exception { + public void testIncompatibleNSMappingServerEnabledConnectionFails() throws Exception { SystemCatalogCreationOnConnectionIT.PhoenixSysCatCreationTestingDriver driver = firstConnectionNSMappingServerEnabledClientDisabled(); driver.resetCQS(); - Properties clientProps = getClientProperties(false, false); - try { - driver.getConnectionQueryServices(getJdbcUrl(), clientProps); - fail("Client should not be able to connect to cluster with inconsistent client-server namespace mapping properties"); - } catch (SQLException sqlE) { - assertEquals(SQLExceptionCode.INCONSISTENT_NAMESPACE_MAPPING_PROPERTIES.getErrorCode(), sqlE.getErrorCode()); - } + // now try a client with ns mapping enabled + Properties clientProps = getClientProperties(true, true); + Connection conn = driver.getConnectionQueryServices(getJdbcUrl(), clientProps) + .connect(getJdbcUrl(), new Properties()); hbaseTables = getHBaseTables(); - assertTrue(hbaseTables.contains(PHOENIX_SYSTEM_CATALOG)); - assertTrue(hbaseTables.size() == 1); + assertEquals(PHOENIX_NAMESPACE_MAPPED_SYSTEM_TABLES, hbaseTables); assertEquals(0, countUpgradeAttempts); - driver.resetCQS(); - clientProps = getClientProperties(true, true); - driver.getConnectionQueryServices(getJdbcUrl(), clientProps); - hbaseTables = getHBaseTables(); - assertEquals(PHOENIX_NAMESPACE_MAPPED_SYSTEM_TABLES, hbaseTables); - // SYSTEM.CATALOG migration to the SYSTEM namespace is counted as an upgrade - assertEquals(1, countUpgradeAttempts); + ResultSet rs = conn.createStatement().executeQuery("SELECT * FROM SYSTEM.CATALOG LIMIT 1"); + // Tests that SYSTEM:CATALOG contains necessary metadata rows for itself (See PHOENIX-5302) + assertTrue(rs.next()); } // Conditions: server-side namespace mapping is disabled, the first connection to the server will create all unmapped @@ -344,42 +336,25 @@ public class SystemCatalogCreationOnConnectionIT { assertEquals(0, countUpgradeAttempts); } - // Conditions: server-side namespace mapping is disabled, the first connection to the server will create only SYSTEM:CATALOG - // and the second connection has client-side namespace mapping disabled - // Expected: The second connection should fail with Inconsistent namespace mapping exception - // - // A third connection has client-side namespace mapping enabled - // Expected: The third connection should fail with Inconsistent namespace mapping exception + // Conditions: server-side namespace mapping is disabled, the first connection to the server will not create any + // SYSTEM tables. The second connection has client-side namespace mapping disabled + // Expected: The second connection should create all SYSTEM.* tables @Test - public void testClientNSMappingDisabledConnectionFails() throws Exception { + public void testIncompatibleNSMappingServerDisabledConnectionFails() throws Exception { SystemCatalogCreationOnConnectionIT.PhoenixSysCatCreationTestingDriver driver = firstConnectionNSMappingServerDisabledClientEnabled(); driver.resetCQS(); + // now try a client with ns mapping disabled Properties clientProps = getClientProperties(false, false); - try{ - driver.getConnectionQueryServices(getJdbcUrl(), clientProps); - fail("Client should not be able to connect to cluster with inconsistent client-server namespace mapping properties"); - } catch (SQLException sqlE) { - assertEquals(SQLExceptionCode.INCONSISTENT_NAMESPACE_MAPPING_PROPERTIES.getErrorCode(), sqlE.getErrorCode()); - } + Connection conn = driver.getConnectionQueryServices(getJdbcUrl(), clientProps) + .connect(getJdbcUrl(), new Properties()); hbaseTables = getHBaseTables(); - assertTrue(hbaseTables.contains(PHOENIX_NAMESPACE_MAPPED_SYSTEM_CATALOG)); - assertTrue(hbaseTables.size() == 1); + assertEquals(PHOENIX_SYSTEM_TABLES, hbaseTables); assertEquals(0, countUpgradeAttempts); - // now try a client with ns mapping enabled - driver.resetCQS(); - clientProps = getClientProperties(true, true); - try{ - driver.getConnectionQueryServices(getJdbcUrl(), clientProps); - fail("Client should not be able to connect to cluster with inconsistent client-server namespace mapping properties"); - } catch (SQLException sqlE) { - assertEquals(SQLExceptionCode.INCONSISTENT_NAMESPACE_MAPPING_PROPERTIES.getErrorCode(), sqlE.getErrorCode()); - } - hbaseTables = getHBaseTables(); - assertTrue(hbaseTables.contains(PHOENIX_NAMESPACE_MAPPED_SYSTEM_CATALOG)); - assertTrue(hbaseTables.size() == 1); - assertEquals(0, countUpgradeAttempts); + ResultSet rs = conn.createStatement().executeQuery("SELECT * FROM SYSTEM.CATALOG LIMIT 1"); + // Tests that SYSTEM.CATALOG contains necessary metadata rows for itself (See PHOENIX-5302) + assertTrue(rs.next()); } // Conditions: The first connection creates all SYSTEM tables via "EXECUTE UPGRADE" since auto-upgrade is disabled @@ -418,7 +393,7 @@ public class SystemCatalogCreationOnConnectionIT { hbaseTables = getHBaseTables(); assertFalse(hbaseTables.contains(PHOENIX_SYSTEM_CATALOG) || hbaseTables.contains(PHOENIX_NAMESPACE_MAPPED_SYSTEM_CATALOG)); - assertTrue(hbaseTables.size() == 0); + assertEquals(0, hbaseTables.size()); // Test that we are unable to run any other queries using this connection until we upgrade final String tableName = generateUniqueName(); @@ -479,6 +454,16 @@ public class SystemCatalogCreationOnConnectionIT { return tables; } + // Check if the SYSTEM namespace has been created + private boolean isSystemNamespaceCreated() throws IOException { + try { + testUtil.getHBaseAdmin().getNamespaceDescriptor(SYSTEM_CATALOG_SCHEMA); + } catch (NamespaceNotFoundException ex) { + return false; + } + return true; + } + /** * Alter the table metadata and return modified value * @param driver @@ -565,7 +550,7 @@ public class SystemCatalogCreationOnConnectionIT { .connect(getJdbcUrl(), new Properties()); hbaseTables = getHBaseTables(); assertFalse(hbaseTables.contains(PHOENIX_SYSTEM_CATALOG) || hbaseTables.contains(PHOENIX_NAMESPACE_MAPPED_SYSTEM_CATALOG)); - assertTrue(hbaseTables.size() == 0); + assertEquals(0, hbaseTables.size()); assertEquals(1, countUpgradeAttempts); // We use the same connection to run "EXECUTE UPGRADE" @@ -592,6 +577,7 @@ public class SystemCatalogCreationOnConnectionIT { hbaseTables = getHBaseTables(); assertEquals(PHOENIX_NAMESPACE_MAPPED_SYSTEM_TABLES, hbaseTables); assertEquals(0, countUpgradeAttempts); + assertTrue(isSystemNamespaceCreated()); return driver; } @@ -609,12 +595,13 @@ public class SystemCatalogCreationOnConnectionIT { hbaseTables = getHBaseTables(); assertEquals(PHOENIX_SYSTEM_TABLES, hbaseTables); assertEquals(0, countUpgradeAttempts); + assertFalse(isSystemNamespaceCreated()); return driver; } // Conditions: server-side namespace mapping is enabled, client-side namespace mapping is disabled - // Expected: Since this is the first connection to the server, we will create SYSTEM.CATALOG but immediately - // throw an exception for inconsistent namespace mapping + // Expected: Since this is the first connection to the server, we will immediately + // throw an exception for inconsistent namespace mapping without creating any SYSTEM tables private SystemCatalogCreationOnConnectionIT.PhoenixSysCatCreationTestingDriver firstConnectionNSMappingServerEnabledClientDisabled() throws Exception { startMiniClusterWithToggleNamespaceMapping(Boolean.TRUE.toString()); @@ -628,15 +615,14 @@ public class SystemCatalogCreationOnConnectionIT { assertEquals(SQLExceptionCode.INCONSISTENT_NAMESPACE_MAPPING_PROPERTIES.getErrorCode(), sqlE.getErrorCode()); } hbaseTables = getHBaseTables(); - assertTrue(hbaseTables.contains(PHOENIX_SYSTEM_CATALOG)); - assertTrue(hbaseTables.size() == 1); + assertEquals(0, hbaseTables.size()); assertEquals(0, countUpgradeAttempts); return driver; } // Conditions: server-side namespace mapping is disabled, client-side namespace mapping is enabled - // Expected: Since this is the first connection to the server, we will create the SYSTEM namespace and create - // SYSTEM:CATALOG and then immediately throw an exception for inconsistent namespace mapping + // Expected: Since this is the first connection to the server, we will immediately throw an exception for + // inconsistent namespace mapping without creating any SYSTEM tables or SYSTEM namespace private SystemCatalogCreationOnConnectionIT.PhoenixSysCatCreationTestingDriver firstConnectionNSMappingServerDisabledClientEnabled() throws Exception { startMiniClusterWithToggleNamespaceMapping(Boolean.FALSE.toString()); @@ -650,9 +636,9 @@ public class SystemCatalogCreationOnConnectionIT { assertEquals(SQLExceptionCode.INCONSISTENT_NAMESPACE_MAPPING_PROPERTIES.getErrorCode(), sqlE.getErrorCode()); } hbaseTables = getHBaseTables(); - assertTrue(hbaseTables.contains(PHOENIX_NAMESPACE_MAPPED_SYSTEM_CATALOG)); - assertTrue(hbaseTables.size() == 1); + assertEquals(0, hbaseTables.size()); assertEquals(0, countUpgradeAttempts); + assertFalse(isSystemNamespaceCreated()); return driver; } @@ -669,6 +655,7 @@ public class SystemCatalogCreationOnConnectionIT { hbaseTables = getHBaseTables(); assertEquals(PHOENIX_SYSTEM_TABLES, hbaseTables); assertEquals(0, countUpgradeAttempts); + assertFalse(isSystemNamespaceCreated()); return driver; } } \ No newline at end of file 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 94b4f92..7d7860c 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 @@ -1125,25 +1125,33 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement QueryServicesOptions.DEFAULT_ALLOW_ONLINE_TABLE_SCHEMA_UPDATE); } - void ensureNamespaceCreated(String schemaName) throws SQLException { + /** + * Ensure that the HBase namespace is created/exists already + * @param schemaName Phoenix schema name for which we ensure existence of the HBase namespace + * @return true if we created the HBase namespace because it didn't already exist + * @throws SQLException If there is an exception creating the HBase namespace + */ + boolean ensureNamespaceCreated(String schemaName) throws SQLException { SQLException sqlE = null; + boolean createdNamespace = false; try (HBaseAdmin admin = getAdmin()) { NamespaceDescriptor namespaceDescriptor = null; try { namespaceDescriptor = admin.getNamespaceDescriptor(schemaName); - } catch (org.apache.hadoop.hbase.NamespaceNotFoundException e) { + } catch (NamespaceNotFoundException ignored) { } if (namespaceDescriptor == null) { namespaceDescriptor = NamespaceDescriptor.create(schemaName).build(); admin.createNamespace(namespaceDescriptor); + createdNamespace = true; } - return; } catch (IOException e) { sqlE = ServerUtil.parseServerException(e); } finally { if (sqlE != null) { throw sqlE; } } + return createdNamespace; } /** @@ -1169,6 +1177,7 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement try (HBaseAdmin admin = getAdmin()) { final String quorum = ZKConfig.getZKQuorumServersString(config); final String znode = this.getProps().get(HConstants.ZOOKEEPER_ZNODE_PARENT); + boolean createdNamespace = false; LOGGER.debug("Found quorum: " + quorum + ":" + znode); if (isMetaTable) { @@ -1176,7 +1185,7 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement try { // SYSTEM namespace needs to be created via HBase APIs because "CREATE SCHEMA" statement tries to write // its metadata in SYSTEM:CATALOG table. Without SYSTEM namespace, SYSTEM:CATALOG table cannot be created - ensureNamespaceCreated(QueryConstants.SYSTEM_SCHEMA_NAME); + createdNamespace = ensureNamespaceCreated(QueryConstants.SYSTEM_SCHEMA_NAME); } catch (PhoenixIOException e) { // We could either: // 1) Not access the NS descriptor. The NS may or may not exist at this point @@ -1188,14 +1197,25 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement } if (admin.tableExists(SchemaUtil.getPhysicalTableName(SYSTEM_CATALOG_NAME_BYTES, false))) { // SYSTEM.CATALOG exists, so at this point, we have 3 cases: - // 1) If server-side namespace mapping is disabled, throw Inconsistent namespace mapping exception - // 2) If server-side namespace mapping is enabled and SYSCAT needs to be upgraded, upgrade SYSCAT - // and also migrate SYSTEM tables to the SYSTEM namespace - // 3. If server-side namespace mapping is enabled and SYSCAT doesn't need to be upgraded, we still - // need to migrate SYSTEM tables to the SYSTEM namespace using the + // 1) If server-side namespace mapping is disabled, drop the SYSTEM namespace if it was created + // above and throw Inconsistent namespace mapping exception + // 2) If server-side namespace mapping is enabled and SYSTEM.CATALOG needs to be upgraded, + // upgrade SYSTEM.CATALOG and also migrate SYSTEM tables to the SYSTEM namespace + // 3. If server-side namespace mapping is enabled and SYSTEM.CATALOG doesn't need to be + // upgraded, we still need to migrate SYSTEM tables to the SYSTEM namespace using the // {@link ensureSystemTablesMigratedToSystemNamespace(ReadOnlyProps)} method (as part of // {@link upgradeSystemTables(String, Properties)}) - checkClientServerCompatibility(SYSTEM_CATALOG_NAME_BYTES); + try { + checkClientServerCompatibility(SYSTEM_CATALOG_NAME_BYTES); + } catch (SQLException possibleCompatException) { + // Handles Case 1: Drop the SYSTEM namespace in case it was created above + if (createdNamespace && possibleCompatException.getErrorCode() == + SQLExceptionCode.INCONSISTENT_NAMESPACE_MAPPING_PROPERTIES.getErrorCode()) { + ensureNamespaceDropped(QueryConstants.SYSTEM_SCHEMA_NAME); + } + // rethrow the SQLException + throw possibleCompatException; + } // Thrown so we can force an upgrade which will just migrate SYSTEM tables to the SYSTEM namespace throw new UpgradeRequiredException(MIN_SYSTEM_TABLE_TIMESTAMP); } @@ -1251,7 +1271,31 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement return null; } if (isMetaTable && !isUpgradeRequired()) { - checkClientServerCompatibility(SchemaUtil.getPhysicalName(SYSTEM_CATALOG_NAME_BYTES, this.getProps()).getName()); + try { + checkClientServerCompatibility(SchemaUtil.getPhysicalName(SYSTEM_CATALOG_NAME_BYTES, + this.getProps()).getName()); + } catch (SQLException possibleCompatException) { + if (possibleCompatException.getErrorCode() == + SQLExceptionCode.INCONSISTENT_NAMESPACE_MAPPING_PROPERTIES.getErrorCode()) { + try { + // In case we wrongly created SYSTEM.CATALOG or SYSTEM:CATALOG, we should drop it + admin.disableTable(physicalTableName); + admin.deleteTable(physicalTableName); + } catch (org.apache.hadoop.hbase.TableNotFoundException ignored) { + // Ignore this since it just means that another client with a similar set of + // incompatible configs and conditions beat us to dropping the SYSCAT HBase table + } + if (createdNamespace && + SchemaUtil.isNamespaceMappingEnabled(PTableType.SYSTEM, this.getProps())) { + // We should drop the SYSTEM namespace which we just created, since + // server-side namespace mapping is disabled + ensureNamespaceDropped(QueryConstants.SYSTEM_SCHEMA_NAME); + } + } + // rethrow the SQLException + throw possibleCompatException; + } + } return null; } else { @@ -5163,7 +5207,7 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement ReadOnlyProps props = this.getProps(); boolean dropMetadata = props.getBoolean(DROP_METADATA_ATTRIB, DEFAULT_DROP_METADATA); if (dropMetadata) { - ensureNamespaceDropped(schemaName, result.getMutationTime()); + ensureNamespaceDropped(schemaName); } break; default: @@ -5172,7 +5216,7 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement return result; } - private void ensureNamespaceDropped(String schemaName, long mutationTime) throws SQLException { + private void ensureNamespaceDropped(String schemaName) throws SQLException { SQLException sqlE = null; try (HBaseAdmin admin = getAdmin()) { final String quorum = ZKConfig.getZKQuorumServersString(config);