@Test(timeout=60000) public void testTruncateNotDisabledTable() throws Exception { final TableName tableName = TableName.valueOf("testTruncateNotDisabledTable"); final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); MasterProcedureTestingUtility.createTable(procExec, tableName, null, "f"); long procId = ProcedureTestingUtility.submitAndWait(procExec, new TruncateTableProcedure(procExec.getEnvironment(), tableName, false)); // Second delete should fail with TableNotDisabled ProcedureInfo result = procExec.getResult(procId); assertTrue(result.isFailed()); LOG.debug("Truncate failed with exception: " + result.getExceptionFullMessage()); assertTrue( ProcedureTestingUtility.getExceptionCause(result) instanceof TableNotDisabledException); }
public TableEventHandler(EventType eventType, byte [] tableName, Server server, MasterServices masterServices) throws IOException { super(server, eventType); this.masterServices = masterServices; this.tableName = tableName; try { this.masterServices.checkTableModifiable(tableName); } catch (TableNotDisabledException ex) { if (isOnlineSchemaChangeAllowed() && eventType.isOnlineSchemaChangeSupported()) { LOG.debug("Ignoring table not disabled exception " + "for supporting online schema changes."); } else { throw ex; } } this.tableNameStr = Bytes.toString(this.tableName); }
@Test(timeout=60000) public void testRestoreSnapshotToEnabledTable() throws Exception { final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); try { UTIL.getAdmin().enableTable(snapshotTableName); long procId = ProcedureTestingUtility.submitAndWait( procExec, new RestoreSnapshotProcedure(procExec.getEnvironment(), snapshotHTD, snapshot)); Procedure<?> result = procExec.getResult(procId); assertTrue(result.isFailed()); LOG.debug("Restore snapshot failed with exception: " + result.getException()); assertTrue( ProcedureTestingUtility.getExceptionCause(result) instanceof TableNotDisabledException); } finally { UTIL.getAdmin().disableTable(snapshotTableName); } }
@Test public void testTruncateNotDisabledTable() throws Exception { final TableName tableName = TableName.valueOf(name.getMethodName()); final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); MasterProcedureTestingUtility.createTable(procExec, tableName, null, "f"); long procId = ProcedureTestingUtility.submitAndWait(procExec, new TruncateTableProcedure(procExec.getEnvironment(), tableName, false)); // Second delete should fail with TableNotDisabled Procedure<?> result = procExec.getResult(procId); assertTrue(result.isFailed()); LOG.debug("Truncate failed with exception: " + result.getException()); assertTrue( ProcedureTestingUtility.getExceptionCause(result) instanceof TableNotDisabledException); }
@Override public Future<Void> restoreSnapshotAsync(final String snapshotName) throws IOException, RestoreSnapshotException { TableName tableName = getTableNameBeforeRestoreSnapshot(snapshotName); // The table does not exists, switch to clone. if (!tableExists(tableName)) { return cloneSnapshotAsync(snapshotName, tableName); } // Check if the table is disabled if (!isTableDisabled(tableName)) { throw new TableNotDisabledException(tableName); } return internalRestoreSnapshotAsync(snapshotName, tableName, false); }
public TableEventHandler prepare() throws IOException { //acquire the table write lock, blocking this.tableLock = masterServices.getTableLockManager() .writeLock(tableName, eventType.toString()); this.tableLock.acquire(); boolean success = false; try { try { this.masterServices.checkTableModifiable(tableName); } catch (TableNotDisabledException ex) { if (isOnlineSchemaChangeAllowed() && eventType.isOnlineSchemaChangeSupported()) { LOG.debug("Ignoring table not disabled exception " + "for supporting online schema changes."); } else { throw ex; } } prepareWithTableLock(); success = true; } finally { if (!success ) { releaseTableLock(); } } this.isPrepareCalled = true; return this; }
@Override public void checkTableModifiable(final TableName tableName) throws IOException, TableNotFoundException, TableNotDisabledException { if (isCatalogTable(tableName)) { throw new IOException("Can't modify catalog tables"); } if (!MetaTableAccessor.tableExists(getConnection(), tableName)) { throw new TableNotFoundException(tableName); } if (!getAssignmentManager().getTableStateManager(). isTableState(tableName, ZooKeeperProtos.Table.State.DISABLED)) { throw new TableNotDisabledException(tableName); } }
private boolean prepareTruncate(final MasterProcedureEnv env) throws IOException { try { env.getMasterServices().checkTableModifiable(getTableName()); } catch (TableNotFoundException|TableNotDisabledException e) { setFailure("master-truncate-table", e); return false; } return true; }
/** * Check whether a table is modifiable - exists and either offline or online with config set * @param env MasterProcedureEnv * @param tableName name of the table * @throws IOException */ public static void checkTableModifiable(final MasterProcedureEnv env, final TableName tableName) throws IOException { // Checks whether the table exists if (!MetaTableAccessor.tableExists(env.getMasterServices().getConnection(), tableName)) { throw new TableNotFoundException(tableName); } // We only execute this procedure with table online if online schema change config is set. if (!env.getMasterServices().getAssignmentManager().getTableStateManager() .isTableState(tableName, ZooKeeperProtos.Table.State.DISABLED) && !MasterDDLOperationHelper.isOnlineSchemaChangeAllowed(env)) { throw new TableNotDisabledException(tableName); } }
/** * Action before any real action of enabling table. Set the exception in the procedure instead * of throwing it. This approach is to deal with backward compatible with 1.0. * @param env MasterProcedureEnv * @return whether the table passes the necessary checks * @throws IOException */ private boolean prepareEnable(final MasterProcedureEnv env) throws IOException { boolean canTableBeEnabled = true; // Check whether table exists if (!MetaTableAccessor.tableExists(env.getMasterServices().getConnection(), tableName)) { setFailure("master-enable-table", new TableNotFoundException(tableName)); canTableBeEnabled = false; } else if (!skipTableStateCheck) { // There could be multiple client requests trying to disable or enable // the table at the same time. Ensure only the first request is honored // After that, no other requests can be accepted until the table reaches // DISABLED or ENABLED. // // Note: in 1.0 release, we called TableStateManager.setTableStateIfInStates() to set // the state to ENABLING from DISABLED. The implementation was done before table lock // was implemented. With table lock, there is no need to set the state here (it will // set the state later on). A quick state check should be enough for us to move forward. TableStateManager tsm = env.getMasterServices().getAssignmentManager().getTableStateManager(); if (!tsm.isTableState(tableName, ZooKeeperProtos.Table.State.DISABLED)) { LOG.info("Table " + tableName + " isn't disabled; skipping enable"); setFailure("master-enable-table", new TableNotDisabledException(this.tableName)); canTableBeEnabled = false; } } // We are done the check. Future actions in this procedure could be done asynchronously. ProcedurePrepareLatch.releaseLatch(syncLatch, this); return canTableBeEnabled; }
private boolean prepareDelete(final MasterProcedureEnv env) throws IOException { try { env.getMasterServices().checkTableModifiable(tableName); } catch (TableNotFoundException|TableNotDisabledException e) { setFailure("master-delete-table", e); return false; } return true; }
/** * Check conditions before any real action of modifying a table. * @param env MasterProcedureEnv * @throws IOException */ private void prepareModify(final MasterProcedureEnv env) throws IOException { // Checks whether the table exists if (!MetaTableAccessor.tableExists(env.getMasterServices().getConnection(), getTableName())) { throw new TableNotFoundException(getTableName()); } // In order to update the descriptor, we need to retrieve the old descriptor for comparison. this.unmodifiedHTableDescriptor = env.getMasterServices().getTableDescriptors().get(getTableName()); if (env.getMasterServices().getAssignmentManager().getTableStateManager() .isTableState(getTableName(), ZooKeeperProtos.Table.State.ENABLED)) { // We only execute this procedure with table online if online schema change config is set. if (!MasterDDLOperationHelper.isOnlineSchemaChangeAllowed(env)) { throw new TableNotDisabledException(getTableName()); } if (modifiedHTableDescriptor.getRegionReplication() != unmodifiedHTableDescriptor .getRegionReplication()) { throw new IOException("REGION_REPLICATION change is not supported for enabled tables"); } } // Find out whether all column families in unmodifiedHTableDescriptor also exists in // the modifiedHTableDescriptor. This is to determine whether we are safe to rollback. final Set<byte[]> oldFamilies = unmodifiedHTableDescriptor.getFamiliesKeys(); final Set<byte[]> newFamilies = modifiedHTableDescriptor.getFamiliesKeys(); for (byte[] familyName : oldFamilies) { if (!newFamilies.contains(familyName)) { this.deleteColumnFamilyInModify = true; break; } } }
/** * Scans the table and merges two adjacent regions if they are small. This * only happens when a lot of rows are deleted. * * When merging the hbase:meta region, the HBase instance must be offline. * When merging a normal table, the HBase instance must be online, but the * table must be disabled. * * @param conf - configuration object for HBase * @param fs - FileSystem where regions reside * @param tableName - Table to be compacted * @param testMasterRunning True if we are to verify master is down before * running merge * @throws IOException */ public static void merge(Configuration conf, FileSystem fs, final TableName tableName, final boolean testMasterRunning) throws IOException { boolean masterIsRunning = false; if (testMasterRunning) { masterIsRunning = HConnectionManager .execute(new HConnectable<Boolean>(conf) { @Override public Boolean connect(HConnection connection) throws IOException { return connection.isMasterRunning(); } }); } if (tableName.equals(TableName.META_TABLE_NAME)) { if (masterIsRunning) { throw new IllegalStateException( "Can not compact hbase:meta table if instance is on-line"); } // TODO reenable new OfflineMerger(conf, fs).process(); } else { if(!masterIsRunning) { throw new IllegalStateException( "HBase instance must be running to merge a normal table"); } Admin admin = new HBaseAdmin(conf); try { if (!admin.isTableDisabled(tableName)) { throw new TableNotDisabledException(tableName); } } finally { admin.close(); } new OnlineMerger(conf, fs, tableName).process(); } }
@Test (timeout=300000) public void testShouldFailOnlineSchemaUpdateIfOnlineSchemaIsNotEnabled() throws Exception { final TableName tableName = TableName.valueOf("changeTableSchemaOnlineFailure"); TEST_UTIL.getMiniHBaseCluster().getMaster().getConfiguration().setBoolean( "hbase.online.schema.update.enable", false); HTableDescriptor[] tables = admin.listTables(); int numTables = tables.length; TEST_UTIL.createTable(tableName, HConstants.CATALOG_FAMILY).close(); tables = this.admin.listTables(); assertEquals(numTables + 1, tables.length); // FIRST, do htabledescriptor changes. HTableDescriptor htd = this.admin.getTableDescriptor(tableName); // Make a copy and assert copy is good. HTableDescriptor copy = new HTableDescriptor(htd); assertTrue(htd.equals(copy)); // Now amend the copy. Introduce differences. long newFlushSize = htd.getMemStoreFlushSize() / 2; if (newFlushSize <=0) { newFlushSize = HTableDescriptor.DEFAULT_MEMSTORE_FLUSH_SIZE / 2; } copy.setMemStoreFlushSize(newFlushSize); final String key = "anyoldkey"; assertTrue(htd.getValue(key) == null); copy.setValue(key, key); boolean expectedException = false; try { admin.modifyTable(tableName, copy); } catch (TableNotDisabledException re) { expectedException = true; } assertTrue("Online schema update should not happen.", expectedException); // Reset the value for the other tests TEST_UTIL.getMiniHBaseCluster().getMaster().getConfiguration().setBoolean( "hbase.online.schema.update.enable", true); }
/** * Can't enable a table if the table isn't in disabled state * @throws IOException */ @Test (expected=TableNotDisabledException.class, timeout=300000) public void testTableNotDisabledExceptionWithATable() throws IOException { final TableName name = TableName.valueOf("testTableNotDisabledExceptionWithATable"); Table t = TEST_UTIL.createTable(name, HConstants.CATALOG_FAMILY); try { this.admin.enableTable(name); }finally { t.close(); } }
@Override public void postAddColumnHandler(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName, HColumnDescriptor column) throws IOException { Threads.sleep(6000); try { ctx.getEnvironment().getMasterServices().checkTableModifiable(tableName); } catch(TableNotDisabledException expected) { //pass return; } catch(IOException ex) { } fail("was expecting the table to be enabled"); }
@Test(timeout=60000, expected=TableNotDisabledException.class) public void testDeleteNotDisabledTable() throws Exception { final TableName tableName = TableName.valueOf("testDeleteNotDisabledTable"); final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); MasterProcedureTestingUtility.createTable(procExec, tableName, null, "f"); ProcedurePrepareLatch latch = new ProcedurePrepareLatch.CompatibilityLatch(); long procId = ProcedureTestingUtility.submitAndWait(procExec, new DeleteTableProcedure(procExec.getEnvironment(), tableName, latch)); latch.await(); }
@Test(timeout=60000, expected=TableNotDisabledException.class) public void testEnableNonDisabledTable() throws Exception { final TableName tableName = TableName.valueOf("testEnableNonExistingTable"); final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); MasterProcedureTestingUtility.createTable(procExec, tableName, null, "f1", "f2"); // Enable the table - expect failure long procId1 = procExec.submitProcedure( new EnableTableProcedure(procExec.getEnvironment(), tableName, false), nonceGroup, nonce); ProcedureTestingUtility.waitProcedure(procExec, procId1); ProcedureInfo result = procExec.getResult(procId1); assertTrue(result.isFailed()); LOG.debug("Enable failed with exception: " + result.getExceptionFullMessage()); assertTrue( ProcedureTestingUtility.getExceptionCause(result) instanceof TableNotDisabledException); // Enable the table with skipping table state check flag (simulate recovery scenario) long procId2 = procExec.submitProcedure( new EnableTableProcedure(procExec.getEnvironment(), tableName, true), nonceGroup + 1, nonce + 1); // Wait the completion ProcedureTestingUtility.waitProcedure(procExec, procId2); ProcedureTestingUtility.assertProcNotFailed(procExec, procId2); // Enable the table - expect failure from ProcedurePrepareLatch final ProcedurePrepareLatch prepareLatch = new ProcedurePrepareLatch.CompatibilityLatch(); long procId3 = procExec.submitProcedure( new EnableTableProcedure(procExec.getEnvironment(), tableName, false, prepareLatch), nonceGroup + 2, nonce + 2); prepareLatch.await(); Assert.fail("Enable should throw exception through latch."); }
@Override public void checkTableModifiable(final byte [] tableName) throws IOException { String tableNameStr = Bytes.toString(tableName); if (isCatalogTable(tableName)) { throw new IOException("Can't modify catalog tables"); } if (!MetaReader.tableExists(getCatalogTracker(), tableNameStr)) { throw new TableNotFoundException(tableNameStr); } if (!getAssignmentManager().getZKTable(). isDisabledTable(Bytes.toString(tableName))) { throw new TableNotDisabledException(tableName); } }
/** * Scans the table and merges two adjacent regions if they are small. This * only happens when a lot of rows are deleted. * * When merging the META region, the HBase instance must be offline. * When merging a normal table, the HBase instance must be online, but the * table must be disabled. * * @param conf - configuration object for HBase * @param fs - FileSystem where regions reside * @param tableName - Table to be compacted * @param testMasterRunning True if we are to verify master is down before * running merge * @throws IOException */ public static void merge(Configuration conf, FileSystem fs, final byte [] tableName, final boolean testMasterRunning) throws IOException { boolean masterIsRunning = false; if (testMasterRunning) { masterIsRunning = HConnectionManager .execute(new HConnectable<Boolean>(conf) { @Override public Boolean connect(HConnection connection) throws IOException { return connection.isMasterRunning(); } }); } if (Bytes.equals(tableName, HConstants.META_TABLE_NAME)) { if (masterIsRunning) { throw new IllegalStateException( "Can not compact META table if instance is on-line"); } new OfflineMerger(conf, fs).process(); } else { if(!masterIsRunning) { throw new IllegalStateException( "HBase instance must be running to merge a normal table"); } HBaseAdmin admin = new HBaseAdmin(conf); if (!admin.isTableDisabled(tableName)) { throw new TableNotDisabledException(tableName); } new OnlineMerger(conf, fs, tableName).process(); } }
@Test public void testShouldFailOnlineSchemaUpdateIfOnlineSchemaIsNotEnabled() throws Exception { final byte[] tableName = Bytes.toBytes("changeTableSchemaOnlineFailure"); TEST_UTIL.getMiniHBaseCluster().getMaster().getConfiguration().setBoolean( "hbase.online.schema.update.enable", false); HTableDescriptor[] tables = admin.listTables(); int numTables = tables.length; TEST_UTIL.createTable(tableName, HConstants.CATALOG_FAMILY).close(); tables = this.admin.listTables(); assertEquals(numTables + 1, tables.length); // FIRST, do htabledescriptor changes. HTableDescriptor htd = this.admin.getTableDescriptor(tableName); // Make a copy and assert copy is good. HTableDescriptor copy = new HTableDescriptor(htd); assertTrue(htd.equals(copy)); // Now amend the copy. Introduce differences. long newFlushSize = htd.getMemStoreFlushSize() / 2; if (newFlushSize <=0) { newFlushSize = HTableDescriptor.DEFAULT_MEMSTORE_FLUSH_SIZE / 2; } copy.setMemStoreFlushSize(newFlushSize); final String key = "anyoldkey"; assertTrue(htd.getValue(key) == null); copy.setValue(key, key); boolean expectedException = false; try { modifyTable(tableName, copy); } catch (TableNotDisabledException re) { expectedException = true; } assertTrue("Online schema update should not happen.", expectedException); TEST_UTIL.getMiniHBaseCluster().getMaster().getConfiguration().setBoolean( "hbase.online.schema.update.enable", true); }
/** * Can't enable a table if the table isn't in disabled state * @throws IOException */ @Test (expected=TableNotDisabledException.class) public void testTableNotDisabledExceptionWithATable() throws IOException { final byte [] name = Bytes.toBytes( "testTableNotDisabledExceptionWithATable"); HTable t = TEST_UTIL.createTable(name, HConstants.CATALOG_FAMILY); try { this.admin.enableTable(name); }finally { t.close(); } }
@Override public void clearTable(String tableString, long timestamp) throws IOException { TableName tableName = TableName.valueOf(tableString); if (!adm.tableExists(tableName)) { log.debug("Attempted to clear table {} before it exists (noop)", tableString); return; } if (!adm.isTableDisabled(tableName)) adm.disableTable(tableName); if (!adm.isTableDisabled(tableName)) throw new RuntimeException("Unable to disable table " + tableName); // This API call appears to both truncate and reenable the table. log.info("Truncating table {}", tableName); adm.truncateTable(tableName, true /* preserve splits */); try { adm.enableTable(tableName); } catch (TableNotDisabledException e) { // This triggers seemingly every time in testing with 1.0.2. log.debug("Table automatically reenabled by truncation: {}", tableName, e); } }