001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.master.assignment; 019 020import static org.junit.Assert.assertEquals; 021import static org.junit.Assert.assertNotNull; 022import static org.junit.Assert.assertNull; 023import static org.junit.Assert.assertTrue; 024 025import java.util.Collections; 026import java.util.concurrent.Executors; 027import java.util.concurrent.Future; 028import org.apache.hadoop.hbase.HBaseClassTestRule; 029import org.apache.hadoop.hbase.HBaseTestingUtil; 030import org.apache.hadoop.hbase.MetaTableAccessor; 031import org.apache.hadoop.hbase.TableName; 032import org.apache.hadoop.hbase.client.RegionInfo; 033import org.apache.hadoop.hbase.client.RegionInfoBuilder; 034import org.apache.hadoop.hbase.master.RegionState.State; 035import org.apache.hadoop.hbase.procedure2.ProcedureTestingUtility; 036import org.apache.hadoop.hbase.procedure2.util.StringUtils; 037import org.apache.hadoop.hbase.testclassification.LargeTests; 038import org.apache.hadoop.hbase.testclassification.MasterTests; 039import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 040import org.junit.ClassRule; 041import org.junit.Test; 042import org.junit.experimental.categories.Category; 043import org.slf4j.Logger; 044import org.slf4j.LoggerFactory; 045 046@Category({ MasterTests.class, LargeTests.class }) 047public class TestAssignmentManager extends TestAssignmentManagerBase { 048 049 @ClassRule 050 public static final HBaseClassTestRule CLASS_RULE = 051 HBaseClassTestRule.forClass(TestAssignmentManager.class); 052 053 private static final Logger LOG = LoggerFactory.getLogger(TestAssignmentManager.class); 054 055 @Test 056 public void testAssignWithGoodExec() throws Exception { 057 // collect AM metrics before test 058 collectAssignmentManagerMetrics(); 059 060 testAssign(new GoodRsExecutor()); 061 062 assertEquals(assignSubmittedCount + NREGIONS, 063 assignProcMetrics.getSubmittedCounter().getCount()); 064 assertEquals(assignFailedCount, assignProcMetrics.getFailedCounter().getCount()); 065 } 066 067 @Test 068 public void testAssignAndCrashBeforeResponse() throws Exception { 069 TableName tableName = TableName.valueOf("testAssignAndCrashBeforeResponse"); 070 RegionInfo hri = createRegionInfo(tableName, 1); 071 rsDispatcher.setMockRsExecutor(new HangThenRSCrashExecutor()); 072 TransitRegionStateProcedure proc = createAssignProcedure(hri); 073 waitOnFuture(submitProcedure(proc)); 074 } 075 076 @Test 077 public void testUnassignAndCrashBeforeResponse() throws Exception { 078 TableName tableName = TableName.valueOf("testAssignAndCrashBeforeResponse"); 079 RegionInfo hri = createRegionInfo(tableName, 1); 080 rsDispatcher.setMockRsExecutor(new HangOnCloseThenRSCrashExecutor()); 081 for (int i = 0; i < HangOnCloseThenRSCrashExecutor.TYPES_OF_FAILURE; i++) { 082 TransitRegionStateProcedure assign = createAssignProcedure(hri); 083 waitOnFuture(submitProcedure(assign)); 084 TransitRegionStateProcedure unassign = createUnassignProcedure(hri); 085 waitOnFuture(submitProcedure(unassign)); 086 } 087 } 088 089 @Test 090 public void testAssignSocketTimeout() throws Exception { 091 TableName tableName = TableName.valueOf(this.name.getMethodName()); 092 RegionInfo hri = createRegionInfo(tableName, 1); 093 094 // collect AM metrics before test 095 collectAssignmentManagerMetrics(); 096 097 rsDispatcher.setMockRsExecutor(new SocketTimeoutRsExecutor(20)); 098 waitOnFuture(submitProcedure(createAssignProcedure(hri))); 099 100 // we crashed a rs, so it is possible that there are other regions on the rs which will also be 101 // reassigned, so here we just assert greater than, not the exact number. 102 assertTrue(assignProcMetrics.getSubmittedCounter().getCount() > assignSubmittedCount); 103 assertEquals(assignFailedCount, assignProcMetrics.getFailedCounter().getCount()); 104 } 105 106 @Test 107 public void testAssignQueueFullOnce() throws Exception { 108 TableName tableName = TableName.valueOf(this.name.getMethodName()); 109 RegionInfo hri = createRegionInfo(tableName, 1); 110 111 // collect AM metrics before test 112 collectAssignmentManagerMetrics(); 113 114 rsDispatcher.setMockRsExecutor(new CallQueueTooBigOnceRsExecutor()); 115 waitOnFuture(submitProcedure(createAssignProcedure(hri))); 116 117 assertEquals(assignSubmittedCount + 1, assignProcMetrics.getSubmittedCounter().getCount()); 118 assertEquals(assignFailedCount, assignProcMetrics.getFailedCounter().getCount()); 119 } 120 121 @Test 122 public void testTimeoutThenQueueFull() throws Exception { 123 TableName tableName = TableName.valueOf(this.name.getMethodName()); 124 RegionInfo hri = createRegionInfo(tableName, 1); 125 126 // collect AM metrics before test 127 collectAssignmentManagerMetrics(); 128 129 rsDispatcher.setMockRsExecutor(new TimeoutThenCallQueueTooBigRsExecutor(10)); 130 waitOnFuture(submitProcedure(createAssignProcedure(hri))); 131 rsDispatcher.setMockRsExecutor(new TimeoutThenCallQueueTooBigRsExecutor(15)); 132 waitOnFuture(submitProcedure(createUnassignProcedure(hri))); 133 134 assertEquals(assignSubmittedCount + 1, assignProcMetrics.getSubmittedCounter().getCount()); 135 assertEquals(assignFailedCount, assignProcMetrics.getFailedCounter().getCount()); 136 assertEquals(unassignSubmittedCount + 1, unassignProcMetrics.getSubmittedCounter().getCount()); 137 assertEquals(unassignFailedCount, unassignProcMetrics.getFailedCounter().getCount()); 138 } 139 140 private void testAssign(final MockRSExecutor executor) throws Exception { 141 testAssign(executor, NREGIONS); 142 } 143 144 private void testAssign(MockRSExecutor executor, int nRegions) throws Exception { 145 rsDispatcher.setMockRsExecutor(executor); 146 147 TransitRegionStateProcedure[] assignments = new TransitRegionStateProcedure[nRegions]; 148 149 long st = EnvironmentEdgeManager.currentTime(); 150 bulkSubmit(assignments); 151 152 for (int i = 0; i < assignments.length; ++i) { 153 ProcedureTestingUtility.waitProcedure(master.getMasterProcedureExecutor(), assignments[i]); 154 assertTrue(assignments[i].toString(), assignments[i].isSuccess()); 155 } 156 long et = EnvironmentEdgeManager.currentTime(); 157 float sec = ((et - st) / 1000.0f); 158 LOG.info(String.format("[T] Assigning %dprocs in %s (%.2fproc/sec)", assignments.length, 159 StringUtils.humanTimeDiff(et - st), assignments.length / sec)); 160 } 161 162 @Test 163 public void testAssignAnAssignedRegion() throws Exception { 164 final TableName tableName = TableName.valueOf("testAssignAnAssignedRegion"); 165 final RegionInfo hri = createRegionInfo(tableName, 1); 166 167 // collect AM metrics before test 168 collectAssignmentManagerMetrics(); 169 170 rsDispatcher.setMockRsExecutor(new GoodRsExecutor()); 171 172 Future<byte[]> futureA = submitProcedure(createAssignProcedure(hri)); 173 174 // wait first assign 175 waitOnFuture(futureA); 176 am.getRegionStates().isRegionInState(hri, State.OPEN); 177 // Second should be a noop. We should recognize region is already OPEN internally 178 // and skip out doing nothing. 179 // wait second assign 180 Future<byte[]> futureB = submitProcedure(createAssignProcedure(hri)); 181 waitOnFuture(futureB); 182 am.getRegionStates().isRegionInState(hri, State.OPEN); 183 // TODO: What else can we do to ensure just a noop. 184 185 // TODO: Though second assign is noop, it's considered success, can noop be handled in a 186 // better way? 187 assertEquals(assignSubmittedCount + 2, assignProcMetrics.getSubmittedCounter().getCount()); 188 assertEquals(assignFailedCount, assignProcMetrics.getFailedCounter().getCount()); 189 } 190 191 @Test 192 public void testUnassignAnUnassignedRegion() throws Exception { 193 final TableName tableName = TableName.valueOf("testUnassignAnUnassignedRegion"); 194 final RegionInfo hri = createRegionInfo(tableName, 1); 195 196 // collect AM metrics before test 197 collectAssignmentManagerMetrics(); 198 199 rsDispatcher.setMockRsExecutor(new GoodRsExecutor()); 200 201 // assign the region first 202 waitOnFuture(submitProcedure(createAssignProcedure(hri))); 203 204 final Future<byte[]> futureA = submitProcedure(createUnassignProcedure(hri)); 205 206 // Wait first unassign. 207 waitOnFuture(futureA); 208 am.getRegionStates().isRegionInState(hri, State.CLOSED); 209 // Second should be a noop. We should recognize region is already CLOSED internally 210 // and skip out doing nothing. 211 final Future<byte[]> futureB = submitProcedure(createUnassignProcedure(hri)); 212 waitOnFuture(futureB); 213 // Ensure we are still CLOSED. 214 am.getRegionStates().isRegionInState(hri, State.CLOSED); 215 // TODO: What else can we do to ensure just a noop. 216 217 assertEquals(assignSubmittedCount + 1, assignProcMetrics.getSubmittedCounter().getCount()); 218 assertEquals(assignFailedCount, assignProcMetrics.getFailedCounter().getCount()); 219 // TODO: Though second unassign is noop, it's considered success, can noop be handled in a 220 // better way? 221 assertEquals(unassignSubmittedCount + 2, unassignProcMetrics.getSubmittedCounter().getCount()); 222 assertEquals(unassignFailedCount, unassignProcMetrics.getFailedCounter().getCount()); 223 } 224 225 /** 226 * It is possible that when AM send assign meta request to a RS successfully, but RS can not send 227 * back any response, which cause master startup hangs forever 228 */ 229 @Test 230 public void testAssignMetaAndCrashBeforeResponse() throws Exception { 231 tearDown(); 232 // See setUp(), start HBase until set up meta 233 util = new HBaseTestingUtil(); 234 this.executor = Executors.newSingleThreadScheduledExecutor(); 235 setupConfiguration(util.getConfiguration()); 236 master = new MockMasterServices(util.getConfiguration()); 237 rsDispatcher = new MockRSProcedureDispatcher(master); 238 master.start(NSERVERS, rsDispatcher); 239 am = master.getAssignmentManager(); 240 241 // Assign meta 242 rsDispatcher.setMockRsExecutor(new HangThenRSRestartExecutor()); 243 am.assign(RegionInfoBuilder.FIRST_META_REGIONINFO); 244 assertEquals(true, am.isMetaAssigned()); 245 246 // set it back as default, see setUpMeta() 247 am.wakeMetaLoadedEvent(); 248 } 249 250 private void assertCloseThenOpen() { 251 assertEquals(closeSubmittedCount + 1, closeProcMetrics.getSubmittedCounter().getCount()); 252 assertEquals(closeFailedCount, closeProcMetrics.getFailedCounter().getCount()); 253 assertEquals(openSubmittedCount + 1, openProcMetrics.getSubmittedCounter().getCount()); 254 assertEquals(openFailedCount, openProcMetrics.getFailedCounter().getCount()); 255 } 256 257 @Test 258 public void testMove() throws Exception { 259 TableName tableName = TableName.valueOf("testMove"); 260 RegionInfo hri = createRegionInfo(tableName, 1); 261 rsDispatcher.setMockRsExecutor(new GoodRsExecutor()); 262 am.assign(hri); 263 264 // collect AM metrics before test 265 collectAssignmentManagerMetrics(); 266 267 am.move(hri); 268 269 assertEquals(moveSubmittedCount + 1, moveProcMetrics.getSubmittedCounter().getCount()); 270 assertEquals(moveFailedCount, moveProcMetrics.getFailedCounter().getCount()); 271 assertCloseThenOpen(); 272 } 273 274 @Test 275 public void testReopen() throws Exception { 276 TableName tableName = TableName.valueOf("testReopen"); 277 RegionInfo hri = createRegionInfo(tableName, 1); 278 rsDispatcher.setMockRsExecutor(new GoodRsExecutor()); 279 am.assign(hri); 280 281 // collect AM metrics before test 282 collectAssignmentManagerMetrics(); 283 284 TransitRegionStateProcedure proc = 285 TransitRegionStateProcedure.reopen(master.getMasterProcedureExecutor().getEnvironment(), hri); 286 am.getRegionStates().getRegionStateNode(hri).setProcedure(proc); 287 waitOnFuture(submitProcedure(proc)); 288 289 assertEquals(reopenSubmittedCount + 1, reopenProcMetrics.getSubmittedCounter().getCount()); 290 assertEquals(reopenFailedCount, reopenProcMetrics.getFailedCounter().getCount()); 291 assertCloseThenOpen(); 292 } 293 294 @Test 295 public void testLoadRegionFromMetaAfterRegionManuallyAdded() throws Exception { 296 try { 297 this.util.startMiniCluster(); 298 final AssignmentManager am = this.util.getHBaseCluster().getMaster().getAssignmentManager(); 299 final TableName tableName = 300 TableName.valueOf("testLoadRegionFromMetaAfterRegionManuallyAdded"); 301 this.util.createTable(tableName, "f"); 302 RegionInfo hri = createRegionInfo(tableName, 1); 303 assertNull("RegionInfo was just instantiated by the test, but " 304 + "shouldn't be in AM regionStates yet.", am.getRegionStates().getRegionState(hri)); 305 MetaTableAccessor.addRegionsToMeta(this.util.getConnection(), Collections.singletonList(hri), 306 1); 307 // TODO: is there a race here -- no other thread else will refresh the table states behind 308 // the scenes? 309 assertNull("RegionInfo was manually added in META, but shouldn't be in AM regionStates yet.", 310 am.getRegionStates().getRegionState(hri)); 311 am.populateRegionStatesFromMeta(hri.getEncodedName()); 312 assertNotNull(am.getRegionInfo(hri.getRegionName())); 313 assertNotNull(am.getRegionInfo(hri.getEncodedName())); 314 } finally { 315 this.util.killMiniHBaseCluster(); 316 } 317 } 318 319 @Test 320 public void testLoadRegionFromMetaRegionNotInMeta() throws Exception { 321 try { 322 this.util.startMiniCluster(); 323 final AssignmentManager am = this.util.getHBaseCluster().getMaster().getAssignmentManager(); 324 final TableName tableName = TableName.valueOf("testLoadRegionFromMetaRegionNotInMeta"); 325 this.util.createTable(tableName, "f"); 326 final RegionInfo hri = createRegionInfo(tableName, 1); 327 assertNull("Bogus RegionInfo discovered in RegionStates.", 328 am.getRegionStates().getRegionState(hri)); 329 am.populateRegionStatesFromMeta(hri.getEncodedName()); 330 assertNull("RegionInfo was never added in META", am.getRegionStates().getRegionState(hri)); 331 } finally { 332 this.util.killMiniHBaseCluster(); 333 } 334 } 335}