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