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