Index: src/main/java/org/apache/hadoop/hbase/avro/AvroServer.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/avro/AvroServer.java (revision 483a54d3d0a6c9a89205b0541303c1aabf512913) +++ src/main/java/org/apache/hadoop/hbase/avro/AvroServer.java (revision ) @@ -283,7 +283,7 @@ public Void modifyTable(ByteBuffer tableName, ATableDescriptor tableDescriptor) throws AIOError { try { admin.modifyTable(Bytes.toBytes(tableName), - AvroUtil.atdToHTD(tableDescriptor)); + AvroUtil.atdToHTD(tableDescriptor), false); return null; } catch (IOException e) { AIOError ioe = new AIOError(); @@ -353,7 +353,7 @@ public Void addFamily(ByteBuffer table, AFamilyDescriptor family) throws AIOError { try { admin.addColumn(Bytes.toBytes(table), - AvroUtil.afdToHCD(family)); + AvroUtil.afdToHCD(family), false); return null; } catch (IOException e) { AIOError ioe = new AIOError(); @@ -365,7 +365,7 @@ // NB: Asynchronous operation public Void deleteFamily(ByteBuffer table, ByteBuffer family) throws AIOError { try { - admin.deleteColumn(Bytes.toBytes(table), Bytes.toBytes(family)); + admin.deleteColumn(Bytes.toBytes(table), Bytes.toBytes(family), false); return null; } catch (IOException e) { AIOError ioe = new AIOError(); @@ -377,7 +377,7 @@ // NB: Asynchronous operation public Void modifyFamily(ByteBuffer table, ByteBuffer familyName, AFamilyDescriptor familyDescriptor) throws AIOError { try { - admin.modifyColumn(Bytes.toBytes(table), AvroUtil.afdToHCD(familyDescriptor)); + admin.modifyColumn(Bytes.toBytes(table), AvroUtil.afdToHCD(familyDescriptor), false); return null; } catch (IOException e) { AIOError ioe = new AIOError(); Index: src/main/java/org/apache/hadoop/hbase/ipc/HMasterInterface.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/ipc/HMasterInterface.java (revision 483a54d3d0a6c9a89205b0541303c1aabf512913) +++ src/main/java/org/apache/hadoop/hbase/ipc/HMasterInterface.java (revision ) @@ -89,8 +89,19 @@ * Adds a column to the specified table * @param tableName table to modify * @param column column descriptor + * @param instantAdd Set to True if the addColumn operation + * should happen instantly. * @throws IOException e */ + public void addColumn(final byte [] tableName, HColumnDescriptor column, + boolean instantAdd) throws IOException; + + /** + * Adds a column to the specified table + * @param tableName table to modify + * @param column column descriptor + * @throws IOException e + */ public void addColumn(final byte [] tableName, HColumnDescriptor column) throws IOException; @@ -98,8 +109,20 @@ * Modifies an existing column on the specified table * @param tableName table name * @param descriptor new column descriptor + * @param instantModify Set to True if the modifyColumn operation + * should happen instantly. * @throws IOException e */ + public void modifyColumn(final byte [] tableName, + HColumnDescriptor descriptor, + boolean instantModify) throws IOException; + + /** + * Modifies an existing column on the specified table + * @param tableName table name + * @param descriptor new column descriptor + * @throws IOException e + */ public void modifyColumn(final byte [] tableName, HColumnDescriptor descriptor) throws IOException; @@ -114,6 +137,17 @@ throws IOException; /** + * Deletes a column from the specified table. Table must be disabled. + * @param tableName table to alter + * @param columnName column family to remove + * @param instantDelete Set to True if the deleteColumn operation + * should happen instantly. + * @throws IOException e + */ + public void deleteColumn(final byte [] tableName, final byte [] columnName, + boolean instantDelete) throws IOException; + + /** * Puts the table on-line (only needed if table has been previously taken offline) * @param tableName table to enable * @throws IOException e @@ -133,8 +167,20 @@ * * @param tableName table to modify * @param htd new descriptor for table + * @param instantModify Set to true to if the modifyTable action should + * happen instantly. * @throws IOException e */ + public void modifyTable(byte[] tableName, HTableDescriptor htd, + boolean instantModify) throws IOException; + + /** + * Modify a table's metadata + * + * @param tableName table to modify + * @param htd new descriptor for table + * @throws IOException e + */ public void modifyTable(byte[] tableName, HTableDescriptor htd) throws IOException; @@ -223,13 +269,6 @@ public HTableDescriptor[] getHTableDescriptors(); /** - * Get current HTD for a given tablename - * @param tableName - * @return HTableDescriptor for the table - */ - //public HTableDescriptor getHTableDescriptor(final byte[] tableName); - - /** * Get array of HTDs for requested tables. * @param tableNames * @return array of HTableDescriptor Index: src/main/java/org/apache/hadoop/hbase/rest/SchemaResource.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/rest/SchemaResource.java (revision 483a54d3d0a6c9a89205b0541303c1aabf512913) +++ src/main/java/org/apache/hadoop/hbase/rest/SchemaResource.java (revision ) @@ -124,7 +124,7 @@ } if (admin.tableExists(name)) { admin.disableTable(name); - admin.modifyTable(name, htd); + admin.modifyTable(name, htd, false); admin.enableTable(name); } else try { admin.createTable(htd); @@ -154,9 +154,9 @@ hcd.setValue(e.getKey().getLocalPart(), e.getValue().toString()); } if (htd.hasFamily(hcd.getName())) { - admin.modifyColumn(name, hcd); + admin.modifyColumn(name, hcd, false); } else { - admin.addColumn(name, hcd); + admin.addColumn(name, hcd, false); } } } catch (IOException e) { Index: src/test/java/org/apache/hadoop/hbase/regionserver/handler/MockRegionServerServices.java =================================================================== --- src/test/java/org/apache/hadoop/hbase/regionserver/handler/MockRegionServerServices.java (revision 483a54d3d0a6c9a89205b0541303c1aabf512913) +++ src/test/java/org/apache/hadoop/hbase/regionserver/handler/MockRegionServerServices.java (revision ) @@ -54,6 +54,10 @@ return this.regions.get(encodedRegionName); } + public void refreshSchema(byte[] tableName) throws IOException { + // do nothing + } + @Override public void addToOnlineRegions(HRegion r) { this.regions.put(r.getRegionInfo().getEncodedName(), r); Index: src/test/java/org/apache/hadoop/hbase/master/TestCatalogJanitor.java =================================================================== --- src/test/java/org/apache/hadoop/hbase/master/TestCatalogJanitor.java (revision 483a54d3d0a6c9a89205b0541303c1aabf512913) +++ src/test/java/org/apache/hadoop/hbase/master/TestCatalogJanitor.java (revision ) @@ -46,12 +46,14 @@ import org.apache.hadoop.hbase.TableExistsException; import org.apache.hadoop.hbase.catalog.CatalogTracker; import org.apache.hadoop.hbase.client.Result; +import org.apache.hadoop.hbase.executor.EventHandler; import org.apache.hadoop.hbase.executor.ExecutorService; import org.apache.hadoop.hbase.io.Reference; import org.apache.hadoop.hbase.ipc.HRegionInterface; import org.apache.hadoop.hbase.regionserver.Store; import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.hbase.util.Writables; +import org.apache.hadoop.hbase.zookeeper.MasterSchemaChangeTracker; import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher; import org.junit.Test; import org.mockito.Mockito; @@ -128,11 +130,6 @@ } @Override - public void checkTableModifiable(byte[] tableName) throws IOException { - //no-op - } - - @Override public AssignmentManager getAssignmentManager() { return this.asm; } @@ -142,7 +139,17 @@ return null; } + public void checkTableModifiable(byte[] tableName) throws IOException { + + } + @Override + public void checkTableModifiable(byte[] tableName, + EventHandler.EventType eventType) + throws IOException { + } + + @Override public MasterFileSystem getMasterFileSystem() { return this.mfs; } @@ -223,7 +230,11 @@ } }; } + + public MasterSchemaChangeTracker getSchemaChangeTracker() { + return null; - } + } + } @Test public void testGetHRegionInfo() throws IOException { Index: src/main/java/org/apache/hadoop/hbase/zookeeper/ZooKeeperWatcher.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/zookeeper/ZooKeeperWatcher.java (revision 483a54d3d0a6c9a89205b0541303c1aabf512913) +++ src/main/java/org/apache/hadoop/hbase/zookeeper/ZooKeeperWatcher.java (revision ) @@ -85,6 +85,8 @@ public String clusterIdZNode; // znode used for log splitting work assignment public String splitLogZNode; + // znode used to record table schema changes + public String schemaZNode; private final Configuration conf; @@ -140,6 +142,7 @@ ZKUtil.createAndFailSilent(this, rsZNode); ZKUtil.createAndFailSilent(this, tableZNode); ZKUtil.createAndFailSilent(this, splitLogZNode); + ZKUtil.createAndFailSilent(this, schemaZNode); } catch (KeeperException e) { throw new ZooKeeperConnectionException( prefix("Unexpected KeeperException creating base node"), e); @@ -187,6 +190,9 @@ conf.get("zookeeper.znode.clusterId", "hbaseid")); splitLogZNode = ZKUtil.joinZNode(baseZNode, conf.get("zookeeper.znode.splitlog", HConstants.SPLIT_LOGDIR_NAME)); + schemaZNode = ZKUtil.joinZNode(baseZNode, + conf.get("zookeeper.znode.schema", "schema")); + } /** Index: src/main/java/org/apache/hadoop/hbase/master/HMaster.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/master/HMaster.java (revision 483a54d3d0a6c9a89205b0541303c1aabf512913) +++ src/main/java/org/apache/hadoop/hbase/master/HMaster.java (revision ) @@ -56,6 +56,7 @@ import org.apache.hadoop.hbase.client.MetaScanner; import org.apache.hadoop.hbase.client.Result; import org.apache.hadoop.hbase.client.MetaScanner.MetaScannerVisitor; +import org.apache.hadoop.hbase.executor.EventHandler; import org.apache.hadoop.hbase.executor.ExecutorService; import org.apache.hadoop.hbase.executor.ExecutorService.ExecutorType; import org.apache.hadoop.hbase.ipc.HBaseRPC; @@ -90,6 +91,7 @@ import org.apache.hadoop.hbase.util.VersionInfo; import org.apache.hadoop.hbase.zookeeper.ClusterId; import org.apache.hadoop.hbase.zookeeper.ClusterStatusTracker; +import org.apache.hadoop.hbase.zookeeper.MasterSchemaChangeTracker; import org.apache.hadoop.hbase.zookeeper.RegionServerTracker; import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher; import org.apache.hadoop.io.MapWritable; @@ -157,7 +159,10 @@ private CatalogTracker catalogTracker; // Cluster status zk tracker and local setter private ClusterStatusTracker clusterStatusTracker; - + + // Schema change tracker + private MasterSchemaChangeTracker schemaChangeTracker; + // buffer for "fatal error" notices from region servers // in the cluster. This is only used for assisting // operations/debugging. @@ -188,7 +193,9 @@ private final ServerName serverName; private TableDescriptors tableDescriptors; - + + private boolean supportInstantSchemaChanges = false; + /** * Initializes the HMaster. The steps are as follows: *

@@ -249,6 +256,9 @@ } this.zooKeeper = new ZooKeeperWatcher(conf, MASTER + ":" + isa.getPort(), this, true); this.metrics = new MasterMetrics(getServerName().toString()); + // initialize instant schema change settings + this.supportInstantSchemaChanges = conf.getBoolean( + "hbase.instant.schema.alter.enabled", false); } /** @@ -377,6 +387,11 @@ boolean wasUp = this.clusterStatusTracker.isClusterUp(); if (!wasUp) this.clusterStatusTracker.setClusterUp(); + // initialize schema change tracker + this.schemaChangeTracker = new MasterSchemaChangeTracker(getZooKeeper(), + this); + this.schemaChangeTracker.start(); + LOG.info("Server active/primary master; " + this.serverName + ", sessionid=0x" + Long.toHexString(this.zooKeeper.getRecoverableZooKeeper().getSessionId()) + @@ -605,6 +620,11 @@ return this.tableDescriptors; } + @Override + public MasterSchemaChangeTracker getSchemaChangeTracker() { + return this.schemaChangeTracker; + } + /** @return InfoServer object. Maybe null.*/ public InfoServer getInfoServer() { return this.infoServer; @@ -987,7 +1007,7 @@ if (cpHost != null) { cpHost.preDeleteTable(tableName); } - this.executorService.submit(new DeleteTableHandler(tableName, this, this)); + this.executorService.submit(new DeleteTableHandler(tableName, this, this, this)); if (cpHost != null) { cpHost.postDeleteTable(tableName); @@ -1008,12 +1028,22 @@ public void addColumn(byte [] tableName, HColumnDescriptor column) throws IOException { + addColumn(tableName, column, false); + } + + public void addColumn(byte [] tableName, HColumnDescriptor column, boolean insantAdd) + throws IOException { if (cpHost != null) { if (cpHost.preAddColumn(tableName, column)) { return; } } + if (insantAdd || supportInstantSchemaChanges) { + new TableAddFamilyHandler(tableName, column, this, this, + this).process(); + } else { - new TableAddFamilyHandler(tableName, column, this, this).process(); + new TableAddFamilyHandler(tableName, column, this, this).process(); + } if (cpHost != null) { cpHost.postAddColumn(tableName, column); } @@ -1021,12 +1051,25 @@ public void modifyColumn(byte [] tableName, HColumnDescriptor descriptor) throws IOException { + modifyColumn(tableName, descriptor, false); + } + + public void modifyColumn(byte [] tableName, HColumnDescriptor descriptor, + boolean instantModify) throws IOException { if (cpHost != null) { if (cpHost.preModifyColumn(tableName, descriptor)) { return; } } - new TableModifyFamilyHandler(tableName, descriptor, this, this).process(); + if (instantModify || supportInstantSchemaChanges) { + new TableModifyFamilyHandler(tableName, descriptor, this, this, + this).process(); + + } else { + new TableModifyFamilyHandler(tableName, descriptor, this, + this).process(); + } + if (cpHost != null) { cpHost.postModifyColumn(tableName, descriptor); } @@ -1034,12 +1077,22 @@ public void deleteColumn(final byte [] tableName, final byte [] c) throws IOException { + deleteColumn(tableName, c, false); + } + + public void deleteColumn(final byte [] tableName, final byte [] c, + boolean instantDelete) throws IOException { if (cpHost != null) { if (cpHost.preDeleteColumn(tableName, c)) { return; } } + if (instantDelete || supportInstantSchemaChanges) { + new TableDeleteFamilyHandler(tableName, c, this, this, + this).process(); + } else { - new TableDeleteFamilyHandler(tableName, c, this, this).process(); + new TableDeleteFamilyHandler(tableName, c, this, this).process(); + } if (cpHost != null) { cpHost.postDeleteColumn(tableName, c); } @@ -1107,12 +1160,22 @@ @Override public void modifyTable(final byte[] tableName, HTableDescriptor htd) throws IOException { + modifyTable(tableName, htd, false); + } + + @Override + public void modifyTable(final byte[] tableName, HTableDescriptor htd, + boolean instantModify) throws IOException { if (cpHost != null) { cpHost.preModifyTable(tableName, htd); } - + if (instantModify || supportInstantSchemaChanges) { - this.executorService.submit(new ModifyTableHandler(tableName, htd, this, + this.executorService.submit(new ModifyTableHandler(tableName, htd, this, + this, this)); + } else { + this.executorService.submit(new ModifyTableHandler(tableName, htd, this, - this)); + this)); + } if (cpHost != null) { cpHost.postModifyTable(tableName, htd); @@ -1120,8 +1183,20 @@ } @Override - public void checkTableModifiable(final byte [] tableName) + public void checkTableModifiable(final byte [] tableName, + EventHandler.EventType eventType) throws IOException { + preCheckTableModifiable(tableName); + if (!eventType.isSchemaChangeEvent()) { + if (!getAssignmentManager().getZKTable(). + isDisabledTable(Bytes.toString(tableName))) { + throw new TableNotDisabledException(tableName); + } + } + } + + private void preCheckTableModifiable(final byte[] tableName) + throws IOException { String tableNameStr = Bytes.toString(tableName); if (isCatalogTable(tableName)) { throw new IOException("Can't modify catalog tables"); @@ -1129,12 +1204,9 @@ if (!MetaReader.tableExists(getCatalogTracker(), tableNameStr)) { throw new TableNotFoundException(tableNameStr); } - if (!getAssignmentManager().getZKTable(). - isDisabledTable(Bytes.toString(tableName))) { - throw new TableNotDisabledException(tableName); - } + } - } + public void clearFromTransition(HRegionInfo hri) { if (this.assignmentManager.isRegionInTransition(hri) != null) { this.assignmentManager.clearRegionFromTransition(hri); Index: src/main/java/org/apache/hadoop/hbase/client/HBaseAdmin.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/client/HBaseAdmin.java (revision 483a54d3d0a6c9a89205b0541303c1aabf512913) +++ src/main/java/org/apache/hadoop/hbase/client/HBaseAdmin.java (revision ) @@ -890,10 +890,12 @@ * * @param tableName name of the table to add column to * @param column column descriptor of column to be added + * @param instantAdd set to True if the addColumn should happen instantly + * through ZK. By default, addColumn * @throws IOException if a remote or network exception occurs */ public void addColumn(final String tableName, HColumnDescriptor column) - throws IOException { + throws IOException { addColumn(Bytes.toBytes(tableName), column); } @@ -906,7 +908,7 @@ * @throws IOException if a remote or network exception occurs */ public void addColumn(final byte [] tableName, HColumnDescriptor column) - throws IOException { + throws IOException { HTableDescriptor.isLegalTableName(tableName); try { getMaster().addColumn(tableName, column); @@ -916,16 +918,50 @@ } /** + * Add a column to an existing table. + * Asynchronous operation. + * + * @param tableName name of the table to add column to + * @param column column descriptor of column to be added + * @param instantAdd set to True if the addColumn should happen instantly. + * @throws IOException if a remote or network exception occurs + */ + public void addColumn(final String tableName, HColumnDescriptor column, + boolean instantAdd) throws IOException { + addColumn(Bytes.toBytes(tableName), column, instantAdd); + } + + /** + * Add a column to an existing table. + * Asynchronous operation. + * + * @param tableName name of the table to add column to + * @param column column descriptor of column to be added + * @param instantAdd set to True if the addColumn should happen instantly. + * @throws IOException if a remote or network exception occurs + */ + public void addColumn(final byte [] tableName, HColumnDescriptor column, + boolean instantAdd) throws IOException { + HTableDescriptor.isLegalTableName(tableName); + try { + getMaster().addColumn(tableName, column, instantAdd); + } catch (RemoteException e) { + throw RemoteExceptionHandler.decodeRemoteException(e); + } + } + + /** * Delete a column from a table. * Asynchronous operation. * * @param tableName name of table * @param columnName name of column to be deleted + * @param instantDelete set to True if the deleteColumn should happen instantly. * @throws IOException if a remote or network exception occurs */ - public void deleteColumn(final String tableName, final String columnName) - throws IOException { - deleteColumn(Bytes.toBytes(tableName), Bytes.toBytes(columnName)); + public void deleteColumn(final String tableName, final String columnName, + boolean instantDelete) throws IOException { + deleteColumn(Bytes.toBytes(tableName), Bytes.toBytes(columnName), instantDelete); } /** @@ -937,7 +973,7 @@ * @throws IOException if a remote or network exception occurs */ public void deleteColumn(final byte [] tableName, final byte [] columnName) - throws IOException { + throws IOException { try { getMaster().deleteColumn(tableName, columnName); } catch (RemoteException e) { @@ -946,6 +982,37 @@ } /** + * Delete a column from a table. + * Asynchronous operation. + * + * @param tableName name of table + * @param columnName name of column to be deleted + * @throws IOException if a remote or network exception occurs + */ + public void deleteColumn(final String tableName, final String columnName) + throws IOException { + deleteColumn(Bytes.toBytes(tableName), Bytes.toBytes(columnName)); + } + + /** + * Delete a column from a table. + * Asynchronous operation. + * + * @param tableName name of table + * @param columnName name of column to be deleted + * @param instantDelete set to True if the deleteColumn should happen instantly. + * @throws IOException if a remote or network exception occurs + */ + public void deleteColumn(final byte [] tableName, final byte [] columnName, + boolean instantDelete) throws IOException { + try { + getMaster().deleteColumn(tableName, columnName, instantDelete); + } catch (RemoteException e) { + throw RemoteExceptionHandler.decodeRemoteException(e); + } + } + + /** * Modify an existing column family on a table. * Asynchronous operation. * @@ -1011,6 +1078,41 @@ } /** + * Modify an existing column family on a table. + * Asynchronous operation. + * + * @param tableName name of table + * @param descriptor new column descriptor to use + * @param instantModify Set to true if modifyColumn should happen instantly + * @throws IOException if a remote or network exception occurs + */ + public void modifyColumn(final String tableName, HColumnDescriptor descriptor, + boolean instantModify) throws IOException { + modifyColumn(Bytes.toBytes(tableName), descriptor); + } + + /** + * Modify an existing column family on a table. + * Asynchronous operation. + * + * @param tableName name of table + * @param descriptor new column descriptor to use + * @param instantModify Set to true if modifyColumn should happen instantly + * @throws IOException if a remote or network exception occurs + */ + public void modifyColumn(final byte [] tableName, HColumnDescriptor descriptor, + boolean instantModify) throws IOException { + try { + getMaster().modifyColumn(tableName, descriptor, instantModify); + } catch (RemoteException re) { + // Convert RE exceptions in here; client shouldn't have to deal with them, + // at least w/ the type of exceptions that come out of this method: + // TableNotFoundException, etc. + throw RemoteExceptionHandler.decodeRemoteException(re); + } + } + + /** * Close a region. For expert-admins. Runs close on the regionserver. The * master will not be informed of the close. * @param regionname region name to close @@ -1444,10 +1546,10 @@ * @param htd modified description of the table * @throws IOException if a remote or network exception occurs */ - public void modifyTable(final byte [] tableName, HTableDescriptor htd) + public void modifyTable(final byte [] tableName, HTableDescriptor htd, boolean instantModify) throws IOException { try { - getMaster().modifyTable(tableName, htd); + getMaster().modifyTable(tableName, htd, instantModify); } catch (RemoteException re) { // Convert RE exceptions in here; client shouldn't have to deal with them, // at least w/ the type of exceptions that come out of this method: Index: src/test/java/org/apache/hadoop/hbase/coprocessor/TestMasterObserver.java =================================================================== --- src/test/java/org/apache/hadoop/hbase/coprocessor/TestMasterObserver.java (revision 483a54d3d0a6c9a89205b0541303c1aabf512913) +++ src/test/java/org/apache/hadoop/hbase/coprocessor/TestMasterObserver.java (revision ) @@ -541,20 +541,20 @@ // modify table htd.setMaxFileSize(512 * 1024 * 1024); - admin.modifyTable(TEST_TABLE, htd); + admin.modifyTable(TEST_TABLE, htd, false); // preModifyTable can't bypass default action. assertTrue("Test table should have been modified", cp.wasModifyTableCalled()); // add a column family - admin.addColumn(TEST_TABLE, new HColumnDescriptor(TEST_FAMILY2)); + admin.addColumn(TEST_TABLE, new HColumnDescriptor(TEST_FAMILY2), false); assertTrue("New column family shouldn't have been added to test table", cp.preAddColumnCalledOnly()); // modify a column family HColumnDescriptor hcd1 = new HColumnDescriptor(TEST_FAMILY2); hcd1.setMaxVersions(25); - admin.modifyColumn(TEST_TABLE, hcd1); + admin.modifyColumn(TEST_TABLE, hcd1, false); assertTrue("Second column family should be modified", cp.preModifyColumnCalledOnly()); @@ -584,19 +584,19 @@ // modify table htd.setMaxFileSize(512 * 1024 * 1024); - admin.modifyTable(TEST_TABLE, htd); + admin.modifyTable(TEST_TABLE, htd, false); assertTrue("Test table should have been modified", cp.wasModifyTableCalled()); // add a column family - admin.addColumn(TEST_TABLE, new HColumnDescriptor(TEST_FAMILY2)); + admin.addColumn(TEST_TABLE, new HColumnDescriptor(TEST_FAMILY2), false); assertTrue("New column family should have been added to test table", cp.wasAddColumnCalled()); // modify a column family HColumnDescriptor hcd = new HColumnDescriptor(TEST_FAMILY2); hcd.setMaxVersions(25); - admin.modifyColumn(TEST_TABLE, hcd); + admin.modifyColumn(TEST_TABLE, hcd, false); assertTrue("Second column family should be modified", cp.wasModifyColumnCalled()); @@ -613,7 +613,7 @@ // delete column assertFalse("No column family deleted yet", cp.wasDeleteColumnCalled()); - admin.deleteColumn(TEST_TABLE, TEST_FAMILY2); + admin.deleteColumn(TEST_TABLE, TEST_FAMILY2, false); HTableDescriptor tableDesc = admin.getTableDescriptor(TEST_TABLE); assertNull("'"+Bytes.toString(TEST_FAMILY2)+"' should have been removed", tableDesc.getFamily(TEST_FAMILY2)); Index: src/main/java/org/apache/hadoop/hbase/executor/EventHandler.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/executor/EventHandler.java (revision 483a54d3d0a6c9a89205b0541303c1aabf512913) +++ src/main/java/org/apache/hadoop/hbase/executor/EventHandler.java (revision ) @@ -140,14 +140,14 @@ * Constructor */ EventType(int value) {} - public boolean isOnlineSchemaChangeSupported() { + public boolean isSchemaChangeEvent() { return ( - this.equals(EventType.C_M_ADD_FAMILY) || - this.equals(EventType.C_M_DELETE_FAMILY) || - this.equals(EventType.C_M_MODIFY_FAMILY) || + this.equals(EventType.C_M_ADD_FAMILY) || + this.equals(EventType.C_M_DELETE_FAMILY) || + this.equals(EventType.C_M_MODIFY_FAMILY) || - this.equals(EventType.C_M_MODIFY_TABLE) - ); + this.equals(EventType.C_M_MODIFY_TABLE)); } + } /** Index: src/main/java/org/apache/hadoop/hbase/regionserver/OnlineRegions.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/regionserver/OnlineRegions.java (revision 483a54d3d0a6c9a89205b0541303c1aabf512913) +++ src/main/java/org/apache/hadoop/hbase/regionserver/OnlineRegions.java (revision ) @@ -21,6 +21,8 @@ import org.apache.hadoop.hbase.Server; +import java.io.IOException; + /** * Interface to Map of online regions. In the Map, the key is the region's * encoded name and the value is an {@link HRegion} instance. @@ -49,4 +51,11 @@ * null if named region is not member of the online regions. */ public HRegion getFromOnlineRegions(String encodedRegionName); + + /** + * Refresh schema for online regions of a table. + * @param tableName + * @return + */ + public void refreshSchema(byte[] tableName) throws IOException; } \ No newline at end of file Index: src/main/java/org/apache/hadoop/hbase/master/handler/TableEventHandler.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/master/handler/TableEventHandler.java (revision 483a54d3d0a6c9a89205b0541303c1aabf512913) +++ src/main/java/org/apache/hadoop/hbase/master/handler/TableEventHandler.java (revision ) @@ -36,8 +36,10 @@ import org.apache.hadoop.hbase.executor.EventHandler; import org.apache.hadoop.hbase.master.BulkReOpen; import org.apache.hadoop.hbase.master.MasterServices; +import org.apache.hadoop.hbase.ipc.HMasterInterface; import org.apache.hadoop.hbase.util.Bytes; import org.apache.zookeeper.KeeperException; +import org.apache.hadoop.hbase.zookeeper.MasterSchemaChangeTracker; import com.google.common.collect.Lists; import com.google.common.collect.Maps; @@ -52,8 +54,10 @@ public abstract class TableEventHandler extends EventHandler { private static final Log LOG = LogFactory.getLog(TableEventHandler.class); protected final MasterServices masterServices; + protected HMasterInterface master = null; protected final byte [] tableName; protected final String tableNameStr; + protected boolean instantAction = false; public TableEventHandler(EventType eventType, byte [] tableName, Server server, MasterServices masterServices) @@ -61,19 +65,18 @@ super(server, eventType); this.masterServices = masterServices; this.tableName = tableName; - try { - this.masterServices.checkTableModifiable(tableName); - } catch (TableNotDisabledException ex) { - if (eventType.isOnlineSchemaChangeSupported()) { - LOG.debug("Ignoring table not disabled exception " + - "for supporting online schema changes."); - } else { - throw ex; - } - } + this.masterServices.checkTableModifiable(tableName, eventType); this.tableNameStr = Bytes.toString(this.tableName); } + public TableEventHandler(EventType eventType, byte [] tableName, Server server, + MasterServices masterServices, HMasterInterface masterInterface) + throws IOException { + this(eventType, tableName, server, masterServices); + this.instantAction = true; + this.master = masterInterface; + } + @Override public void process() { try { @@ -83,23 +86,37 @@ MetaReader.getTableRegions(this.server.getCatalogTracker(), tableName); handleTableOperation(hris); - if (eventType.isOnlineSchemaChangeSupported() && this.masterServices. + handleSchemaChanges(hris); + } catch (IOException e) { + LOG.error("Error manipulating table " + Bytes.toString(tableName), e); + } catch (KeeperException e) { + LOG.error("Error manipulating table " + Bytes.toString(tableName), e); + } + } + + private void handleSchemaChanges(List regions) + throws IOException { + if (instantAction) { + handleInstantSchemaChanges(); + } else { + handleRegularSchemaChanges(regions); + } + } + + private void handleRegularSchemaChanges(List regions) + throws IOException { + if (eventType.isSchemaChangeEvent() && this.masterServices. - getAssignmentManager().getZKTable(). - isEnabledTable(Bytes.toString(tableName))) { + getAssignmentManager().getZKTable(). + isEnabledTable(Bytes.toString(tableName))) { - this.masterServices.getAssignmentManager().setRegionsToReopen(hris); - if (reOpenAllRegions(hris)) { + this.masterServices.getAssignmentManager().setRegionsToReopen(regions); + if (reOpenAllRegions(regions)) { - LOG.info("Completed table operation " + eventType + " on table " + - Bytes.toString(tableName)); - } else { - LOG.warn("Error on reopening the regions"); - } - } + LOG.info("Completed table operation " + eventType + " on table " + + Bytes.toString(tableName)); + } else { + LOG.warn("Error on reopening the regions"); + } + } - } catch (IOException e) { - LOG.error("Error manipulating table " + Bytes.toString(tableName), e); - } catch (KeeperException e) { - LOG.error("Error manipulating table " + Bytes.toString(tableName), e); - } + } - } public boolean reOpenAllRegions(List regions) throws IOException { boolean done = false; @@ -107,7 +124,8 @@ HTable table = new HTable(masterServices.getConfiguration(), tableName); TreeMap> serverToRegions = Maps .newTreeMap(); - NavigableMap hriHserverMapping = table.getRegionLocations(); + NavigableMap hriHserverMapping + = table.getRegionLocations(); for (HRegionInfo hri : regions) { ServerName rsLocation = hriHserverMapping.get(hri); @@ -138,6 +156,32 @@ } return done; } + + protected void handleInstantSchemaChanges() { + if (eventType.isSchemaChangeEvent()) { + try { + MasterSchemaChangeTracker masterSchemaChangeTracker = + this.masterServices.getSchemaChangeTracker(); + // Turn the load balancer off if necessary. + boolean currentBalanceSwitch = master.balanceSwitch(false); + masterSchemaChangeTracker + .createSchemaChangeNode(Bytes.toString(tableName)); + while(true) { + if (!masterSchemaChangeTracker.isSchemaChangeNodeExists( + Bytes.toString(tableName))) { + break; + } + } + if (currentBalanceSwitch) { + LOG.info("Schema change operation completed. Enabling load balancer now."); + this.master.balanceSwitch(true); + } + } catch (KeeperException e) { + LOG.warn("Instant schema change failed for table " + tableName, e); + } + } + } + protected abstract void handleTableOperation(List regions) throws IOException, KeeperException; } Index: src/main/java/org/apache/hadoop/hbase/master/MasterServices.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/master/MasterServices.java (revision 483a54d3d0a6c9a89205b0541303c1aabf512913) +++ src/main/java/org/apache/hadoop/hbase/master/MasterServices.java (revision ) @@ -26,7 +26,10 @@ import org.apache.hadoop.hbase.TableNotDisabledException; import org.apache.hadoop.hbase.TableNotFoundException; import org.apache.hadoop.hbase.executor.ExecutorService; +import org.apache.hadoop.hbase.executor.EventHandler; +import org.apache.hadoop.hbase.zookeeper.MasterSchemaChangeTracker; + /** * Services Master supplies */ @@ -52,15 +55,26 @@ public ExecutorService getExecutorService(); /** - * Check table is modifiable; i.e. exists and is offline. - * @param tableName Name of table to check. - * @throws TableNotDisabledException - * @throws TableNotFoundException + * Check table modifiable. i.e not ROOT or META and offlined for all commands except + * alter commands + * @param tableName + * @param eventType + * @throws IOException */ - public void checkTableModifiable(final byte [] tableName) throws IOException; + public void checkTableModifiable(final byte [] tableName, + EventHandler.EventType eventType) + throws IOException; + /** * @return Return table descriptors implementation. */ public TableDescriptors getTableDescriptors(); + + /** + * Get Master Schema change tracker + * @return + */ + public MasterSchemaChangeTracker getSchemaChangeTracker(); + } Index: src/main/java/org/apache/hadoop/hbase/master/handler/ModifyTableHandler.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/master/handler/ModifyTableHandler.java (revision 483a54d3d0a6c9a89205b0541303c1aabf512913) +++ src/main/java/org/apache/hadoop/hbase/master/handler/ModifyTableHandler.java (revision ) @@ -25,6 +25,7 @@ import org.apache.hadoop.hbase.HRegionInfo; import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.Server; +import org.apache.hadoop.hbase.ipc.HMasterInterface; import org.apache.hadoop.hbase.master.MasterServices; import org.apache.hadoop.hbase.util.Bytes; @@ -36,8 +37,23 @@ final MasterServices masterServices) throws IOException { super(EventType.C_M_MODIFY_TABLE, tableName, server, masterServices); this.htd = htd; + validateTable(tableName, htd); + } + + public ModifyTableHandler(final byte [] tableName, + final HTableDescriptor htd, final Server server, + final MasterServices masterServices, + final HMasterInterface masterInterface) throws IOException { + super(EventType.C_M_MODIFY_TABLE, tableName, server, masterServices, + masterInterface); + this.htd = htd; + validateTable(tableName, htd); + } + + private void validateTable(final byte[] tableName, HTableDescriptor htd) + throws IOException { if (!Bytes.equals(tableName, htd.getName())) { - throw new IOException("TableDescriptor name & tableName must match: " + throw new IOException("TableDescriptor name & tableName must match: " + htd.getNameAsString() + " vs " + Bytes.toString(tableName)); } } Index: src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java (revision 483a54d3d0a6c9a89205b0541303c1aabf512913) +++ src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java (revision ) @@ -135,6 +135,7 @@ import org.apache.hadoop.hbase.util.Threads; import org.apache.hadoop.hbase.util.VersionInfo; import org.apache.hadoop.hbase.zookeeper.ClusterStatusTracker; +import org.apache.hadoop.hbase.zookeeper.SchemaChangeTracker; import org.apache.hadoop.hbase.zookeeper.ZKUtil; import org.apache.hadoop.hbase.zookeeper.ZooKeeperNodeTracker; import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher; @@ -269,6 +270,9 @@ // Cluster Status Tracker private ClusterStatusTracker clusterStatusTracker; + // Schema change Tracker + private SchemaChangeTracker schemaChangeTracker; + // Log Splitting Worker private SplitLogWorker splitLogWorker; @@ -543,6 +547,11 @@ this.catalogTracker = new CatalogTracker(this.zooKeeper, this.conf, this, this.conf.getInt("hbase.regionserver.catalog.timeout", Integer.MAX_VALUE)); catalogTracker.start(); + + // Schema change tracker + this.schemaChangeTracker = new SchemaChangeTracker(this.zooKeeper, + this, this); + this.schemaChangeTracker.start(); } /** @@ -3114,4 +3123,82 @@ HLog wal = this.getWAL(); return wal.rollWriter(true); } + + /** + * Refresh schema changes for given regions. + * @param onlineRegionsOfTable + * @throws IOException + */ + private void reopenRegions(List onlineRegionsOfTable) throws IOException { + + if (onlineRegionsOfTable != null && onlineRegionsOfTable.size() > 0) { + for (HRegion hRegion : onlineRegionsOfTable) { + HRegionInfo regionInfo = hRegion.getRegionInfo(); + // Close the region + hRegion.close(); + // Remove from online regions + removeFromOnlineRegions(regionInfo.getEncodedName()); + // Get new HTD + HTableDescriptor htd = this.tableDescriptors.get(regionInfo.getTableName()); + LOG.info("HTD for region = " + regionInfo.getRegionNameAsString() + + " Is = " + htd ); + HRegion region = + HRegion.openHRegion(hRegion.getRegionInfo(), htd, hlog, conf); + // Add new region to the onlineRegions + addToOnlineRegions(region); -} + } + } + } + + /** + * Gets the online regions of the specified table. + * This method looks at the in-memory onlineRegions. It does not go to .META.. + * Only returns online regions. If a region on this table has been + * closed during a disable, etc., it will not be included in the returned list. + * So, the returned list may not necessarily be ALL regions in this table, its + * all the ONLINE regions in the table. + * @param tableName + * @return Online regions from tableName + */ + private List getOnlineRegionsForTable(byte[] tableName) { + List tableRegions = new ArrayList(); + synchronized (this.onlineRegions) { + for (HRegion region: this.onlineRegions.values()) { + HRegionInfo regionInfo = region.getRegionInfo(); + if(Bytes.equals(regionInfo.getTableName(), tableName)) { + tableRegions.add(region); + } + } + } + return tableRegions; + + } + + /** + * Refresh schema changes to all online regions of given table. + * @param tableName + * @return true on successful schema refresh. + */ + public void refreshSchema(byte[] tableName) throws IOException { + List onlineRegionsForTable = null; + try { + onlineRegionsForTable = getOnlineRegionsForTable(tableName); + if (onlineRegionsForTable != null && onlineRegionsForTable.size() > 0) { + LOG.info("refreshSchema found " + onlineRegionsForTable + + " online regions for table = " + Bytes.toString(tableName) + + " Refreshing them now.."); + reopenRegions(onlineRegionsForTable); + } else { + LOG.info("refreshSchema found no onlineRegions for table = " + + Bytes.toString(tableName) + ". Skipping refreshSchema..."); + } + } catch (IOException ioe) { + LOG.info("refreshSchema failed with exception " + + " for table = " + Bytes.toString(tableName) + + " Number of regions online = " + + onlineRegionsForTable == null ? 0 : onlineRegionsForTable.size()); + throw ioe; + } + } + +} Index: src/main/java/org/apache/hadoop/hbase/master/handler/DeleteTableHandler.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/master/handler/DeleteTableHandler.java (revision 483a54d3d0a6c9a89205b0541303c1aabf512913) +++ src/main/java/org/apache/hadoop/hbase/master/handler/DeleteTableHandler.java (revision ) @@ -27,6 +27,7 @@ import org.apache.hadoop.hbase.HRegionInfo; import org.apache.hadoop.hbase.Server; import org.apache.hadoop.hbase.catalog.MetaEditor; +import org.apache.hadoop.hbase.ipc.HMasterInterface; import org.apache.hadoop.hbase.master.AssignmentManager; import org.apache.hadoop.hbase.master.MasterServices; import org.apache.hadoop.hbase.util.Bytes; @@ -37,9 +38,10 @@ private static final Log LOG = LogFactory.getLog(DeleteTableHandler.class); public DeleteTableHandler(byte [] tableName, Server server, - final MasterServices masterServices) + final MasterServices masterServices, HMasterInterface masterInterface) throws IOException { - super(EventType.C_M_DELETE_TABLE, tableName, server, masterServices); + super(EventType.C_M_DELETE_TABLE, tableName, server, masterServices, + masterInterface); } @Override Index: src/main/ruby/hbase/admin.rb =================================================================== --- src/main/ruby/hbase/admin.rb (revision 483a54d3d0a6c9a89205b0541303c1aabf512913) +++ src/main/ruby/hbase/admin.rb (revision ) @@ -327,13 +327,13 @@ # If column already exist, then try to alter it. Create otherwise. if htd.hasFamily(column_name.to_java_bytes) - @admin.modifyColumn(table_name, column_name, descriptor) + @admin.modifyColumn(table_name, column_name, descriptor, false) if wait == true puts "Updating all regions with the new schema..." alter_status(table_name) end else - @admin.addColumn(table_name, descriptor) + @admin.addColumn(table_name, descriptor, false) if wait == true puts "Updating all regions with the new schema..." alter_status(table_name) @@ -345,7 +345,7 @@ # Delete column family if method == "delete" raise(ArgumentError, "NAME parameter missing for delete method") unless arg[NAME] - @admin.deleteColumn(table_name, arg[NAME]) + @admin.deleteColumn(table_name, arg[NAME], false) if wait == true puts "Updating all regions with the new schema..." alter_status(table_name) @@ -359,7 +359,7 @@ htd.setReadOnly(JBoolean.valueOf(arg[READONLY])) if arg[READONLY] htd.setMemStoreFlushSize(JLong.valueOf(arg[MEMSTORE_FLUSHSIZE])) if arg[MEMSTORE_FLUSHSIZE] htd.setDeferredLogFlush(JBoolean.valueOf(arg[DEFERRED_LOG_FLUSH])) if arg[DEFERRED_LOG_FLUSH] - @admin.modifyTable(table_name.to_java_bytes, htd) + @admin.modifyTable(table_name.to_java_bytes, htd, false) if wait == true puts "Updating all regions with the new schema..." alter_status(table_name) @@ -372,6 +372,68 @@ end end + #---------------------------------------------------------------------------------------------- + # Change table structure or table options + def alter_instant(table_name, wait = true, *args) + # Table name should be a string + raise(ArgumentError, "Table name must be of type String") unless table_name.kind_of?(String) + + # Table should exist + raise(ArgumentError, "Can't find a table: #{table_name}") unless exists?(table_name) + + # There should be at least one argument + raise(ArgumentError, "There should be at least one argument but the table name") if args.empty? + + # Get table descriptor + htd = @admin.getTableDescriptor(table_name.to_java_bytes) + + # Process all args + args.each do |arg| + # Normalize args to support column name only alter specs + arg = { NAME => arg } if arg.kind_of?(String) + + # Normalize args to support shortcut delete syntax + arg = { METHOD => 'delete', NAME => arg['delete'] } if arg['delete'] + + # No method parameter, try to use the args as a column definition + unless method = arg.delete(METHOD) + descriptor = hcd(arg, htd) + if arg[COMPRESSION_COMPACT] + descriptor.setValue(COMPRESSION_COMPACT, arg[COMPRESSION_COMPACT]) + end + column_name = descriptor.getNameAsString + + # If column already exist, then try to alter it. Create otherwise. + if htd.hasFamily(column_name.to_java_bytes) + @admin.modifyColumn(table_name, column_name, descriptor, true) + else + @admin.addColumn(table_name, descriptor, true) + end + next + end + + # Delete column family + if method == "delete" + raise(ArgumentError, "NAME parameter missing for delete method") unless arg[NAME] + @admin.deleteColumn(table_name, arg[NAME], true) + next + end + + # Change table attributes + if method == "table_att" + htd.setMaxFileSize(JLong.valueOf(arg[MAX_FILESIZE])) if arg[MAX_FILESIZE] + htd.setReadOnly(JBoolean.valueOf(arg[READONLY])) if arg[READONLY] + htd.setMemStoreFlushSize(JLong.valueOf(arg[MEMSTORE_FLUSHSIZE])) if arg[MEMSTORE_FLUSHSIZE] + htd.setDeferredLogFlush(JBoolean.valueOf(arg[DEFERRED_LOG_FLUSH])) if arg[DEFERRED_LOG_FLUSH] + @admin.modifyTable(table_name.to_java_bytes, htd, true) + next + end + + # Unknown method + raise ArgumentError, "Unknown method: #{method}" + end + end + def status(format) status = @admin.getClusterStatus() if format == "detailed" Index: src/main/ruby/shell.rb =================================================================== --- src/main/ruby/shell.rb (revision 483a54d3d0a6c9a89205b0541303c1aabf512913) +++ src/main/ruby/shell.rb (revision ) @@ -230,6 +230,7 @@ show_filters alter_status alter_async + alter_instant ] ) Index: src/test/java/org/apache/hadoop/hbase/client/TestFromClientSide.java =================================================================== --- src/test/java/org/apache/hadoop/hbase/client/TestFromClientSide.java (revision 483a54d3d0a6c9a89205b0541303c1aabf512913) +++ src/test/java/org/apache/hadoop/hbase/client/TestFromClientSide.java (revision ) @@ -3819,7 +3819,7 @@ for (HColumnDescriptor c : desc.getFamilies()) c.setValue(attrName, attrValue); // update metadata for all regions of this table - admin.modifyTable(tableAname, desc); + admin.modifyTable(tableAname, desc, false); // enable the table admin.enableTable(tableAname); Index: src/test/java/org/apache/hadoop/hbase/client/TestAdmin.java =================================================================== --- src/test/java/org/apache/hadoop/hbase/client/TestAdmin.java (revision 483a54d3d0a6c9a89205b0541303c1aabf512913) +++ src/test/java/org/apache/hadoop/hbase/client/TestAdmin.java (revision ) @@ -271,7 +271,7 @@ final byte [] hcdName = hcd.getName(); expectedException = false; try { - this.admin.modifyColumn(tableName, hcd); + this.admin.modifyColumn(tableName, hcd, false); } catch (TableNotDisabledException re) { expectedException = true; } @@ -288,7 +288,7 @@ xtracol.setValue(xtracolName, xtracolName); expectedException = false; try { - this.admin.addColumn(tableName, xtracol); + this.admin.addColumn(tableName, xtracol, false); } catch (TableNotDisabledException re) { expectedException = true; } @@ -300,7 +300,7 @@ assertTrue(hcd.getValue(xtracolName).equals(xtracolName)); // Delete the just-added column. - this.admin.deleteColumn(tableName, xtracol.getName()); + this.admin.deleteColumn(tableName, xtracol.getName(), false); modifiedHtd = this.admin.getTableDescriptor(tableName); hcd = modifiedHtd.getFamily(xtracol.getName()); assertTrue(hcd == null); @@ -324,7 +324,7 @@ ExecutorService executor = services.getExecutorService(); AtomicBoolean done = new AtomicBoolean(false); executor.registerListener(EventType.C_M_MODIFY_TABLE, new DoneListener(done)); - this.admin.modifyTable(tableName, htd); + this.admin.modifyTable(tableName, htd, false); while (!done.get()) { synchronized (done) { try { @@ -753,15 +753,15 @@ } catch (org.apache.hadoop.hbase.client.RegionOfflineException e) { // Expected } - this.admin.addColumn(tableName, new HColumnDescriptor("col2")); + this.admin.addColumn(tableName, new HColumnDescriptor("col2"), false); this.admin.enableTable(tableName); try { - this.admin.deleteColumn(tableName, Bytes.toBytes("col2")); + this.admin.deleteColumn(tableName, Bytes.toBytes("col2"), false); } catch(TableNotDisabledException e) { // Expected } this.admin.disableTable(tableName); - this.admin.deleteColumn(tableName, Bytes.toBytes("col2")); + this.admin.deleteColumn(tableName, Bytes.toBytes("col2"), false); this.admin.deleteTable(tableName); } Index: src/main/java/org/apache/hadoop/hbase/master/handler/TableAddFamilyHandler.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/master/handler/TableAddFamilyHandler.java (revision 483a54d3d0a6c9a89205b0541303c1aabf512913) +++ src/main/java/org/apache/hadoop/hbase/master/handler/TableAddFamilyHandler.java (revision ) @@ -27,6 +27,7 @@ import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.InvalidFamilyOperationException; import org.apache.hadoop.hbase.Server; +import org.apache.hadoop.hbase.ipc.HMasterInterface; import org.apache.hadoop.hbase.master.MasterServices; import org.apache.hadoop.hbase.util.Bytes; @@ -43,6 +44,13 @@ this.familyDesc = familyDesc; } + public TableAddFamilyHandler(byte[] tableName, HColumnDescriptor familyDesc, + Server server, final MasterServices masterServices, + HMasterInterface masterInterface) throws IOException { + super(EventType.C_M_ADD_FAMILY, tableName, server, masterServices, masterInterface); + this.familyDesc = familyDesc; + } + @Override protected void handleTableOperation(List hris) throws IOException { Index: src/main/resources/hbase-default.xml =================================================================== --- src/main/resources/hbase-default.xml (revision 483a54d3d0a6c9a89205b0541303c1aabf512913) +++ src/main/resources/hbase-default.xml (revision ) @@ -513,6 +513,15 @@ used for client / server RPC call marshalling. + + hbase.instant.schema.alter.enabled + false + Whether or not to handle alter schema changes instantly or not. + If enabled, all schema change alter operations will be instant, as the master will not + explicitly unassign/assign the impacted regions and instead will rely on Region servers to + refresh their schema changes. + +