1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.regionserver;
20
21 import static org.junit.Assert.assertTrue;
22
23 import java.io.IOException;
24 import java.io.InterruptedIOException;
25 import java.util.concurrent.atomic.AtomicBoolean;
26
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29 import org.apache.hadoop.conf.Configuration;
30 import org.apache.hadoop.fs.FileSystem;
31 import org.apache.hadoop.fs.Path;
32 import org.apache.hadoop.hbase.Cell;
33 import org.apache.hadoop.hbase.HBaseTestingUtility;
34 import org.apache.hadoop.hbase.HColumnDescriptor;
35 import org.apache.hadoop.hbase.HConstants;
36 import org.apache.hadoop.hbase.HRegionInfo;
37 import org.apache.hadoop.hbase.HTableDescriptor;
38 import org.apache.hadoop.hbase.NotServingRegionException;
39 import org.apache.hadoop.hbase.TableName;
40 import org.apache.hadoop.hbase.TableNameTestRule;
41 import org.apache.hadoop.hbase.Waiter;
42 import org.apache.hadoop.hbase.client.Admin;
43 import org.apache.hadoop.hbase.client.Append;
44 import org.apache.hadoop.hbase.client.BufferedMutator;
45 import org.apache.hadoop.hbase.client.Delete;
46 import org.apache.hadoop.hbase.client.Durability;
47 import org.apache.hadoop.hbase.client.Increment;
48 import org.apache.hadoop.hbase.client.Put;
49 import org.apache.hadoop.hbase.client.Result;
50 import org.apache.hadoop.hbase.client.ResultScanner;
51 import org.apache.hadoop.hbase.client.Scan;
52 import org.apache.hadoop.hbase.client.Table;
53 import org.apache.hadoop.hbase.coprocessor.BaseRegionObserver;
54 import org.apache.hadoop.hbase.coprocessor.ObserverContext;
55 import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
56 import org.apache.hadoop.hbase.exceptions.DeserializationException;
57 import org.apache.hadoop.hbase.filter.FilterBase;
58 import org.apache.hadoop.hbase.regionserver.wal.WALEdit;
59 import org.apache.hadoop.hbase.testclassification.LargeTests;
60 import org.apache.hadoop.hbase.testclassification.RegionServerTests;
61 import org.apache.hadoop.hbase.util.Bytes;
62 import org.apache.hadoop.hbase.wal.WAL;
63 import org.junit.After;
64 import org.junit.Before;
65 import org.junit.BeforeClass;
66 import org.junit.Rule;
67 import org.junit.Test;
68 import org.junit.experimental.categories.Category;
69
70 @Category({RegionServerTests.class, LargeTests.class})
71 public class TestRegionInterrupt {
72
73 private static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
74 private static final Log LOG = LogFactory.getLog(TestRegionInterrupt.class);
75
76 static final byte[] FAMILY = Bytes.toBytes("info");
77
78 static long sleepTime;
79
80 @Rule
81 public TableNameTestRule name = new TableNameTestRule();
82
83 @BeforeClass
84 public static void setUpBeforeClass() throws Exception {
85 Configuration conf = TEST_UTIL.getConfiguration();
86 conf.setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, 1);
87 conf.setClass(HConstants.REGION_IMPL, InterruptInterceptingHRegion.class, Region.class);
88 conf.setBoolean(HRegion.CLOSE_WAIT_ABORT, true);
89
90 long waitInterval = conf.getLong(HRegion.CLOSE_WAIT_INTERVAL,
91 HRegion.DEFAULT_CLOSE_WAIT_INTERVAL);
92 sleepTime = waitInterval * 2;
93
94 conf.setLong(HRegion.CLOSE_WAIT_TIME, sleepTime * 2);
95 }
96
97 @Before
98 public void setUp() throws Exception {
99 TEST_UTIL.startMiniCluster();
100 }
101
102 @After
103 public void tearDown() throws Exception {
104 TEST_UTIL.shutdownMiniCluster();
105 }
106
107 @Test
108 public void testCloseInterruptScanning() throws Exception {
109 final TableName tableName = name.getTableName();
110 LOG.info("Creating table " + tableName);
111 try (Table table = TEST_UTIL.createTable(tableName, FAMILY)) {
112
113 TEST_UTIL.waitUntilAllRegionsAssigned(tableName);
114 TEST_UTIL.loadTable(table, FAMILY);
115 final AtomicBoolean expectedExceptionCaught = new AtomicBoolean(false);
116
117 Thread scanner = new Thread(new Runnable() {
118 @Override
119 public void run() {
120 Scan scan = new Scan();
121 scan.addFamily(FAMILY);
122 scan.setFilter(new DelayingFilter());
123 try {
124 LOG.info("Starting scan");
125 try (ResultScanner rs = table.getScanner(scan)) {
126 Result r;
127 do {
128 r = rs.next();
129 if (r != null) {
130 LOG.info("Scanned row " + Bytes.toStringBinary(r.getRow()));
131 }
132 } while (r != null);
133 }
134 } catch (IOException e) {
135 LOG.info("Scanner caught exception", e);
136 expectedExceptionCaught.set(true);
137 } finally {
138 LOG.info("Finished scan");
139 }
140 }
141 });
142 scanner.start();
143
144
145 LOG.info("Waiting for scanner to start");
146 Waiter.waitFor(TEST_UTIL.getConfiguration(), 10*1000, new Waiter.Predicate<Exception>() {
147 @Override
148 public boolean evaluate() throws Exception {
149 return DelayingFilter.isSleeping();
150 }
151 });
152
153
154 LOG.info("Offlining table " + tableName);
155 TEST_UTIL.getHBaseAdmin().disableTable(tableName);
156
157
158 scanner.join();
159
160
161 assertTrue("Region operations were not interrupted",
162 InterruptInterceptingHRegion.wasInterrupted());
163 assertTrue("Scanner did not catch expected exception", expectedExceptionCaught.get());
164 }
165 }
166
167 @Test
168 public void testCloseInterruptMutation() throws Exception {
169 final TableName tableName = name.getTableName();
170 final Admin admin = TEST_UTIL.getHBaseAdmin();
171
172 HTableDescriptor htd = new HTableDescriptor(tableName);
173 htd.addFamily(new HColumnDescriptor(FAMILY));
174 htd.addCoprocessor(MutationDelayingCoprocessor.class.getName());
175 LOG.info("Creating table " + tableName);
176 admin.createTable(htd);
177 TEST_UTIL.waitUntilAllRegionsAssigned(tableName);
178
179
180 LOG.info("Starting writes to table " + tableName);
181 final int NUM_ROWS = 100;
182 final AtomicBoolean expectedExceptionCaught = new AtomicBoolean(false);
183 Thread inserter = new Thread(new Runnable() {
184 @Override
185 public void run() {
186 try (BufferedMutator t = admin.getConnection().getBufferedMutator(tableName)) {
187 for (int i = 0; i < NUM_ROWS; i++) {
188 LOG.info("Writing row " + i + " to " + tableName);
189 byte[] value = new byte[10], row = Bytes.toBytes(Integer.toString(i));
190 Bytes.random(value);
191 t.mutate(new Put(row).addColumn(FAMILY, HConstants.EMPTY_BYTE_ARRAY, value));
192 t.flush();
193 }
194 } catch (IOException e) {
195 LOG.info("Inserter caught exception", e);
196 expectedExceptionCaught.set(true);
197 }
198 }
199 });
200 inserter.start();
201
202
203 LOG.info("Waiting for mutations to start");
204 Waiter.waitFor(TEST_UTIL.getConfiguration(), 10*1000, new Waiter.Predicate<Exception>() {
205 @Override
206 public boolean evaluate() throws Exception {
207 return MutationDelayingCoprocessor.isSleeping();
208 }
209 });
210
211
212 LOG.info("Offlining table " + tableName);
213 admin.disableTable(tableName);
214
215
216 inserter.join();
217
218
219 assertTrue("Region operations were not interrupted",
220 InterruptInterceptingHRegion.wasInterrupted());
221 assertTrue("Inserter did not catch expected exception", expectedExceptionCaught.get());
222 }
223
224 public static class InterruptInterceptingHRegion extends HRegion {
225
226 private static boolean interrupted = false;
227
228 public static boolean wasInterrupted() {
229 return interrupted;
230 }
231
232 public InterruptInterceptingHRegion(Path tableDir, WAL wal, FileSystem fs,
233 Configuration conf, HRegionInfo regionInfo, HTableDescriptor htd,
234 RegionServerServices rsServices) {
235 super(tableDir, wal, fs, conf, regionInfo, htd, rsServices);
236 }
237
238 public InterruptInterceptingHRegion(HRegionFileSystem fs, WAL wal, Configuration conf,
239 HTableDescriptor htd, RegionServerServices rsServices) {
240 super(fs, wal, conf, htd, rsServices);
241 }
242
243 @Override
244 void checkInterrupt() throws NotServingRegionException, InterruptedIOException {
245 try {
246 super.checkInterrupt();
247 } catch (NotServingRegionException | InterruptedIOException e) {
248 interrupted = true;
249 throw e;
250 }
251 }
252
253 @Override
254 IOException throwOnInterrupt(Throwable t) {
255 interrupted = true;
256 return super.throwOnInterrupt(t);
257 }
258
259 }
260
261 public static class DelayingFilter extends FilterBase {
262
263 static volatile boolean sleeping = false;
264
265 public static boolean isSleeping() {
266 return sleeping;
267 }
268
269 @Override
270 public ReturnCode filterKeyValue(Cell v) throws IOException {
271 LOG.info("Starting sleep on " + v);
272 sleeping = true;
273 try {
274 Thread.sleep(sleepTime);
275 } catch (InterruptedException e) {
276
277 Thread.currentThread().interrupt();
278 LOG.info("Interrupted during sleep on " + v);
279 } finally {
280 LOG.info("Done sleep on " + v);
281 sleeping = false;
282 }
283 return ReturnCode.INCLUDE;
284 }
285
286 public static DelayingFilter parseFrom(final byte [] pbBytes)
287 throws DeserializationException {
288
289 return new DelayingFilter();
290 }
291
292 }
293
294 public static class MutationDelayingCoprocessor extends BaseRegionObserver {
295
296 static volatile boolean sleeping = false;
297
298 public static boolean isSleeping() {
299 return sleeping;
300 }
301
302 private void doSleep(Region.Operation op) {
303 LOG.info("Starting sleep for " + op);
304 sleeping = true;
305 try {
306 Thread.sleep(sleepTime);
307 } catch (InterruptedException e) {
308
309 Thread.currentThread().interrupt();
310 LOG.info("Interrupted during " + op);
311 } finally {
312 LOG.info("Done");
313 sleeping = false;
314 }
315 }
316
317 @Override
318 public void prePut(ObserverContext<RegionCoprocessorEnvironment> c, Put put, WALEdit edit,
319 Durability durability) throws IOException {
320 doSleep(Region.Operation.PUT);
321 super.prePut(c, put, edit, durability);
322 }
323
324 @Override
325 public void preDelete(ObserverContext<RegionCoprocessorEnvironment> c, Delete delete,
326 WALEdit edit, Durability durability) throws IOException {
327 doSleep(Region.Operation.DELETE);
328 super.preDelete(c, delete, edit, durability);
329 }
330
331 @Override
332 public Result preAppend(ObserverContext<RegionCoprocessorEnvironment> c, Append append)
333 throws IOException {
334 doSleep(Region.Operation.APPEND);
335 return super.preAppend(c, append);
336 }
337
338 @Override
339 public Result preIncrement(ObserverContext<RegionCoprocessorEnvironment> c, Increment increment)
340 throws IOException {
341 doSleep(Region.Operation.INCREMENT);
342 return super.preIncrement(c, increment);
343 }
344
345 }
346
347 }