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