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