diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java index 9f93e0bb356..4c7d728f86f 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java @@ -572,6 +572,7 @@ public class AssignmentManager extends ZooKeeperListener { } Set disabledOrDisablingOrEnabling = null; + Map allRegions = null; if (!failover) { disabledOrDisablingOrEnabling = tableStateManager.getTablesInStates( @@ -579,7 +580,8 @@ public class AssignmentManager extends ZooKeeperListener { ZooKeeperProtos.Table.State.ENABLING); // Clean re/start, mark all user regions closed before reassignment - regionStates.closeAllUserRegions(disabledOrDisablingOrEnabling); + allRegions = regionStates.closeAllUserRegions( + disabledOrDisablingOrEnabling); } // Now region states are restored @@ -608,7 +610,7 @@ public class AssignmentManager extends ZooKeeperListener { if (!failover) { // Fresh cluster startup. LOG.info("Clean cluster startup. Assigning user regions"); - assignAllUserRegions(disabledOrDisablingOrEnabling); + assignAllUserRegions(allRegions); } return failover; } @@ -2648,22 +2650,10 @@ public class AssignmentManager extends ZooKeeperListener { * should be shutdown. * @throws InterruptedException * @throws IOException - * @throws KeeperException */ - private void assignAllUserRegions(Set disabledOrDisablingOrEnabling) - throws IOException, InterruptedException, KeeperException, CoordinatedStateException { - // Skip assignment for regions of tables in DISABLING state because during clean cluster startup - // no RS is alive and regions map also doesn't have any information about the regions. - // See HBASE-6281. - // Scan hbase:meta for all user regions, skipping any disabled tables - Map allRegions; - SnapshotOfRegionAssignmentFromMeta snapshotOfRegionAssignment = - new SnapshotOfRegionAssignmentFromMeta(catalogTracker, disabledOrDisablingOrEnabling, true); - snapshotOfRegionAssignment.initialize(); - allRegions = snapshotOfRegionAssignment.getRegionToRegionServerMap(); - if (allRegions == null || allRegions.isEmpty()) { - return; - } + private void assignAllUserRegions(Map allRegions) + throws IOException, InterruptedException { + if (allRegions == null || allRegions.isEmpty()) return; // Determine what type of assignment to do on startup boolean retainAssignment = server.getConfiguration(). diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java index e444a94239d..c92c020d829 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java @@ -73,8 +73,6 @@ import org.apache.hadoop.hbase.CoordinatedStateManager; import org.apache.hadoop.hbase.coprocessor.CoprocessorHost; import org.apache.hadoop.hbase.exceptions.DeserializationException; import org.apache.hadoop.hbase.executor.ExecutorType; -import org.apache.hadoop.hbase.http.InfoServer; -import org.apache.hadoop.hbase.ipc.FifoRpcScheduler; import org.apache.hadoop.hbase.ipc.RequestContext; import org.apache.hadoop.hbase.ipc.RpcServer; import org.apache.hadoop.hbase.ipc.ServerNotRunningYetException; diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/RegionStates.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/RegionStates.java index 39c42b9df08..85677af8cbd 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/RegionStates.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/RegionStates.java @@ -651,7 +651,12 @@ public class RegionStates { lastAssignments.put(encodedName, serverName); } - synchronized void closeAllUserRegions(Set excludedTables) { + /** + * At cluster clean re/start, mark all user regions closed except those of tables + * that are excluded, such as disabled/disabling/enabling tables. All user regions + * and their previous locations are returned. + */ + synchronized Map closeAllUserRegions(Set excludedTables) { boolean noExcludeTables = excludedTables == null || excludedTables.isEmpty(); Set toBeClosed = new HashSet(regionStates.size()); for(RegionState state: regionStates.values()) { @@ -662,9 +667,13 @@ public class RegionStates { toBeClosed.add(hri); } } + Map allUserRegions = + new HashMap(toBeClosed.size()); for (HRegionInfo hri: toBeClosed) { - updateRegionState(hri, State.CLOSED); + RegionState regionState = updateRegionState(hri, State.CLOSED); + allUserRegions.put(hri, regionState.getServerName()); } + return allUserRegions; } /** diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/ServerManager.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/ServerManager.java index 3a874103124..3591188816a 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/ServerManager.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/ServerManager.java @@ -63,7 +63,6 @@ import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.ServerInfo; import org.apache.hadoop.hbase.protobuf.generated.ZooKeeperProtos.SplitLogTask.RecoveryMode; import org.apache.hadoop.hbase.regionserver.HRegionServer; import org.apache.hadoop.hbase.regionserver.RegionOpeningState; -import org.apache.hadoop.hbase.regionserver.wal.HLogSplitter; import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.hbase.util.Triple; import org.apache.hadoop.hbase.zookeeper.ZKUtil; @@ -926,8 +925,8 @@ public class ServerManager { int oldCount = 0; ServerName masterSn = master.getServerName(); boolean selfCheckedIn = isServerOnline(masterSn); - while (!this.master.isStopped() && !selfCheckedIn && count < maxToStart - && (lastCountChange+interval > now || timeout > slept || count < minToStart)) { + while (!this.master.isStopped() && (!selfCheckedIn || (count < maxToStart + && (lastCountChange+interval > now || timeout > slept || count < minToStart)))) { // Log some info at every interval time or if there is a change if (oldCount != count || lastLogTime+interval < now){ lastLogTime = now; @@ -935,7 +934,8 @@ public class ServerManager { "Waiting for region servers count to settle; currently"+ " checked in " + count + ", slept for " + slept + " ms," + " expecting minimum of " + minToStart + ", maximum of "+ maxToStart+ - ", timeout of "+timeout+" ms, interval of "+interval+" ms."; + ", timeout of "+timeout+" ms, interval of "+interval+" ms," + + " selfCheckedIn " + selfCheckedIn; LOG.info(msg); status.setStatus(msg); } @@ -958,7 +958,8 @@ public class ServerManager { LOG.info("Finished waiting for region servers count to settle;" + " checked in " + count + ", slept for " + slept + " ms," + " expecting minimum of " + minToStart + ", maximum of "+ maxToStart+","+ - " master is "+ (this.master.isStopped() ? "stopped.": "running.") + " master is "+ (this.master.isStopped() ? "stopped.": "running," + + " selfCheckedIn " + selfCheckedIn) ); } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/BaseLoadBalancer.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/BaseLoadBalancer.java index 8af97a5be6c..1c3c647205c 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/BaseLoadBalancer.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/BaseLoadBalancer.java @@ -834,10 +834,17 @@ public abstract class BaseLoadBalancer implements LoadBalancer { numRetainedAssigments++; } else { // multiple new servers in the cluster on this same host - int size = localServers.size(); - ServerName target = - localServers.contains(oldServerName) ? oldServerName : localServers.get(RANDOM - .nextInt(size)); + ServerName target = null; + for (ServerName tmp: localServers) { + if (tmp.getPort() == oldServerName.getPort()) { + target = tmp; + break; + } + } + if (target == null) { + int size = localServers.size(); + target = localServers.get(RANDOM.nextInt(size)); + } assignments.get(target).add(region); numRetainedAssigments++; } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java index 59713df3240..1664befcb08 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java @@ -118,7 +118,6 @@ import org.apache.hadoop.hbase.regionserver.handler.CloseMetaHandler; import org.apache.hadoop.hbase.regionserver.handler.CloseRegionHandler; import org.apache.hadoop.hbase.regionserver.wal.HLog; import org.apache.hadoop.hbase.regionserver.wal.HLogFactory; -import org.apache.hadoop.hbase.regionserver.wal.HLogSplitter; import org.apache.hadoop.hbase.regionserver.wal.HLogUtil; import org.apache.hadoop.hbase.regionserver.wal.WALActionsListener; import org.apache.hadoop.hbase.security.UserProvider; diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestMasterShutdown.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestMasterShutdown.java index e2bf0a0c128..9a3ec919b03 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestMasterShutdown.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestMasterShutdown.java @@ -102,6 +102,7 @@ public class TestMasterShutdown { // Create config to use for this cluster Configuration conf = HBaseConfiguration.create(); conf.setInt("hbase.ipc.client.failed.servers.expiry", 200); + conf.setInt(ServerManager.WAIT_ON_REGIONSERVERS_MINTOSTART, 1); // Start the cluster final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(conf); diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestRestartCluster.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestRestartCluster.java index 372c495a364..7fe1d231ba1 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestRestartCluster.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestRestartCluster.java @@ -19,17 +19,21 @@ package org.apache.hadoop.hbase.master; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; import java.util.List; +import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.*; +import org.apache.hadoop.hbase.catalog.CatalogTracker; import org.apache.hadoop.hbase.client.MetaScanner; import org.apache.hadoop.hbase.executor.EventType; import org.apache.hadoop.hbase.util.Bytes; +import org.apache.hadoop.hbase.util.JVMClusterUtil; import org.apache.hadoop.hbase.util.Threads; import org.apache.hadoop.hbase.zookeeper.ZKAssign; import org.apache.hadoop.hbase.zookeeper.ZKUtil; @@ -128,4 +132,112 @@ public class TestRestartCluster { UTIL.waitTableAvailable(TABLE); } } -} \ No newline at end of file + + /** + * This tests retaining assignments on a cluster restart + */ + @Test (timeout=300000) + public void testRetainAssignmentOnRestart() throws Exception { + UTIL.startMiniCluster(2); + while (!UTIL.getMiniHBaseCluster().getMaster().isInitialized()) { + Threads.sleep(1); + } + // Turn off balancer + UTIL.getMiniHBaseCluster().getMaster(). + getMasterRpcServices().synchronousBalanceSwitch(false); + LOG.info("\n\nCreating tables"); + for(byte [] TABLE : TABLES) { + UTIL.createTable(TABLE, FAMILY); + } + for(byte [] TABLE : TABLES) { + UTIL.waitTableEnabled(TABLE); + } + + HMaster master = UTIL.getMiniHBaseCluster().getMaster(); + AssignmentManager am = master.getAssignmentManager(); + am.waitUntilNoRegionsInTransition(120000); + + // We don't have to use SnapshotOfRegionAssignmentFromMeta. + // We use it here because AM used to use it to load all user region placements + CatalogTracker ct = new CatalogTracker(UTIL.getConfiguration()); + SnapshotOfRegionAssignmentFromMeta snapshot = new SnapshotOfRegionAssignmentFromMeta(ct); + snapshot.initialize(); + Map regionToRegionServerMap + = snapshot.getRegionToRegionServerMap(); + + MiniHBaseCluster cluster = UTIL.getHBaseCluster(); + List threads = cluster.getLiveRegionServerThreads(); + assertEquals(2, threads.size()); + int[] rsPorts = new int[3]; + for (int i = 0; i < 2; i++) { + rsPorts[i] = threads.get(i).getRegionServer().getServerName().getPort(); + } + rsPorts[2] = cluster.getMaster().getServerName().getPort(); + for (ServerName serverName: regionToRegionServerMap.values()) { + boolean found = false; // Test only, no need to optimize + for (int k = 0; k < 3 && !found; k++) { + found = serverName.getPort() == rsPorts[k]; + } + assertTrue(found); + } + + LOG.info("\n\nShutting down HBase cluster"); + cluster.shutdown(); + cluster.waitUntilShutDown(); + + LOG.info("\n\nSleeping a bit"); + Thread.sleep(2000); + + LOG.info("\n\nStarting cluster the second time with the same ports"); + try { + cluster.getConf().setInt( + ServerManager.WAIT_ON_REGIONSERVERS_MINTOSTART, 4); + master = cluster.startMaster().getMaster(); + for (int i = 0; i < 3; i++) { + cluster.getConf().setInt(HConstants.REGIONSERVER_PORT, rsPorts[i]); + cluster.startRegionServer(); + } + } finally { + // Reset region server port so as not to conflict with other tests + cluster.getConf().setInt(HConstants.REGIONSERVER_PORT, 0); + cluster.getConf().setInt( + ServerManager.WAIT_ON_REGIONSERVERS_MINTOSTART, 2); + } + + // Make sure live regionservers are on the same host/port + List localServers = master.getServerManager().getOnlineServersList(); + assertEquals(4, localServers.size()); + for (int i = 0; i < 3; i++) { + boolean found = false; + for (ServerName serverName: localServers) { + if (serverName.getPort() == rsPorts[i]) { + found = true; + break; + } + } + assertTrue(found); + } + + // Wait till master is initialized and all regions are assigned + RegionStates regionStates = master.getAssignmentManager().getRegionStates(); + int expectedRegions = regionToRegionServerMap.size() + 1; + while (!master.isInitialized() + || regionStates.getRegionAssignments().size() != expectedRegions) { + Threads.sleep(100); + } + + ct = new CatalogTracker(UTIL.getConfiguration()); + snapshot =new SnapshotOfRegionAssignmentFromMeta(ct); + snapshot.initialize(); + Map newRegionToRegionServerMap = + snapshot.getRegionToRegionServerMap(); + assertEquals(regionToRegionServerMap.size(), newRegionToRegionServerMap.size()); + for (Map.Entry entry: newRegionToRegionServerMap.entrySet()) { + if (TableName.NAMESPACE_TABLE_NAME.equals(entry.getKey().getTable())) continue; + ServerName oldServer = regionToRegionServerMap.get(entry.getKey()); + ServerName currentServer = entry.getValue(); + assertEquals(oldServer.getHostAndPort(), currentServer.getHostAndPort()); + assertNotEquals(oldServer.getStartcode(), currentServer.getStartcode()); + } + } +} diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestRSKilledWhenInitializing.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestRSKilledWhenInitializing.java index 29fb694307a..16c8681b880 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestRSKilledWhenInitializing.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestRSKilledWhenInitializing.java @@ -34,6 +34,7 @@ import org.apache.hadoop.hbase.LocalHBaseCluster; import org.apache.hadoop.hbase.MiniHBaseCluster; import org.apache.hadoop.hbase.ServerName; import org.apache.hadoop.hbase.master.HMaster; +import org.apache.hadoop.hbase.master.ServerManager; import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.NameStringPair; import org.apache.hadoop.hbase.protobuf.generated.RegionServerStatusProtos.RegionServerStartupResponse; import org.apache.hadoop.hbase.util.JVMClusterUtil.MasterThread; @@ -62,6 +63,7 @@ public class TestRSKilledWhenInitializing { firstRS.set(true); // Create config to use for this cluster Configuration conf = HBaseConfiguration.create(); + conf.setInt(ServerManager.WAIT_ON_REGIONSERVERS_MINTOSTART, 1); // Start the cluster final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(conf);