/** * Reset the split parent region info in meta table */ private void resetSplitParent(HbckInfo hi) throws IOException { RowMutations mutations = new RowMutations(hi.metaEntry.getRegionName()); Delete d = new Delete(hi.metaEntry.getRegionName()); d.deleteColumn(HConstants.CATALOG_FAMILY, HConstants.SPLITA_QUALIFIER); d.deleteColumn(HConstants.CATALOG_FAMILY, HConstants.SPLITB_QUALIFIER); mutations.add(d); HRegionInfo hri = new HRegionInfo(hi.metaEntry); hri.setOffline(false); hri.setSplit(false); Put p = MetaTableAccessor.makePutFromRegionInfo(hri); mutations.add(p); meta.mutateRow(mutations); LOG.info("Reset split parent " + hi.metaEntry.getRegionNameAsString() + " in META" ); }
/********************************* Master related hooks **********************************/ @Override public void postStartMaster(ObserverContext<MasterCoprocessorEnvironment> ctx) throws IOException { // Need to create the new system table for labels here MasterServices master = ctx.getEnvironment().getMasterServices(); if (!MetaTableAccessor.tableExists(master.getConnection(), LABELS_TABLE_NAME)) { HTableDescriptor labelsTable = new HTableDescriptor(LABELS_TABLE_NAME); HColumnDescriptor labelsColumn = new HColumnDescriptor(LABELS_TABLE_FAMILY); labelsColumn.setBloomFilterType(BloomType.NONE); labelsColumn.setBlockCacheEnabled(false); // We will cache all the labels. No need of normal // table block cache. labelsTable.addFamily(labelsColumn); // Let the "labels" table having only one region always. We are not expecting too many labels in // the system. labelsTable.setValue(HTableDescriptor.SPLIT_POLICY, DisabledRegionSplitPolicy.class.getName()); labelsTable.setValue(Bytes.toBytes(HConstants.DISALLOW_WRITES_IN_RECOVERING), Bytes.toBytes(true)); master.createTable(labelsTable, null, HConstants.NO_NONCE, HConstants.NO_NONCE); } }
@Override protected void updateMeta(final byte [] oldRegion1, final byte [] oldRegion2, HRegion newRegion) throws IOException { byte[][] regionsToDelete = {oldRegion1, oldRegion2}; for (int r = 0; r < regionsToDelete.length; r++) { if(Bytes.equals(regionsToDelete[r], latestRegion.getRegionName())) { latestRegion = null; } Delete delete = new Delete(regionsToDelete[r]); table.delete(delete); if(LOG.isDebugEnabled()) { LOG.debug("updated columns in row: " + Bytes.toStringBinary(regionsToDelete[r])); } } newRegion.getRegionInfo().setOffline(true); MetaTableAccessor.addRegionToMeta(table, newRegion.getRegionInfo()); if(LOG.isDebugEnabled()) { LOG.debug("updated columns in row: " + Bytes.toStringBinary(newRegion.getRegionInfo().getRegionName())); } }
/** * This method does an RPC to hbase:meta. Do not call this method with a lock/synchronize held. * @param hris The hris to check if empty in hbase:meta and if so, clean them up. */ private void cleanIfNoMetaEntry(Set<HRegionInfo> hris) { if (hris.isEmpty()) return; for (HRegionInfo hri: hris) { try { // This is RPC to meta table. It is done while we have a synchronize on // regionstates. No progress will be made if meta is not available at this time. // This is a cleanup task. Not critical. if (MetaTableAccessor.getRegion(server.getConnection(), hri.getEncodedNameAsBytes()) == null) { regionOffline(hri); FSUtils.deleteRegionDir(server.getConfiguration(), hri); } } catch (IOException e) { LOG.warn("Got exception while deleting " + hri + " directories from file system.", e); } } }
/** * Get the HRegionInfo from cache, if not there, from the hbase:meta table * @param regionName * @return HRegionInfo for the region */ @SuppressWarnings("deprecation") protected HRegionInfo getRegionInfo(final byte [] regionName) { String encodedName = HRegionInfo.encodeRegionName(regionName); RegionState regionState = getRegionState(encodedName); if (regionState != null) { return regionState.getRegion(); } try { Pair<HRegionInfo, ServerName> p = MetaTableAccessor.getRegion(server.getConnection(), regionName); HRegionInfo hri = p == null ? null : p.getFirst(); if (hri != null) { createRegionState(hri); } return hri; } catch (IOException e) { server.abort("Aborting because error occoured while reading " + Bytes.toStringBinary(regionName) + " from hbase:meta", e); return null; } }
/** * If merged region no longer holds reference to the merge regions, archive * merge region on hdfs and perform deleting references in hbase:meta * @param mergedRegion * @param regionA * @param regionB * @return true if we delete references in merged region on hbase:meta and archive * the files on the file system * @throws IOException */ boolean cleanMergeRegion(final HRegionInfo mergedRegion, final HRegionInfo regionA, final HRegionInfo regionB) throws IOException { FileSystem fs = this.services.getMasterFileSystem().getFileSystem(); Path rootdir = this.services.getMasterFileSystem().getRootDir(); Path tabledir = FSUtils.getTableDir(rootdir, mergedRegion.getTable()); HTableDescriptor htd = getTableDescriptor(mergedRegion.getTable()); HRegionFileSystem regionFs = null; try { regionFs = HRegionFileSystem.openRegionFromFileSystem( this.services.getConfiguration(), fs, tabledir, mergedRegion, true); } catch (IOException e) { LOG.warn("Merged region does not exist: " + mergedRegion.getEncodedName()); } if (regionFs == null || !regionFs.hasReferences(htd)) { LOG.debug("Deleting region " + regionA.getRegionNameAsString() + " and " + regionB.getRegionNameAsString() + " from fs because merged region no longer holds references"); HFileArchiver.archiveRegion(this.services.getConfiguration(), fs, regionA); HFileArchiver.archiveRegion(this.services.getConfiguration(), fs, regionB); MetaTableAccessor.deleteMergeQualifiers(server.getConnection(), mergedRegion); return true; } return false; }
/** * Checks if the specified region has merge qualifiers, if so, try to clean * them * @param region * @return true if the specified region doesn't have merge qualifier now * @throws IOException */ public boolean cleanMergeQualifier(final HRegionInfo region) throws IOException { // Get merge regions if it is a merged region and already has merge // qualifier Pair<HRegionInfo, HRegionInfo> mergeRegions = MetaTableAccessor .getRegionsFromMergeQualifier(this.services.getConnection(), region.getRegionName()); if (mergeRegions == null || (mergeRegions.getFirst() == null && mergeRegions.getSecond() == null)) { // It doesn't have merge qualifier, no need to clean return true; } // It shouldn't happen, we must insert/delete these two qualifiers together if (mergeRegions.getFirst() == null || mergeRegions.getSecond() == null) { LOG.error("Merged region " + region.getRegionNameAsString() + " has only one merge qualifier in META."); return false; } return cleanMergeRegion(region, mergeRegions.getFirst(), mergeRegions.getSecond()); }
/** * Returns the {@link ServerName} from catalog table {@link Result} * where the region is transitioning. It should be the same as * {@link HRegionInfo#getServerName(Result)} if the server is at OPEN state. * @param r Result to pull the transitioning server name from * @return A ServerName instance or {@link HRegionInfo#getServerName(Result)} * if necessary fields not found or empty. */ static ServerName getRegionServer(final Result r, int replicaId) { Cell cell = r.getColumnLatestCell(HConstants.CATALOG_FAMILY, getServerNameColumn(replicaId)); if (cell == null || cell.getValueLength() == 0) { RegionLocations locations = MetaTableAccessor.getRegionLocations(r); if (locations != null) { HRegionLocation location = locations.getRegionLocation(replicaId); if (location != null) { return location.getServerName(); } } return null; } return ServerName.parseServerName(Bytes.toString(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength())); }
private boolean prepareCreate(final MasterProcedureEnv env) throws IOException { final TableName tableName = getTableName(); if (MetaTableAccessor.tableExists(env.getMasterServices().getConnection(), tableName)) { setFailure("master-create-table", new TableExistsException(getTableName())); return false; } // During master initialization, the ZK state could be inconsistent from failed DDL // in the past. If we fail here, it would prevent master to start. We should force // setting the system table state regardless the table state. boolean skipTableStateCheck = !(env.getMasterServices().isInitialized()) && tableName.isSystemTable(); if (!skipTableStateCheck) { TableStateManager tsm = env.getMasterServices().getAssignmentManager().getTableStateManager(); if (tsm.isTableState(tableName, true, ZooKeeperProtos.Table.State.ENABLING, ZooKeeperProtos.Table.State.ENABLED)) { LOG.warn("The table " + tableName + " does not exist in meta but has a znode. " + "run hbck to fix inconsistencies."); setFailure("master-create-table", new TableExistsException(getTableName())); return false; } } return true; }
/** * There may be items for this table still up in hbase:meta in the case where the * info:regioninfo column was empty because of some write error. Remove ALL rows from hbase:meta * that have to do with this table. See HBASE-12980. * @throws IOException */ private static void cleanAnyRemainingRows(final MasterProcedureEnv env, final TableName tableName) throws IOException { ClusterConnection connection = env.getMasterServices().getConnection(); Scan tableScan = MetaTableAccessor.getScanForTableName(tableName); try (Table metaTable = connection.getTable(TableName.META_TABLE_NAME)) { List<Delete> deletes = new ArrayList<Delete>(); try (ResultScanner resScanner = metaTable.getScanner(tableScan)) { for (Result result : resScanner) { deletes.add(new Delete(result.getRow())); } } if (!deletes.isEmpty()) { LOG.warn("Deleting some vestigal " + deletes.size() + " rows of " + tableName + " from " + TableName.META_TABLE_NAME); metaTable.delete(deletes); } } }
/** * Used by the client to identify if all regions have the schema updates * * @param tableName * @return Pair indicating the status of the alter command * @throws IOException */ public Pair<Integer, Integer> getReopenStatus(TableName tableName) throws IOException { List<HRegionInfo> hris; if (TableName.META_TABLE_NAME.equals(tableName)) { hris = new MetaTableLocator().getMetaRegions(server.getZooKeeper()); } else { hris = MetaTableAccessor.getTableRegions(server.getZooKeeper(), server.getConnection(), tableName, true); } Integer pending = 0; for (HRegionInfo hri : hris) { String name = hri.getEncodedName(); // no lock concurrent access ok: sequential consistency respected. if (regionsToReopen.containsKey(name) || regionStates.isRegionInTransition(name)) { pending++; } } return new Pair<Integer, Integer>(pending, hris.size()); }
/** * Puts the specified HRegionInfo into META with replica related columns */ public static void fixMetaHoleOnlineAndAddReplicas(Configuration conf, HRegionInfo hri, Collection<ServerName> servers, int numReplicas) throws IOException { Connection conn = ConnectionFactory.createConnection(conf); Table meta = conn.getTable(TableName.META_TABLE_NAME); Put put = MetaTableAccessor.makePutFromRegionInfo(hri); if (numReplicas > 1) { Random r = new Random(); ServerName[] serversArr = servers.toArray(new ServerName[servers.size()]); for (int i = 1; i < numReplicas; i++) { ServerName sn = serversArr[r.nextInt(serversArr.length)]; // the column added here is just to make sure the master is able to // see the additional replicas when it is asked to assign. The // final value of these columns will be different and will be updated // by the actual regionservers that start hosting the respective replicas MetaTableAccessor.addLocation(put, sn, sn.getStartcode(), -1, i); } } meta.put(put); meta.close(); conn.close(); }
/** * Lists all of the table regions currently in META. * @param connection * @param tableName * @return Map of all user-space regions to servers * @throws IOException */ public static NavigableMap<HRegionInfo, ServerName> allTableRegions( Connection connection, final TableName tableName) throws IOException { final NavigableMap<HRegionInfo, ServerName> regions = new TreeMap<HRegionInfo, ServerName>(); MetaScannerVisitor visitor = new TableMetaScannerVisitor(tableName) { @Override public boolean processRowInternal(Result result) throws IOException { RegionLocations locations = MetaTableAccessor.getRegionLocations(result); if (locations == null) return true; for (HRegionLocation loc : locations.getRegionLocations()) { if (loc != null) { HRegionInfo regionInfo = loc.getRegionInfo(); regions.put(new UnmodifyableHRegionInfo(regionInfo), loc.getServerName()); } } return true; } }; metaScan(connection, visitor, tableName); return regions; }
@Test public void testCreateTableWithSingleReplica() throws Exception { final int numRegions = 3; final int numReplica = 1; final TableName table = TableName.valueOf("singleReplicaTable"); try { HTableDescriptor desc = new HTableDescriptor(table); desc.setRegionReplication(numReplica); desc.addFamily(new HColumnDescriptor("family")); ADMIN.createTable(desc, Bytes.toBytes("A"), Bytes.toBytes("Z"), numRegions); validateNumberOfRowsInMeta(table, numRegions, ADMIN.getConnection()); List<HRegionInfo> hris = MetaTableAccessor.getTableRegions(TEST_UTIL.getZooKeeperWatcher(), ADMIN.getConnection(), table); assert(hris.size() == numRegions * numReplica); } finally { ADMIN.disableTable(table); ADMIN.deleteTable(table); } }
protected HRegionInfo createRegion(Configuration conf, final Table htbl, byte[] startKey, byte[] endKey) throws IOException { Table meta = new HTable(conf, TableName.META_TABLE_NAME); HTableDescriptor htd = htbl.getTableDescriptor(); HRegionInfo hri = new HRegionInfo(htbl.getName(), startKey, endKey); LOG.info("manually adding regioninfo and hdfs data: " + hri.toString()); Path rootDir = FSUtils.getRootDir(conf); FileSystem fs = rootDir.getFileSystem(conf); Path p = new Path(FSUtils.getTableDir(rootDir, htbl.getName()), hri.getEncodedName()); fs.mkdirs(p); Path riPath = new Path(p, HRegionFileSystem.REGION_INFO_FILE); FSDataOutputStream out = fs.create(riPath); out.write(hri.toDelimitedByteArray()); out.close(); // add to meta. MetaTableAccessor.addRegionToMeta(meta, hri); meta.close(); return hri; }
@Test public void testOpenClosingRegion() throws Exception { Assert.assertTrue(getRS().getRegion(regionName).isAvailable()); try { // we re-opened meta so some of its data is lost ServerName sn = getRS().getServerName(); MetaTableAccessor.updateRegionLocation(getRS().getConnection(), hri, sn, getRS().getRegion(regionName).getOpenSeqNum(), -1); // fake region to be closing now, need to clear state afterwards getRS().regionsInTransitionInRS.put(hri.getEncodedNameAsBytes(), Boolean.FALSE); AdminProtos.OpenRegionRequest orr = RequestConverter.buildOpenRegionRequest(sn, hri, 0, null, null); getRS().rpcServices.openRegion(null, orr); Assert.fail("The closing region should not be opened"); } catch (ServiceException se) { Assert.assertTrue("The region should be already in transition", se.getCause() instanceof RegionAlreadyInTransitionException); } finally { getRS().regionsInTransitionInRS.remove(hri.getEncodedNameAsBytes()); } }
private void waitAndVerifyRegionNum(HMaster master, TableName tablename, int expectedRegionNum) throws Exception { List<Pair<HRegionInfo, ServerName>> tableRegionsInMeta; List<HRegionInfo> tableRegionsInMaster; long timeout = System.currentTimeMillis() + waitTime; while (System.currentTimeMillis() < timeout) { tableRegionsInMeta = MetaTableAccessor.getTableRegionsAndLocations(master.getZooKeeper(), master.getConnection(), tablename); tableRegionsInMaster = master.getAssignmentManager().getRegionStates() .getRegionsOfTable(tablename); if (tableRegionsInMeta.size() == expectedRegionNum && tableRegionsInMaster.size() == expectedRegionNum) { break; } Thread.sleep(250); } tableRegionsInMeta = MetaTableAccessor.getTableRegionsAndLocations(master.getZooKeeper(), master.getConnection(), tablename); LOG.info("Regions after merge:" + Joiner.on(',').join(tableRegionsInMeta)); assertEquals(expectedRegionNum, tableRegionsInMeta.size()); }
/** * Lists table regions and locations grouped by region range from META. */ public static List<RegionLocations> listTableRegionLocations(Configuration conf, Connection connection, final TableName tableName) throws IOException { final List<RegionLocations> regions = new ArrayList<RegionLocations>(); MetaScannerVisitor visitor = new TableMetaScannerVisitor(tableName) { @Override public boolean processRowInternal(Result result) throws IOException { RegionLocations locations = MetaTableAccessor.getRegionLocations(result); if (locations == null) return true; regions.add(locations); return true; } }; metaScan(connection, visitor, tableName); return regions; }
@Override public void postStartMaster(ObserverContext<MasterCoprocessorEnvironment> ctx) throws IOException { if (!MetaTableAccessor.tableExists(ctx.getEnvironment().getMasterServices() .getConnection(), AccessControlLists.ACL_TABLE_NAME)) { // initialize the ACL storage table AccessControlLists.createACLTable(ctx.getEnvironment().getMasterServices()); } else { aclTabAvailable = true; } }
/** * Check if table exists or not * @param tableName Name of a table. * @return tableName instance * @throws IOException if a remote or network exception occurs. * @throws TableNotFoundException if table does not exist. */ private TableName checkTableExists(final TableName tableName) throws IOException { if (!MetaTableAccessor.tableExists(connection, tableName)) { throw new TableNotFoundException(tableName); } return tableName; }
/** * Ensures that the given number of reduce tasks for the given job * configuration does not exceed the number of regions for the given table. * * @param table The table to get the region count for. * @param job The current job configuration to adjust. * @throws IOException When retrieving the table details fails. */ // Used by tests. public static void limitNumReduceTasks(String table, JobConf job) throws IOException { int regions = MetaTableAccessor.getRegionCount(HBaseConfiguration.create(job), TableName.valueOf(table)); if (job.getNumReduceTasks() > regions) job.setNumReduceTasks(regions); }
/** * Ensures that the given number of map tasks for the given job * configuration does not exceed the number of regions for the given table. * * @param table The table to get the region count for. * @param job The current job configuration to adjust. * @throws IOException When retrieving the table details fails. */ // Used by tests. public static void limitNumMapTasks(String table, JobConf job) throws IOException { int regions = MetaTableAccessor.getRegionCount(HBaseConfiguration.create(job), TableName.valueOf(table)); if (job.getNumMapTasks() > regions) job.setNumMapTasks(regions); }
/** * get the regions of a given table. * * @param tableName the name of the table * @return Ordered list of {@link HRegionInfo}. * @throws IOException */ @Override public List<HRegionInfo> getTableRegions(final TableName tableName) throws IOException { ZooKeeperWatcher zookeeper = new ZooKeeperWatcher(conf, ZK_IDENTIFIER_PREFIX + connection.toString(), new ThrowableAbortable()); List<HRegionInfo> Regions = null; try { Regions = MetaTableAccessor.getTableRegions(zookeeper, connection, tableName, true); } finally { zookeeper.close(); } return Regions; }
/** * If daughters no longer hold reference to the parents, delete the parent. * @param parent HRegionInfo of split offlined parent * @param rowContent Content of <code>parent</code> row in * <code>metaRegionName</code> * @return True if we removed <code>parent</code> from meta table and from * the filesystem. * @throws IOException */ boolean cleanParent(final HRegionInfo parent, Result rowContent) throws IOException { boolean result = false; // Check whether it is a merged region and not clean reference // No necessary to check MERGEB_QUALIFIER because these two qualifiers will // be inserted/deleted together if (rowContent.getValue(HConstants.CATALOG_FAMILY, HConstants.MERGEA_QUALIFIER) != null) { // wait cleaning merge region first return result; } // Run checks on each daughter split. PairOfSameType<HRegionInfo> daughters = HRegionInfo.getDaughterRegions(rowContent); Pair<Boolean, Boolean> a = checkDaughterInFs(parent, daughters.getFirst()); Pair<Boolean, Boolean> b = checkDaughterInFs(parent, daughters.getSecond()); if (hasNoReferences(a) && hasNoReferences(b)) { LOG.debug("Deleting region " + parent.getRegionNameAsString() + " because daughter splits no longer hold references"); FileSystem fs = this.services.getMasterFileSystem().getFileSystem(); if (LOG.isTraceEnabled()) LOG.trace("Archiving parent region: " + parent); HFileArchiver.archiveRegion(this.services.getConfiguration(), fs, parent); MetaTableAccessor.deleteRegion(this.connection, parent); result = true; } return result; }
/** * Update meta table with favored nodes info * @param regionToFavoredNodes map of HRegionInfo's to their favored nodes * @param connection connection to be used * @throws IOException */ public static void updateMetaWithFavoredNodesInfo( Map<HRegionInfo, List<ServerName>> regionToFavoredNodes, Connection connection) throws IOException { List<Put> puts = new ArrayList<Put>(); for (Map.Entry<HRegionInfo, List<ServerName>> entry : regionToFavoredNodes.entrySet()) { Put put = makePutFromRegionInfo(entry.getKey(), entry.getValue()); if (put != null) { puts.add(put); } } MetaTableAccessor.putsToMetaTable(connection, puts); LOG.info("Added " + puts.size() + " regions in META"); }
/** * Generates and returns a Put containing the region info for the catalog table * and the servers * @param regionInfo * @param favoredNodeList * @return Put object */ static Put makePutFromRegionInfo(HRegionInfo regionInfo, List<ServerName>favoredNodeList) throws IOException { Put put = null; if (favoredNodeList != null) { put = MetaTableAccessor.makePutFromRegionInfo(regionInfo); byte[] favoredNodes = getFavoredNodes(favoredNodeList); put.addImmutable(HConstants.CATALOG_FAMILY, FAVOREDNODES_QUALIFIER, EnvironmentEdgeManager.currentTime(), favoredNodes); LOG.info("Create the region " + regionInfo.getRegionNameAsString() + " with favored nodes " + Bytes.toString(favoredNodes)); } return put; }
@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); } }
/** * Add the specified set of regions to the hbase:meta table. */ protected static void addRegionsToMeta(final MasterProcedureEnv env, final HTableDescriptor hTableDescriptor, final List<HRegionInfo> regionInfos) throws IOException { MetaTableAccessor.addRegionsToMeta(env.getMasterServices().getConnection(), regionInfos, hTableDescriptor.getRegionReplication()); }
/** * Action before any real action of disabling 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 * @throws HBaseException * @throws IOException */ private boolean prepareDisable(final MasterProcedureEnv env) throws HBaseException, IOException { boolean canTableBeDisabled = true; if (tableName.equals(TableName.META_TABLE_NAME)) { setFailure("master-disable-table", new ConstraintException("Cannot disable catalog table")); canTableBeDisabled = false; } else if (!MetaTableAccessor.tableExists(env.getMasterServices().getConnection(), tableName)) { setFailure("master-disable-table", new TableNotFoundException(tableName)); canTableBeDisabled = 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: A quick state check should be enough for us to move forward. However, instead of // calling TableStateManager.isTableState() to just check the state, we called // TableStateManager.setTableStateIfInStates() to set the state to DISABLING from ENABLED. // This is because we treat empty state as enabled from 0.92-clusters. See // ZKTableStateManager.setTableStateIfInStates() that has a hack solution to work around // this issue. TableStateManager tsm = env.getMasterServices().getAssignmentManager().getTableStateManager(); if (!tsm.setTableStateIfInStates(tableName, ZooKeeperProtos.Table.State.DISABLING, ZooKeeperProtos.Table.State.DISABLING, ZooKeeperProtos.Table.State.ENABLED)) { LOG.info("Table " + tableName + " isn't enabled; skipping disable"); setFailure("master-disable-table", new TableNotEnabledException(tableName)); canTableBeDisabled = false; } } // We are done the check. Future actions in this procedure could be done asynchronously. ProcedurePrepareLatch.releaseLatch(syncLatch, this); return canTableBeDisabled; }
/** * 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); } }
protected static List<HRegionInfo> getRegionsFromMeta(final MasterProcedureEnv env, final TableName tableName) throws IOException { return ProcedureSyncWait.waitFor(env, "regions of table=" + tableName + " from meta", new ProcedureSyncWait.Predicate<List<HRegionInfo>>() { @Override public List<HRegionInfo> evaluate() throws IOException { if (TableName.META_TABLE_NAME.equals(tableName)) { return new MetaTableLocator().getMetaRegions(env.getMasterServices().getZooKeeper()); } return MetaTableAccessor.getTableRegions(env.getMasterServices().getZooKeeper(), env.getMasterServices().getConnection(), 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; }
/** * 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; } } }
/** * update replica column families if necessary. * @param env MasterProcedureEnv * @throws IOException */ private void updateReplicaColumnsIfNeeded( final MasterProcedureEnv env, final HTableDescriptor oldHTableDescriptor, final HTableDescriptor newHTableDescriptor) throws IOException { final int oldReplicaCount = oldHTableDescriptor.getRegionReplication(); final int newReplicaCount = newHTableDescriptor.getRegionReplication(); if (newReplicaCount < oldReplicaCount) { Set<byte[]> tableRows = new HashSet<byte[]>(); Connection connection = env.getMasterServices().getConnection(); Scan scan = MetaTableAccessor.getScanForTableName(getTableName()); scan.addColumn(HConstants.CATALOG_FAMILY, HConstants.REGIONINFO_QUALIFIER); try (Table metaTable = connection.getTable(TableName.META_TABLE_NAME)) { ResultScanner resScanner = metaTable.getScanner(scan); for (Result result : resScanner) { tableRows.add(result.getRow()); } MetaTableAccessor.removeRegionReplicasFromMeta( tableRows, newReplicaCount, oldReplicaCount - newReplicaCount, connection); } } // Setup replication for region replicas if needed if (newReplicaCount > 1 && oldReplicaCount <= 1) { ServerRegionReplicaUtil.setupRegionReplicaReplication(env.getMasterConfiguration()); } }
public void start() throws IOException { if (!MetaTableAccessor.tableExists(masterServices.getConnection(), TableName.NAMESPACE_TABLE_NAME)) { LOG.info("Namespace table not found. Creating..."); createNamespaceTable(masterServices); } try { // Wait for the namespace table to be assigned. // If timed out, we will move ahead without initializing it. // So that it should be initialized later on lazily. long startTime = EnvironmentEdgeManager.currentTime(); int timeout = conf.getInt(NS_INIT_TIMEOUT, DEFAULT_NS_INIT_TIMEOUT); while (!isTableAssigned()) { if (EnvironmentEdgeManager.currentTime() - startTime + 100 > timeout) { // We can't do anything if ns is not online. throw new IOException("Timedout " + timeout + "ms waiting for namespace table to " + "be assigned"); } Thread.sleep(100); } } catch (InterruptedException e) { throw (InterruptedIOException)new InterruptedIOException().initCause(e); } // initialize namespace table isTableAvailableAndInitialized(); }
/** * Offline specified region from master's in-memory state. It will not attempt to * reassign the region as in unassign. * * This is a special method that should be used by experts or hbck. * */ @Override public OfflineRegionResponse offlineRegion(RpcController controller, OfflineRegionRequest request) throws ServiceException { final byte [] regionName = request.getRegion().getValue().toByteArray(); RegionSpecifierType type = request.getRegion().getType(); if (type != RegionSpecifierType.REGION_NAME) { LOG.warn("moveRegion specifier type: expected: " + RegionSpecifierType.REGION_NAME + " actual: " + type); } try { master.checkInitialized(); Pair<HRegionInfo, ServerName> pair = MetaTableAccessor.getRegion(master.getConnection(), regionName); if (pair == null) throw new UnknownRegionException(Bytes.toStringBinary(regionName)); HRegionInfo hri = pair.getFirst(); if (master.cpHost != null) { master.cpHost.preRegionOffline(hri); } LOG.info(master.getClientIdAuditPrefix() + " offline " + hri.getRegionNameAsString()); master.assignmentManager.regionOffline(hri); if (master.cpHost != null) { master.cpHost.postRegionOffline(hri); } } catch (IOException ioe) { throw new ServiceException(ioe); } return OfflineRegionResponse.newBuilder().build(); }
@Override public UnassignRegionResponse unassignRegion(RpcController controller, UnassignRegionRequest req) throws ServiceException { try { final byte [] regionName = req.getRegion().getValue().toByteArray(); RegionSpecifierType type = req.getRegion().getType(); final boolean force = req.getForce(); UnassignRegionResponse urr = UnassignRegionResponse.newBuilder().build(); master.checkInitialized(); if (type != RegionSpecifierType.REGION_NAME) { LOG.warn("unassignRegion specifier type: expected: " + RegionSpecifierType.REGION_NAME + " actual: " + type); } Pair<HRegionInfo, ServerName> pair = MetaTableAccessor.getRegion(master.getConnection(), regionName); if (pair == null) throw new UnknownRegionException(Bytes.toString(regionName)); HRegionInfo hri = pair.getFirst(); if (master.cpHost != null) { if (master.cpHost.preUnassign(hri, force)) { return urr; } } LOG.debug(master.getClientIdAuditPrefix() + " unassign " + hri.getRegionNameAsString() + " in current location if it is online and reassign.force=" + force); master.assignmentManager.unassign(hri, force); if (master.assignmentManager.getRegionStates().isRegionOffline(hri)) { LOG.debug("Region " + hri.getRegionNameAsString() + " is not online on any region server, reassigning it."); master.assignRegion(hri); } if (master.cpHost != null) { master.cpHost.postUnassign(hri, force); } return urr; } catch (IOException ioe) { throw new ServiceException(ioe); } }
/** * Handle a ZK unassigned node transition triggered by HBCK repair tool. * <p> * This is handled in a separate code path because it breaks the normal rules. * @param rt */ @SuppressWarnings("deprecation") private void handleHBCK(RegionTransition rt) { String encodedName = HRegionInfo.encodeRegionName(rt.getRegionName()); LOG.info("Handling HBCK triggered transition=" + rt.getEventType() + ", server=" + rt.getServerName() + ", region=" + HRegionInfo.prettyPrint(encodedName)); RegionState regionState = regionStates.getRegionTransitionState(encodedName); switch (rt.getEventType()) { case M_ZK_REGION_OFFLINE: HRegionInfo regionInfo; if (regionState != null) { regionInfo = regionState.getRegion(); } else { try { byte [] name = rt.getRegionName(); Pair<HRegionInfo, ServerName> p = MetaTableAccessor.getRegion( this.server.getConnection(), name); regionInfo = p.getFirst(); } catch (IOException e) { LOG.info("Exception reading hbase:meta doing HBCK repair operation", e); return; } } LOG.info("HBCK repair is triggering assignment of region=" + regionInfo.getRegionNameAsString()); // trigger assign, node is already in OFFLINE so don't need to update ZK assign(regionInfo, false); break; default: LOG.warn("Received unexpected region state from HBCK: " + rt.toString()); break; } }
/** * Generate set of puts to add to new meta. This expects the tables to be * clean with no overlaps or holes. If there are any problems it returns null. * * @return An array list of puts to do in bulk, null if tables have problems */ private ArrayList<Put> generatePuts( SortedMap<TableName, TableInfo> tablesInfo) throws IOException { ArrayList<Put> puts = new ArrayList<Put>(); boolean hasProblems = false; for (Entry<TableName, TableInfo> e : tablesInfo.entrySet()) { TableName name = e.getKey(); // skip "hbase:meta" if (name.compareTo(TableName.META_TABLE_NAME) == 0) { continue; } TableInfo ti = e.getValue(); for (Entry<byte[], Collection<HbckInfo>> spl : ti.sc.getStarts().asMap() .entrySet()) { Collection<HbckInfo> his = spl.getValue(); int sz = his.size(); if (sz != 1) { // problem LOG.error("Split starting at " + Bytes.toStringBinary(spl.getKey()) + " had " + sz + " regions instead of exactly 1." ); hasProblems = true; continue; } // add the row directly to meta. HbckInfo hi = his.iterator().next(); HRegionInfo hri = hi.getHdfsHRI(); // hi.metaEntry; Put p = MetaTableAccessor.makePutFromRegionInfo(hri); puts.add(p); } } return hasProblems ? null : puts; }
static void createPresplitTable(TableName tableName, SplitAlgorithm splitAlgo, String[] columnFamilies, Configuration conf) throws IOException, InterruptedException { final int splitCount = conf.getInt("split.count", 0); Preconditions.checkArgument(splitCount > 1, "Split count must be > 1"); Preconditions.checkArgument(columnFamilies.length > 0, "Must specify at least one column family. "); LOG.debug("Creating table " + tableName + " with " + columnFamilies.length + " column families. Presplitting to " + splitCount + " regions"); HTableDescriptor desc = new HTableDescriptor(tableName); for (String cf : columnFamilies) { desc.addFamily(new HColumnDescriptor(Bytes.toBytes(cf))); } try (Connection connection = ConnectionFactory.createConnection(conf)) { Admin admin = connection.getAdmin(); try { Preconditions.checkArgument(!admin.tableExists(tableName), "Table already exists: " + tableName); admin.createTable(desc, splitAlgo.split(splitCount)); } finally { admin.close(); } LOG.debug("Table created! Waiting for regions to show online in META..."); if (!conf.getBoolean("split.verify", true)) { // NOTE: createTable is synchronous on the table, but not on the regions int onlineRegions = 0; while (onlineRegions < splitCount) { onlineRegions = MetaTableAccessor.getRegionCount(connection, tableName); LOG.debug(onlineRegions + " of " + splitCount + " regions online..."); if (onlineRegions < splitCount) { Thread.sleep(10 * 1000); // sleep } } } LOG.debug("Finished creating table with " + splitCount + " regions"); } }