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.regionserver;
019
020import static org.junit.Assert.assertTrue;
021import static org.junit.Assert.fail;
022
023import java.io.IOException;
024import java.util.Optional;
025import java.util.TimerTask;
026
027import org.apache.hadoop.conf.Configuration;
028import org.apache.hadoop.hbase.HBaseClassTestRule;
029import org.apache.hadoop.hbase.HBaseTestingUtility;
030import org.apache.hadoop.hbase.TableName;
031import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder;
032import org.apache.hadoop.hbase.client.Put;
033import org.apache.hadoop.hbase.client.Table;
034import org.apache.hadoop.hbase.client.TableDescriptor;
035import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
036import org.apache.hadoop.hbase.coprocessor.ObserverContext;
037import org.apache.hadoop.hbase.coprocessor.RegionCoprocessor;
038import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
039import org.apache.hadoop.hbase.coprocessor.RegionObserver;
040import org.apache.hadoop.hbase.master.procedure.ServerCrashProcedure;
041import org.apache.hadoop.hbase.testclassification.MediumTests;
042import org.apache.hadoop.hbase.testclassification.RegionServerTests;
043import org.apache.hadoop.hbase.util.Bytes;
044import org.apache.hadoop.hbase.util.Threads;
045import org.junit.AfterClass;
046import org.junit.BeforeClass;
047import org.junit.ClassRule;
048import org.junit.Test;
049import org.junit.experimental.categories.Category;
050import org.slf4j.Logger;
051import org.slf4j.LoggerFactory;
052
053@Category({ RegionServerTests.class, MediumTests.class })
054public class TestRegionServerAbortTimeout {
055
056  @ClassRule
057  public static final HBaseClassTestRule CLASS_RULE =
058      HBaseClassTestRule.forClass(TestRegionServerAbortTimeout.class);
059
060  private static final Logger LOG = LoggerFactory.getLogger(TestRegionServerAbortTimeout.class);
061
062  private static final HBaseTestingUtility UTIL = new HBaseTestingUtility();
063
064  private static TableName TABLE_NAME = TableName.valueOf("RSAbort");
065
066  private static byte[] CF = Bytes.toBytes("cf");
067
068  private static byte[] CQ = Bytes.toBytes("cq");
069
070  private static final int REGIONS_NUM = 5;
071
072  private static final int SLEEP_TIME_WHEN_CLOSE_REGION = 1000;
073
074  private static volatile boolean abortTimeoutTaskScheduled = false;
075
076  @BeforeClass
077  public static void setUp() throws Exception {
078    Configuration conf = UTIL.getConfiguration();
079    // Will schedule a abort timeout task after SLEEP_TIME_WHEN_CLOSE_REGION ms
080    conf.setLong(HRegionServer.ABORT_TIMEOUT, SLEEP_TIME_WHEN_CLOSE_REGION);
081    conf.set(HRegionServer.ABORT_TIMEOUT_TASK, TestAbortTimeoutTask.class.getName());
082    UTIL.startMiniCluster(2);
083    TableDescriptor td = TableDescriptorBuilder.newBuilder(TABLE_NAME)
084        .setCoprocessor(SleepWhenCloseCoprocessor.class.getName())
085        .setColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder(CF).build()).build();
086    UTIL.getAdmin().createTable(td, Bytes.toBytes("0"), Bytes.toBytes("9"), REGIONS_NUM);
087  }
088
089  @AfterClass
090  public static void tearDown() throws Exception {
091    // Wait the SCP of abort rs to finish
092    UTIL.waitFor(30000, () -> UTIL.getMiniHBaseCluster().getMaster().getProcedures().stream()
093        .filter(p -> p instanceof ServerCrashProcedure && p.isFinished()).count() > 0);
094    UTIL.getAdmin().disableTable(TABLE_NAME);
095    UTIL.getAdmin().deleteTable(TABLE_NAME);
096    UTIL.shutdownMiniCluster();
097  }
098
099  @Test
100  public void testAbortTimeout() throws Exception {
101    Thread writer = new Thread(() -> {
102      try {
103        try (Table table = UTIL.getConnection().getTable(TABLE_NAME)) {
104          for (int i = 0; i < 10000; i++) {
105            table.put(new Put(Bytes.toBytes(i)).addColumn(CF, CQ, Bytes.toBytes(i)));
106          }
107        }
108      } catch (IOException e) {
109        LOG.warn("Failed to load data");
110      }
111    });
112    writer.setDaemon(true);
113    writer.start();
114
115    // Abort one region server
116    UTIL.getMiniHBaseCluster().getRegionServer(0).abort("Abort RS for test");
117
118    long startTime = System.currentTimeMillis();
119    long timeout = REGIONS_NUM * SLEEP_TIME_WHEN_CLOSE_REGION * 10;
120    while (System.currentTimeMillis() - startTime < timeout) {
121      if (UTIL.getMiniHBaseCluster().getLiveRegionServerThreads().size() == 1) {
122        assertTrue("Abort timer task should be scheduled", abortTimeoutTaskScheduled);
123        return;
124      }
125      Threads.sleep(SLEEP_TIME_WHEN_CLOSE_REGION);
126    }
127    fail("Failed to abort a region server in " + timeout + " ms");
128  }
129
130  static class TestAbortTimeoutTask extends TimerTask {
131
132    public TestAbortTimeoutTask() {
133    }
134
135    @Override
136    public void run() {
137      LOG.info("TestAbortTimeoutTask was scheduled");
138      abortTimeoutTaskScheduled = true;
139    }
140  }
141
142  public static class SleepWhenCloseCoprocessor implements RegionCoprocessor, RegionObserver {
143
144    public SleepWhenCloseCoprocessor() {
145    }
146
147    @Override
148    public Optional<RegionObserver> getRegionObserver() {
149      return Optional.of(this);
150    }
151
152    @Override
153    public void preClose(ObserverContext<RegionCoprocessorEnvironment> c, boolean abortRequested)
154        throws IOException {
155      Threads.sleep(SLEEP_TIME_WHEN_CLOSE_REGION);
156    }
157  }
158}