2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
18 package org
.apache
.hadoop
.hbase
.client
;
20 import static org
.hamcrest
.CoreMatchers
.instanceOf
;
21 import static org
.junit
.Assert
.assertEquals
;
22 import static org
.junit
.Assert
.assertThat
;
23 import static org
.junit
.Assert
.assertTrue
;
24 import static org
.junit
.Assert
.fail
;
26 import java
.io
.IOException
;
27 import java
.util
.ArrayList
;
28 import java
.util
.List
;
29 import java
.util
.NavigableSet
;
30 import java
.util
.concurrent
.atomic
.AtomicBoolean
;
31 import java
.util
.concurrent
.atomic
.AtomicLong
;
32 import org
.apache
.hadoop
.conf
.Configuration
;
33 import org
.apache
.hadoop
.fs
.FileSystem
;
34 import org
.apache
.hadoop
.fs
.Path
;
35 import org
.apache
.hadoop
.hbase
.Cell
;
36 import org
.apache
.hadoop
.hbase
.DoNotRetryIOException
;
37 import org
.apache
.hadoop
.hbase
.HBaseClassTestRule
;
38 import org
.apache
.hadoop
.hbase
.HBaseTestingUtility
;
39 import org
.apache
.hadoop
.hbase
.HConstants
;
40 import org
.apache
.hadoop
.hbase
.TableName
;
41 import org
.apache
.hadoop
.hbase
.exceptions
.ScannerResetException
;
42 import org
.apache
.hadoop
.hbase
.regionserver
.DelegatingKeyValueScanner
;
43 import org
.apache
.hadoop
.hbase
.regionserver
.HRegion
;
44 import org
.apache
.hadoop
.hbase
.regionserver
.HStore
;
45 import org
.apache
.hadoop
.hbase
.regionserver
.KeyValueScanner
;
46 import org
.apache
.hadoop
.hbase
.regionserver
.RegionServerServices
;
47 import org
.apache
.hadoop
.hbase
.regionserver
.ReversedStoreScanner
;
48 import org
.apache
.hadoop
.hbase
.regionserver
.ScanInfo
;
49 import org
.apache
.hadoop
.hbase
.regionserver
.StoreScanner
;
50 import org
.apache
.hadoop
.hbase
.testclassification
.ClientTests
;
51 import org
.apache
.hadoop
.hbase
.testclassification
.MediumTests
;
52 import org
.apache
.hadoop
.hbase
.util
.Bytes
;
53 import org
.apache
.hadoop
.hbase
.wal
.WAL
;
54 import org
.junit
.AfterClass
;
55 import org
.junit
.BeforeClass
;
56 import org
.junit
.ClassRule
;
57 import org
.junit
.Rule
;
58 import org
.junit
.Test
;
59 import org
.junit
.experimental
.categories
.Category
;
60 import org
.junit
.rules
.TestName
;
62 @Category({ MediumTests
.class, ClientTests
.class })
63 public class TestFromClientSideScanExcpetion
{
66 public static final HBaseClassTestRule CLASS_RULE
=
67 HBaseClassTestRule
.forClass(TestFromClientSideScanExcpetion
.class);
69 protected final static HBaseTestingUtility TEST_UTIL
= new HBaseTestingUtility();
71 private static byte[] FAMILY
= Bytes
.toBytes("testFamily");
73 private static int SLAVES
= 3;
76 public TestName name
= new TestName();
79 public static void setUpBeforeClass() throws Exception
{
80 Configuration conf
= TEST_UTIL
.getConfiguration();
81 conf
.setLong(HConstants
.HBASE_CLIENT_SCANNER_TIMEOUT_PERIOD
, 6000000);
82 conf
.setClass(HConstants
.REGION_IMPL
, MyHRegion
.class, HRegion
.class);
83 conf
.setBoolean("hbase.client.log.scanner.activity", true);
84 // We need more than one region server in this test
85 TEST_UTIL
.startMiniCluster(SLAVES
);
89 public static void tearDownAfterClass() throws Exception
{
90 TEST_UTIL
.shutdownMiniCluster();
93 private static AtomicBoolean ON
= new AtomicBoolean(false);
94 private static AtomicLong REQ_COUNT
= new AtomicLong(0);
95 private static AtomicBoolean IS_DO_NOT_RETRY
= new AtomicBoolean(false); // whether to throw
97 private static AtomicBoolean THROW_ONCE
= new AtomicBoolean(true); // whether to only throw once
99 private static void reset() {
102 IS_DO_NOT_RETRY
.set(false);
103 THROW_ONCE
.set(true);
106 private static void inject() {
110 public static final class MyHRegion
extends HRegion
{
112 @SuppressWarnings("deprecation")
113 public MyHRegion(Path tableDir
, WAL wal
, FileSystem fs
, Configuration confParam
,
114 RegionInfo regionInfo
, TableDescriptor htd
, RegionServerServices rsServices
) {
115 super(tableDir
, wal
, fs
, confParam
, regionInfo
, htd
, rsServices
);
119 protected HStore
instantiateHStore(ColumnFamilyDescriptor family
) throws IOException
{
120 return new MyHStore(this, family
, conf
);
124 public static final class MyHStore
extends HStore
{
126 public MyHStore(HRegion region
, ColumnFamilyDescriptor family
, Configuration confParam
)
128 super(region
, family
, confParam
);
132 protected KeyValueScanner
createScanner(Scan scan
, ScanInfo scanInfo
,
133 NavigableSet
<byte[]> targetCols
, long readPt
) throws IOException
{
134 return scan
.isReversed() ?
new ReversedStoreScanner(this, scanInfo
, scan
, targetCols
, readPt
)
135 : new MyStoreScanner(this, scanInfo
, scan
, targetCols
, readPt
);
139 public static final class MyStoreScanner
extends StoreScanner
{
140 public MyStoreScanner(HStore store
, ScanInfo scanInfo
, Scan scan
, NavigableSet
<byte[]> columns
,
141 long readPt
) throws IOException
{
142 super(store
, scanInfo
, scan
, columns
, readPt
);
146 protected List
<KeyValueScanner
> selectScannersFrom(HStore store
,
147 List
<?
extends KeyValueScanner
> allScanners
) {
148 List
<KeyValueScanner
> scanners
= super.selectScannersFrom(store
, allScanners
);
149 List
<KeyValueScanner
> newScanners
= new ArrayList
<>(scanners
.size());
150 for (KeyValueScanner scanner
: scanners
) {
151 newScanners
.add(new DelegatingKeyValueScanner(scanner
) {
153 public boolean reseek(Cell key
) throws IOException
{
155 REQ_COUNT
.incrementAndGet();
156 if (!THROW_ONCE
.get() || REQ_COUNT
.get() == 1) {
157 if (IS_DO_NOT_RETRY
.get()) {
158 throw new DoNotRetryIOException("Injected exception");
160 throw new IOException("Injected exception");
164 return super.reseek(key
);
173 * Tests the case where a Scan can throw an IOException in the middle of the seek / reseek leaving
174 * the server side RegionScanner to be in dirty state. The client has to ensure that the
175 * ClientScanner does not get an exception and also sees all the data.
176 * @throws IOException
177 * @throws InterruptedException
180 public void testClientScannerIsResetWhenScanThrowsIOException()
181 throws IOException
, InterruptedException
{
183 THROW_ONCE
.set(true); // throw exceptions only once
184 TableName tableName
= TableName
.valueOf(name
.getMethodName());
185 try (Table t
= TEST_UTIL
.createTable(tableName
, FAMILY
)) {
186 int rowCount
= TEST_UTIL
.loadTable(t
, FAMILY
, false);
187 TEST_UTIL
.getAdmin().flush(tableName
);
189 int actualRowCount
= TEST_UTIL
.countRows(t
, new Scan().addColumn(FAMILY
, FAMILY
));
190 assertEquals(rowCount
, actualRowCount
);
192 assertTrue(REQ_COUNT
.get() > 0);
196 * Tests the case where a coprocessor throws a DoNotRetryIOException in the scan. The expectation
197 * is that the exception will bubble up to the client scanner instead of being retried.
200 public void testScannerThrowsExceptionWhenCoprocessorThrowsDNRIOE()
201 throws IOException
, InterruptedException
{
203 IS_DO_NOT_RETRY
.set(true);
204 TableName tableName
= TableName
.valueOf(name
.getMethodName());
205 try (Table t
= TEST_UTIL
.createTable(tableName
, FAMILY
)) {
206 TEST_UTIL
.loadTable(t
, FAMILY
, false);
207 TEST_UTIL
.getAdmin().flush(tableName
);
209 TEST_UTIL
.countRows(t
, new Scan().addColumn(FAMILY
, FAMILY
));
210 fail("Should have thrown an exception");
211 } catch (DoNotRetryIOException expected
) {
214 assertTrue(REQ_COUNT
.get() > 0);
218 * Tests the case where a coprocessor throws a regular IOException in the scan. The expectation is
219 * that the we will keep on retrying, but fail after the retries are exhausted instead of retrying
223 public void testScannerFailsAfterRetriesWhenCoprocessorThrowsIOE()
224 throws IOException
, InterruptedException
{
225 TEST_UTIL
.getConfiguration().setInt(HConstants
.HBASE_CLIENT_RETRIES_NUMBER
, 3);
226 TableName tableName
= TableName
.valueOf(name
.getMethodName());
228 THROW_ONCE
.set(false); // throw exceptions in every retry
229 try (Table t
= TEST_UTIL
.createTable(tableName
, FAMILY
)) {
230 TEST_UTIL
.loadTable(t
, FAMILY
, false);
231 TEST_UTIL
.getAdmin().flush(tableName
);
233 TEST_UTIL
.countRows(t
, new Scan().addColumn(FAMILY
, FAMILY
));
234 fail("Should have thrown an exception");
235 } catch (DoNotRetryIOException expected
) {
236 assertThat(expected
, instanceOf(ScannerResetException
.class));
239 assertTrue(REQ_COUNT
.get() >= 3);