Index: src/test/org/apache/hadoop/hbase/client/TestScannerTimeout.java =================================================================== --- src/test/org/apache/hadoop/hbase/client/TestScannerTimeout.java (revision 0) +++ src/test/org/apache/hadoop/hbase/client/TestScannerTimeout.java (revision 0) @@ -0,0 +1,91 @@ +package org.apache.hadoop.hbase.client; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.hbase.HBaseConfiguration; +import org.apache.hadoop.hbase.HBaseTestingUtility; +import org.apache.hadoop.hbase.util.Bytes; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +/** + * Test various scanner timeout issues. + */ +public class TestScannerTimeout { + + private final static HBaseTestingUtility + TEST_UTIL = new HBaseTestingUtility(); + + final Log LOG = LogFactory.getLog(getClass()); + private final byte[] someBytes = Bytes.toBytes("f"); + + /** + * @throws java.lang.Exception + */ + @BeforeClass + public static void setUpBeforeClass() throws Exception { + HBaseConfiguration c = TEST_UTIL.getConfiguration(); + c.setInt("hbase.regionserver.lease.period", 1000); + TEST_UTIL.startMiniCluster(1); + } + + /** + * @throws java.lang.Exception + */ + @AfterClass + public static void tearDownAfterClass() throws Exception { + TEST_UTIL.shutdownMiniCluster(); + } + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + /** + * Test that we do get a ScannerTimeoutException + * @throws Exception + */ + @Test + public void test2481() throws Exception { + int initialCount = 10; + HTable t = TEST_UTIL.createTable(Bytes.toBytes("t"), someBytes); + for (int i = 0; i < initialCount; i++) { + Put put = new Put(Bytes.toBytes(i)); + put.add(someBytes, someBytes, someBytes); + t.put(put); + } + Scan scan = new Scan(); + ResultScanner r = t.getScanner(scan); + int count = 0; + try { + Result res = r.next(); + while (res != null) { + count++; + if (count == 5) { + Thread.sleep(1500); + } + res = r.next(); + } + } catch (ScannerTimeoutException e) { + LOG.info("Got the timeout " + e.getMessage(), e); + return; + } + fail("We should be timing out"); + } +} Index: src/java/org/apache/hadoop/hbase/client/HTable.java =================================================================== --- src/java/org/apache/hadoop/hbase/client/HTable.java (revision 937530) +++ src/java/org/apache/hadoop/hbase/client/HTable.java (working copy) @@ -2022,6 +2022,16 @@ values = getConnection().getRegionServerWithRetries(callable); } } catch (DoNotRetryIOException e) { + long timeout = lastNext + scannerTimeout; + if (e instanceof UnknownScannerException && + timeout < System.currentTimeMillis()) { + long elapsed = System.currentTimeMillis() - lastNext; + ScannerTimeoutException ex = new ScannerTimeoutException( + elapsed + "ms passed since the last invocation, " + + "timeout is currently set to " + scannerTimeout); + ex.initCause(e); + throw ex; + } Throwable cause = e.getCause(); if (cause == null || !(cause instanceof NotServingRegionException)) { throw e; @@ -2037,14 +2047,6 @@ // Clear region this.currentRegion = null; continue; - } catch (IOException e) { - if (e instanceof UnknownScannerException && - lastNext + scannerTimeout < System.currentTimeMillis()) { - ScannerTimeoutException ex = new ScannerTimeoutException(); - ex.initCause(e); - throw ex; - } - throw e; } lastNext = System.currentTimeMillis(); if (values != null && values.length > 0) { Index: src/java/org/apache/hadoop/hbase/client/ScannerCallable.java =================================================================== --- src/java/org/apache/hadoop/hbase/client/ScannerCallable.java (revision 937530) +++ src/java/org/apache/hadoop/hbase/client/ScannerCallable.java (working copy) @@ -81,11 +81,15 @@ if (e instanceof RemoteException) { ioe = RemoteExceptionHandler.decodeRemoteException((RemoteException)e); } - if (ioe != null && ioe instanceof NotServingRegionException) { + if (ioe != null) { + if (ioe instanceof NotServingRegionException) { // Throw a DNRE so that we break out of cycle of calling NSRE // when what we need is to open scanner against new location. // Attach NSRE to signal client that it needs to resetup scanner. throw new DoNotRetryIOException("Reset scanner", ioe); + } else if (ioe instanceof DoNotRetryIOException) { + throw ioe; + } } } return rrs;