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.catalog.MetaReader;
31  import org.apache.hadoop.hbase.regionserver.HRegionServer;
32  import org.apache.hadoop.hbase.util.Bytes;
33  import org.junit.AfterClass;
34  import org.junit.Before;
35  import org.junit.BeforeClass;
36  import org.junit.Test;
37  
38  /**
39   * Test various scanner timeout issues.
40   */
41  public class TestScannerTimeout {
42  
43    private final static HBaseTestingUtility
44        TEST_UTIL = new HBaseTestingUtility();
45  
46    final Log LOG = LogFactory.getLog(getClass());
47    private final static byte[] SOME_BYTES = Bytes.toBytes("f");
48    private final static byte[] TABLE_NAME = Bytes.toBytes("t");
49    private final static int NB_ROWS = 10;
50    // Be careful w/ what you set this timer too... it can get in the way of
51    // the mini cluster coming up -- the verification in particular.
52    private final static int SCANNER_TIMEOUT = 10000;
53    private final static int SCANNER_CACHING = 5;
54  
55     /**
56     * @throws java.lang.Exception
57     */
58    @BeforeClass
59    public static void setUpBeforeClass() throws Exception {
60      Configuration c = TEST_UTIL.getConfiguration();
61      c.setInt("hbase.regionserver.lease.period", SCANNER_TIMEOUT);
62      // We need more than one region server for this test
63      TEST_UTIL.startMiniCluster(2);
64      HTable table = TEST_UTIL.createTable(TABLE_NAME, SOME_BYTES);
65       for (int i = 0; i < NB_ROWS; i++) {
66        Put put = new Put(Bytes.toBytes(i));
67        put.add(SOME_BYTES, SOME_BYTES, SOME_BYTES);
68        table.put(put);
69      }
70    }
71  
72    /**
73     * @throws java.lang.Exception
74     */
75    @AfterClass
76    public static void tearDownAfterClass() throws Exception {
77      TEST_UTIL.shutdownMiniCluster();
78    }
79  
80    /**
81     * @throws java.lang.Exception
82     */
83    @Before
84    public void setUp() throws Exception {
85      TEST_UTIL.ensureSomeRegionServersAvailable(2);
86    }
87  
88    /**
89     * Test that we do get a ScannerTimeoutException
90     * @throws Exception
91     */
92    @Test(timeout=300000)
93    public void test2481() throws Exception {
94      LOG.info("START ************ test2481");
95      Scan scan = new Scan();
96      HTable table =
97        new HTable(new Configuration(TEST_UTIL.getConfiguration()), TABLE_NAME);
98      ResultScanner r = table.getScanner(scan);
99      int count = 0;
100     try {
101       Result res = r.next();
102       while (res != null) {
103         count++;
104         if (count == 5) {
105           // Sleep just a bit more to be sure
106           Thread.sleep(SCANNER_TIMEOUT+100);
107         }
108         res = r.next();
109       }
110     } catch (ScannerTimeoutException e) {
111       LOG.info("Got the timeout " + e.getMessage(), e);
112       return;
113     }
114     fail("We should be timing out");
115     LOG.info("END ************ test2481");
116   }
117 
118   /**
119    * Test that scanner can continue even if the region server it was reading
120    * from failed. Before 2772, it reused the same scanner id.
121    * @throws Exception
122    */
123   @Test(timeout=300000)
124   public void test2772() throws Exception {
125     LOG.info("START************ test2772");
126     HRegionServer rs = TEST_UTIL.getRSForFirstRegionInTable(TABLE_NAME);
127     Scan scan = new Scan();
128     // Set a very high timeout, we want to test what happens when a RS
129     // fails but the region is recovered before the lease times out.
130     // Since the RS is already created, this conf is client-side only for
131     // this new table
132     Configuration conf = new Configuration(TEST_UTIL.getConfiguration());
133     conf.setInt(
134         HConstants.HBASE_REGIONSERVER_LEASE_PERIOD_KEY, SCANNER_TIMEOUT*100);
135     HTable higherScanTimeoutTable = new HTable(conf, TABLE_NAME);
136     ResultScanner r = higherScanTimeoutTable.getScanner(scan);
137     // This takes way less than SCANNER_TIMEOUT*100
138     rs.abort("die!");
139     Result[] results = r.next(NB_ROWS);
140     assertEquals(NB_ROWS, results.length);
141     r.close();
142     LOG.info("END ************ test2772");
143 
144   }
145   
146   /**
147    * Test that scanner won't miss any rows if the region server it was reading
148    * from failed. Before 3686, it would skip rows in the scan.
149    * @throws Exception
150    */
151   @Test(timeout=300000)
152   public void test3686a() throws Exception {
153     LOG.info("START ************ TEST3686A---1");
154     HRegionServer rs = TEST_UTIL.getRSForFirstRegionInTable(TABLE_NAME);
155     LOG.info("START ************ TEST3686A---1111");
156 
157     Scan scan = new Scan();
158     scan.setCaching(SCANNER_CACHING);
159     LOG.info("************ TEST3686A");
160     MetaReader.fullScanMetaAndPrint(TEST_UTIL.getHBaseCluster().getMaster().getCatalogTracker());
161     HTable table = new HTable(TEST_UTIL.getConfiguration(), TABLE_NAME);
162     LOG.info("START ************ TEST3686A---22");
163 
164     ResultScanner r = table.getScanner(scan);
165     LOG.info("START ************ TEST3686A---33");
166 
167     int count = 1;
168     r.next();
169     LOG.info("START ************ TEST3686A---44");
170 
171     // Kill after one call to next(), which got 5 rows.
172     rs.abort("die!");
173     while(r.next() != null) {
174       count ++;
175     }
176     assertEquals(NB_ROWS, count);
177     r.close();
178     LOG.info("************ END TEST3686A");
179   }
180   
181   /**
182    * Make sure that no rows are lost if the scanner timeout is longer on the
183    * client than the server, and the scan times out on the server but not the
184    * client.
185    * @throws Exception
186    */
187   @Test(timeout=300000)
188   public void test3686b() throws Exception {
189     LOG.info("START ************ test3686b");
190     HRegionServer rs = TEST_UTIL.getRSForFirstRegionInTable(TABLE_NAME);
191     Scan scan = new Scan();
192     scan.setCaching(SCANNER_CACHING);
193     // Set a very high timeout, we want to test what happens when a RS
194     // fails but the region is recovered before the lease times out.
195     // Since the RS is already created, this conf is client-side only for
196     // this new table
197     Configuration conf = new Configuration(TEST_UTIL.getConfiguration());
198     conf.setInt(
199         HConstants.HBASE_REGIONSERVER_LEASE_PERIOD_KEY, SCANNER_TIMEOUT*100);
200     HTable higherScanTimeoutTable = new HTable(conf, TABLE_NAME);
201     ResultScanner r = higherScanTimeoutTable.getScanner(scan);
202     int count = 1;
203     r.next();
204     // Sleep, allowing the scan to timeout on the server but not on the client.
205     Thread.sleep(SCANNER_TIMEOUT+2000);
206     while(r.next() != null) {
207       count ++;
208     }
209     assertEquals(NB_ROWS, count);
210     r.close();
211     LOG.info("END ************ END test3686b");
212 
213   }
214 }