1   /**
2    * Copyright 2010 The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  package org.apache.hadoop.hbase.client;
21  
22  import static org.junit.Assert.assertEquals;
23  import static org.junit.Assert.fail;
24  
25  import org.apache.commons.logging.Log;
26  import org.apache.commons.logging.LogFactory;
27  import org.apache.hadoop.conf.Configuration;
28  import org.apache.hadoop.hbase.HBaseTestingUtility;
29  import org.apache.hadoop.hbase.HConstants;
30  import org.apache.hadoop.hbase.LargeTests;
31  import org.apache.hadoop.hbase.catalog.MetaReader;
32  import org.apache.hadoop.hbase.regionserver.HRegionServer;
33  import org.apache.hadoop.hbase.util.Bytes;
34  import org.junit.AfterClass;
35  import org.junit.Before;
36  import org.junit.BeforeClass;
37  import org.junit.Test;
38  import org.junit.experimental.categories.Category;
39  
40  /**
41   * Test various scanner timeout issues.
42   */
43  @Category(LargeTests.class)
44  public class TestScannerTimeout {
45  
46    private final static HBaseTestingUtility
47        TEST_UTIL = new HBaseTestingUtility();
48  
49    final Log LOG = LogFactory.getLog(getClass());
50    private final static byte[] SOME_BYTES = Bytes.toBytes("f");
51    private final static byte[] TABLE_NAME = Bytes.toBytes("t");
52    private final static int NB_ROWS = 10;
53    // Be careful w/ what you set this timer too... it can get in the way of
54    // the mini cluster coming up -- the verification in particular.
55    private final static int SCANNER_TIMEOUT = 15000;
56    private final static int SCANNER_CACHING = 5;
57  
58     /**
59     * @throws java.lang.Exception
60     */
61    @BeforeClass
62    public static void setUpBeforeClass() throws Exception {
63      Configuration c = TEST_UTIL.getConfiguration();
64      c.setInt("hbase.regionserver.lease.period", SCANNER_TIMEOUT);
65      // We need more than one region server for this test
66      TEST_UTIL.startMiniCluster(2);
67      HTable table = TEST_UTIL.createTable(TABLE_NAME, SOME_BYTES);
68       for (int i = 0; i < NB_ROWS; i++) {
69        Put put = new Put(Bytes.toBytes(i));
70        put.add(SOME_BYTES, SOME_BYTES, SOME_BYTES);
71        table.put(put);
72      }
73      table.close();
74    }
75  
76    /**
77     * @throws java.lang.Exception
78     */
79    @AfterClass
80    public static void tearDownAfterClass() throws Exception {
81      TEST_UTIL.shutdownMiniCluster();
82    }
83  
84    /**
85     * @throws java.lang.Exception
86     */
87    @Before
88    public void setUp() throws Exception {
89      TEST_UTIL.ensureSomeNonStoppedRegionServersAvailable(2);
90    }
91  
92    /**
93     * Test that we do get a ScannerTimeoutException
94     * @throws Exception
95     */
96    @Test(timeout=300000)
97    public void test2481() throws Exception {
98      LOG.info("START ************ test2481");
99      Scan scan = new Scan();
100     HTable table =
101       new HTable(new Configuration(TEST_UTIL.getConfiguration()), TABLE_NAME);
102     ResultScanner r = table.getScanner(scan);
103     int count = 0;
104     try {
105       Result res = r.next();
106       while (res != null) {
107         count++;
108         if (count == 5) {
109           // Sleep just a bit more to be sure
110           Thread.sleep(SCANNER_TIMEOUT+100);
111         }
112         res = r.next();
113       }
114     } catch (ScannerTimeoutException e) {
115       LOG.info("Got the timeout " + e.getMessage(), e);
116       return;
117     }  finally {
118       table.close();
119     }
120     fail("We should be timing out");
121     LOG.info("END ************ test2481");
122   }
123 
124   /**
125    * Test that scanner can continue even if the region server it was reading
126    * from failed. Before 2772, it reused the same scanner id.
127    * @throws Exception
128    */
129   @Test(timeout=300000)
130   public void test2772() throws Exception {
131     LOG.info("START************ test2772");
132     HRegionServer rs = TEST_UTIL.getRSForFirstRegionInTable(TABLE_NAME);
133     Scan scan = new Scan();
134     // Set a very high timeout, we want to test what happens when a RS
135     // fails but the region is recovered before the lease times out.
136     // Since the RS is already created, this conf is client-side only for
137     // this new table
138     Configuration conf = new Configuration(TEST_UTIL.getConfiguration());
139     conf.setInt(
140         HConstants.HBASE_REGIONSERVER_LEASE_PERIOD_KEY, SCANNER_TIMEOUT*100);
141     HTable higherScanTimeoutTable = new HTable(conf, TABLE_NAME);
142     ResultScanner r = higherScanTimeoutTable.getScanner(scan);
143     // This takes way less than SCANNER_TIMEOUT*100
144     rs.abort("die!");
145     Result[] results = r.next(NB_ROWS);
146     assertEquals(NB_ROWS, results.length);
147     r.close();
148     higherScanTimeoutTable.close();
149     LOG.info("END ************ test2772");
150 
151   }
152 
153   /**
154    * Test that scanner won't miss any rows if the region server it was reading
155    * from failed. Before 3686, it would skip rows in the scan.
156    * @throws Exception
157    */
158   @Test(timeout=300000)
159   public void test3686a() throws Exception {
160     LOG.info("START ************ TEST3686A---1");
161     HRegionServer rs = TEST_UTIL.getRSForFirstRegionInTable(TABLE_NAME);
162     LOG.info("START ************ TEST3686A---1111");
163 
164     Scan scan = new Scan();
165     scan.setCaching(SCANNER_CACHING);
166     LOG.info("************ TEST3686A");
167     MetaReader.fullScanMetaAndPrint(TEST_UTIL.getHBaseCluster().getMaster().getCatalogTracker());
168     // Set a very high timeout, we want to test what happens when a RS
169     // fails but the region is recovered before the lease times out.
170     // Since the RS is already created, this conf is client-side only for
171     // this new table
172     Configuration conf = new Configuration(TEST_UTIL.getConfiguration());
173     conf.setInt(
174         HConstants.HBASE_REGIONSERVER_LEASE_PERIOD_KEY, SCANNER_TIMEOUT*100);
175     HTable table = new HTable(conf, TABLE_NAME);
176     LOG.info("START ************ TEST3686A---22");
177 
178     ResultScanner r = table.getScanner(scan);
179     LOG.info("START ************ TEST3686A---33");
180 
181     int count = 1;
182     r.next();
183     LOG.info("START ************ TEST3686A---44");
184 
185     // Kill after one call to next(), which got 5 rows.
186     rs.abort("die!");
187     while(r.next() != null) {
188       count ++;
189     }
190     assertEquals(NB_ROWS, count);
191     r.close();
192     table.close();
193     LOG.info("************ END TEST3686A");
194   }
195 
196   /**
197    * Make sure that no rows are lost if the scanner timeout is longer on the
198    * client than the server, and the scan times out on the server but not the
199    * client.
200    * @throws Exception
201    */
202   @Test(timeout=300000)
203   public void test3686b() throws Exception {
204     LOG.info("START ************ test3686b");
205     HRegionServer rs = TEST_UTIL.getRSForFirstRegionInTable(TABLE_NAME);
206     Scan scan = new Scan();
207     scan.setCaching(SCANNER_CACHING);
208     // Set a very high timeout, we want to test what happens when a RS
209     // fails but the region is recovered before the lease times out.
210     // Since the RS is already created, this conf is client-side only for
211     // this new table
212     Configuration conf = new Configuration(TEST_UTIL.getConfiguration());
213     conf.setInt(
214         HConstants.HBASE_REGIONSERVER_LEASE_PERIOD_KEY, SCANNER_TIMEOUT*100);
215     HTable higherScanTimeoutTable = new HTable(conf, TABLE_NAME);
216     ResultScanner r = higherScanTimeoutTable.getScanner(scan);
217     int count = 1;
218     r.next();
219     // Sleep, allowing the scan to timeout on the server but not on the client.
220     Thread.sleep(SCANNER_TIMEOUT+2000);
221     while(r.next() != null) {
222       count ++;
223     }
224     assertEquals(NB_ROWS, count);
225     r.close();
226     higherScanTimeoutTable.close();
227     LOG.info("END ************ END test3686b");
228 
229   }
230 
231   @org.junit.Rule
232   public org.apache.hadoop.hbase.ResourceCheckerJUnitRule cu =
233     new org.apache.hadoop.hbase.ResourceCheckerJUnitRule();
234 }
235