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