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.client;
019
020import static org.junit.Assert.assertEquals;
021
022import org.apache.hadoop.conf.Configuration;
023import org.apache.hadoop.hbase.HBaseClassTestRule;
024import org.apache.hadoop.hbase.HBaseTestingUtility;
025import org.apache.hadoop.hbase.HConstants;
026import org.apache.hadoop.hbase.MetaTableAccessor;
027import org.apache.hadoop.hbase.TableName;
028import org.apache.hadoop.hbase.regionserver.HRegionServer;
029import org.apache.hadoop.hbase.testclassification.ClientTests;
030import org.apache.hadoop.hbase.testclassification.LargeTests;
031import org.apache.hadoop.hbase.util.Bytes;
032import org.junit.AfterClass;
033import org.junit.Before;
034import org.junit.BeforeClass;
035import org.junit.ClassRule;
036import org.junit.Test;
037import org.junit.experimental.categories.Category;
038import org.slf4j.Logger;
039import org.slf4j.LoggerFactory;
040
041/**
042 * Test various scanner timeout issues.
043 */
044@Category({LargeTests.class, ClientTests.class})
045public class TestScannerTimeout {
046
047  @ClassRule
048  public static final HBaseClassTestRule CLASS_RULE =
049      HBaseClassTestRule.forClass(TestScannerTimeout.class);
050
051  private final static HBaseTestingUtility
052      TEST_UTIL = new HBaseTestingUtility();
053
054  private static final Logger LOG = LoggerFactory.getLogger(TestScannerTimeout.class);
055  private final static byte[] SOME_BYTES = Bytes.toBytes("f");
056  private final static TableName TABLE_NAME = TableName.valueOf("t");
057  private final static int NB_ROWS = 10;
058  // Be careful w/ what you set this timer to... it can get in the way of
059  // the mini cluster coming up -- the verification in particular.
060  private final static int THREAD_WAKE_FREQUENCY = 1000;
061  private final static int SCANNER_TIMEOUT = 15000;
062  private final static int SCANNER_CACHING = 5;
063
064   /**
065   * @throws java.lang.Exception
066   */
067  @BeforeClass
068  public static void setUpBeforeClass() throws Exception {
069    Configuration c = TEST_UTIL.getConfiguration();
070    c.setInt(HConstants.HBASE_CLIENT_SCANNER_TIMEOUT_PERIOD, SCANNER_TIMEOUT);
071    c.setInt(HConstants.THREAD_WAKE_FREQUENCY, THREAD_WAKE_FREQUENCY);
072    // We need more than one region server for this test
073    TEST_UTIL.startMiniCluster(2);
074    Table table = TEST_UTIL.createTable(TABLE_NAME, SOME_BYTES);
075    for (int i = 0; i < NB_ROWS; i++) {
076      Put put = new Put(Bytes.toBytes(i));
077      put.addColumn(SOME_BYTES, SOME_BYTES, SOME_BYTES);
078      table.put(put);
079    }
080    table.close();
081  }
082
083  /**
084   * @throws java.lang.Exception
085   */
086  @AfterClass
087  public static void tearDownAfterClass() throws Exception {
088    TEST_UTIL.shutdownMiniCluster();
089  }
090
091  /**
092   * @throws java.lang.Exception
093   */
094  @Before
095  public void setUp() throws Exception {
096    TEST_UTIL.ensureSomeNonStoppedRegionServersAvailable(2);
097  }
098
099  /**
100   * Test that scanner can continue even if the region server it was reading
101   * from failed. Before 2772, it reused the same scanner id.
102   * @throws Exception
103   */
104  @Test
105  public void test2772() throws Exception {
106    LOG.info("START************ test2772");
107    HRegionServer rs = TEST_UTIL.getRSForFirstRegionInTable(TABLE_NAME);
108    Scan scan = new Scan();
109    // Set a very high timeout, we want to test what happens when a RS
110    // fails but the region is recovered before the lease times out.
111    // Since the RS is already created, this conf is client-side only for
112    // this new table
113    Configuration conf = new Configuration(TEST_UTIL.getConfiguration());
114    conf.setInt(HConstants.HBASE_CLIENT_SCANNER_TIMEOUT_PERIOD, SCANNER_TIMEOUT * 100);
115
116    Connection connection = ConnectionFactory.createConnection(conf);
117    Table higherScanTimeoutTable = connection.getTable(TABLE_NAME);
118    ResultScanner r = higherScanTimeoutTable.getScanner(scan);
119    // This takes way less than SCANNER_TIMEOUT*100
120    rs.abort("die!");
121    Result[] results = r.next(NB_ROWS);
122    assertEquals(NB_ROWS, results.length);
123    r.close();
124    higherScanTimeoutTable.close();
125    connection.close();
126    LOG.info("END ************ test2772");
127
128  }
129
130  /**
131   * Test that scanner won't miss any rows if the region server it was reading
132   * from failed. Before 3686, it would skip rows in the scan.
133   * @throws Exception
134   */
135  @Test
136  public void test3686a() throws Exception {
137    LOG.info("START ************ TEST3686A---1");
138    HRegionServer rs = TEST_UTIL.getRSForFirstRegionInTable(TABLE_NAME);
139    LOG.info("START ************ TEST3686A---1111");
140
141    Scan scan = new Scan();
142    scan.setCaching(SCANNER_CACHING);
143    LOG.info("************ TEST3686A");
144    MetaTableAccessor.fullScanMetaAndPrint(TEST_UTIL.getAdmin().getConnection());
145    // Set a very high timeout, we want to test what happens when a RS
146    // fails but the region is recovered before the lease times out.
147    // Since the RS is already created, this conf is client-side only for
148    // this new table
149    Configuration conf = new Configuration(TEST_UTIL.getConfiguration());
150    conf.setInt(
151        HConstants.HBASE_CLIENT_SCANNER_TIMEOUT_PERIOD, SCANNER_TIMEOUT*100);
152    Connection connection = ConnectionFactory.createConnection(conf);
153    Table table = connection.getTable(TABLE_NAME);
154    LOG.info("START ************ TEST3686A---22");
155
156    ResultScanner r = table.getScanner(scan);
157    LOG.info("START ************ TEST3686A---33");
158
159    int count = 1;
160    r.next();
161    LOG.info("START ************ TEST3686A---44");
162
163    // Kill after one call to next(), which got 5 rows.
164    rs.abort("die!");
165    while(r.next() != null) {
166      count ++;
167    }
168    assertEquals(NB_ROWS, count);
169    r.close();
170    table.close();
171    connection.close();
172    LOG.info("************ END TEST3686A");
173  }
174
175  /**
176   * Make sure that no rows are lost if the scanner timeout is longer on the
177   * client than the server, and the scan times out on the server but not the
178   * client.
179   * @throws Exception
180   */
181  @Test
182  public void test3686b() throws Exception {
183    LOG.info("START ************ test3686b");
184    HRegionServer rs = TEST_UTIL.getRSForFirstRegionInTable(TABLE_NAME);
185    Scan scan = new Scan();
186    scan.setCaching(SCANNER_CACHING);
187    // Set a very high timeout, we want to test what happens when a RS
188    // fails but the region is recovered before the lease times out.
189    // Since the RS is already created, this conf is client-side only for
190    // this new table
191    Configuration conf = new Configuration(TEST_UTIL.getConfiguration());
192    conf.setInt(HConstants.HBASE_CLIENT_SCANNER_TIMEOUT_PERIOD, SCANNER_TIMEOUT * 100);
193    Connection connection = ConnectionFactory.createConnection(conf);
194    Table higherScanTimeoutTable = connection.getTable(TABLE_NAME);
195    ResultScanner r = higherScanTimeoutTable.getScanner(scan);
196    int count = 1;
197    r.next();
198    // Sleep, allowing the scan to timeout on the server but not on the client.
199    Thread.sleep(SCANNER_TIMEOUT+2000);
200    while(r.next() != null) {
201      count ++;
202    }
203    assertEquals(NB_ROWS, count);
204    r.close();
205    higherScanTimeoutTable.close();
206    connection.close();
207    LOG.info("END ************ END test3686b");
208
209  }
210
211}
212