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