1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.apache.hadoop.hbase.coprocessor;
22
23 import static org.mockito.Mockito.when;
24
25 import java.io.IOException;
26 import java.util.ArrayList;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.concurrent.ConcurrentMap;
30
31 import org.apache.commons.logging.Log;
32 import org.apache.commons.logging.LogFactory;
33 import org.apache.hadoop.conf.Configuration;
34 import org.apache.hadoop.fs.Path;
35 import org.apache.hadoop.hbase.*;
36 import org.apache.hadoop.hbase.client.Get;
37 import org.apache.hadoop.hbase.client.Scan;
38 import org.apache.hadoop.hbase.regionserver.InternalScanner;
39 import org.apache.hadoop.hbase.regionserver.RegionCoprocessorHost;
40 import org.apache.hadoop.hbase.regionserver.HRegion;
41 import org.apache.hadoop.hbase.regionserver.RegionScanner;
42 import org.apache.hadoop.hbase.regionserver.SplitTransaction;
43 import org.apache.hadoop.hbase.regionserver.Store;
44 import org.apache.hadoop.hbase.regionserver.StoreFile;
45 import org.apache.hadoop.hbase.util.Bytes;
46 import org.apache.hadoop.hbase.util.PairOfSameType;
47 import org.junit.experimental.categories.Category;
48 import org.mockito.Mockito;
49
50 @Category(SmallTests.class)
51 public class TestCoprocessorInterface extends HBaseTestCase {
52 static final Log LOG = LogFactory.getLog(TestCoprocessorInterface.class);
53 private static final HBaseTestingUtility TEST_UTIL =
54 new HBaseTestingUtility();
55 static final Path DIR = TEST_UTIL.getDataTestDir();
56
57 private static class CustomScanner implements RegionScanner {
58
59 private RegionScanner delegate;
60
61 public CustomScanner(RegionScanner delegate) {
62 this.delegate = delegate;
63 }
64
65 @Override
66 public boolean next(List<KeyValue> results) throws IOException {
67 return delegate.next(results);
68 }
69
70 @Override
71 public boolean next(List<KeyValue> results, String metric)
72 throws IOException {
73 return delegate.next(results, metric);
74 }
75
76 @Override
77 public boolean next(List<KeyValue> result, int limit) throws IOException {
78 return delegate.next(result, limit);
79 }
80
81 @Override
82 public boolean next(List<KeyValue> result, int limit, String metric)
83 throws IOException {
84 return delegate.next(result, limit, metric);
85 }
86
87 @Override
88 public boolean nextRaw(List<KeyValue> result, int limit, String metric)
89 throws IOException {
90 return delegate.nextRaw(result, limit, metric);
91 }
92
93 @Override
94 public boolean nextRaw(List<KeyValue> result, String metric)
95 throws IOException {
96 return delegate.nextRaw(result, metric);
97 }
98
99 @Override
100 public void close() throws IOException {
101 delegate.close();
102 }
103
104 @Override
105 public HRegionInfo getRegionInfo() {
106 return delegate.getRegionInfo();
107 }
108
109 @Override
110 public boolean isFilterDone() {
111 return delegate.isFilterDone();
112 }
113
114 @Override
115 public boolean reseek(byte[] row) throws IOException {
116 return false;
117 }
118
119 @Override
120 public long getMvccReadPoint() {
121 return delegate.getMvccReadPoint();
122 }
123 }
124
125 public static class CoprocessorImpl extends BaseRegionObserver {
126
127 private boolean startCalled;
128 private boolean stopCalled;
129 private boolean preOpenCalled;
130 private boolean postOpenCalled;
131 private boolean preCloseCalled;
132 private boolean postCloseCalled;
133 private boolean preCompactCalled;
134 private boolean postCompactCalled;
135 private boolean preFlushCalled;
136 private boolean postFlushCalled;
137 private boolean preSplitCalled;
138 private boolean postSplitCalled;
139 private ConcurrentMap<String, Object> sharedData;
140
141 @Override
142 public void start(CoprocessorEnvironment e) {
143 sharedData = ((RegionCoprocessorEnvironment)e).getSharedData();
144
145 sharedData.putIfAbsent("test1", new Object());
146 startCalled = true;
147 }
148
149 @Override
150 public void stop(CoprocessorEnvironment e) {
151 sharedData = null;
152 stopCalled = true;
153 }
154
155 @Override
156 public void preOpen(ObserverContext<RegionCoprocessorEnvironment> e) {
157 preOpenCalled = true;
158 }
159 @Override
160 public void postOpen(ObserverContext<RegionCoprocessorEnvironment> e) {
161 postOpenCalled = true;
162 }
163 @Override
164 public void preClose(ObserverContext<RegionCoprocessorEnvironment> e, boolean abortRequested) {
165 preCloseCalled = true;
166 }
167 @Override
168 public void postClose(ObserverContext<RegionCoprocessorEnvironment> e, boolean abortRequested) {
169 postCloseCalled = true;
170 }
171 @Override
172 public InternalScanner preCompact(ObserverContext<RegionCoprocessorEnvironment> e,
173 Store store, InternalScanner scanner) {
174 preCompactCalled = true;
175 return scanner;
176 }
177 @Override
178 public void postCompact(ObserverContext<RegionCoprocessorEnvironment> e,
179 Store store, StoreFile resultFile) {
180 postCompactCalled = true;
181 }
182 @Override
183 public void preFlush(ObserverContext<RegionCoprocessorEnvironment> e) {
184 preFlushCalled = true;
185 }
186 @Override
187 public void postFlush(ObserverContext<RegionCoprocessorEnvironment> e) {
188 postFlushCalled = true;
189 }
190 @Override
191 public void preSplit(ObserverContext<RegionCoprocessorEnvironment> e) {
192 preSplitCalled = true;
193 }
194 @Override
195 public void postSplit(ObserverContext<RegionCoprocessorEnvironment> e, HRegion l, HRegion r) {
196 postSplitCalled = true;
197 }
198
199 @Override
200 public RegionScanner postScannerOpen(final ObserverContext<RegionCoprocessorEnvironment> e,
201 final Scan scan, final RegionScanner s) throws IOException {
202 return new CustomScanner(s);
203 }
204
205 boolean wasStarted() {
206 return startCalled;
207 }
208 boolean wasStopped() {
209 return stopCalled;
210 }
211 boolean wasOpened() {
212 return (preOpenCalled && postOpenCalled);
213 }
214 boolean wasClosed() {
215 return (preCloseCalled && postCloseCalled);
216 }
217 boolean wasFlushed() {
218 return (preFlushCalled && postFlushCalled);
219 }
220 boolean wasCompacted() {
221 return (preCompactCalled && postCompactCalled);
222 }
223 boolean wasSplit() {
224 return (preSplitCalled && postSplitCalled);
225 }
226 Map<String, Object> getSharedData() {
227 return sharedData;
228 }
229 }
230
231 public static class CoprocessorII extends BaseRegionObserver {
232 private ConcurrentMap<String, Object> sharedData;
233 @Override
234 public void start(CoprocessorEnvironment e) {
235 sharedData = ((RegionCoprocessorEnvironment)e).getSharedData();
236 sharedData.putIfAbsent("test2", new Object());
237 }
238 @Override
239 public void stop(CoprocessorEnvironment e) {
240 sharedData = null;
241 }
242 @Override
243 public void preGet(final ObserverContext<RegionCoprocessorEnvironment> e,
244 final Get get, final List<KeyValue> results) throws IOException {
245 if (1/0 == 1) {
246 e.complete();
247 }
248 }
249
250 Map<String, Object> getSharedData() {
251 return sharedData;
252 }
253 }
254
255 public void testSharedData() throws IOException {
256 byte [] tableName = Bytes.toBytes("testtable");
257 byte [][] families = { fam1, fam2, fam3 };
258
259 Configuration hc = initSplit();
260 HRegion region = initHRegion(tableName, getName(), hc,
261 new Class<?>[]{}, families);
262
263 for (int i = 0; i < 3; i++) {
264 addContent(region, fam3);
265 region.flushcache();
266 }
267
268 region.compactStores();
269
270 byte [] splitRow = region.checkSplit();
271
272 assertNotNull(splitRow);
273 HRegion [] regions = split(region, splitRow);
274 for (int i = 0; i < regions.length; i++) {
275 regions[i] = reopenRegion(regions[i], CoprocessorImpl.class, CoprocessorII.class);
276 }
277 Coprocessor c = regions[0].getCoprocessorHost().
278 findCoprocessor(CoprocessorImpl.class.getName());
279 Coprocessor c2 = regions[0].getCoprocessorHost().
280 findCoprocessor(CoprocessorII.class.getName());
281 Object o = ((CoprocessorImpl)c).getSharedData().get("test1");
282 Object o2 = ((CoprocessorII)c2).getSharedData().get("test2");
283 assertNotNull(o);
284 assertNotNull(o2);
285
286 assertFalse(((CoprocessorImpl)c).getSharedData() == ((CoprocessorII)c2).getSharedData());
287 for (int i = 1; i < regions.length; i++) {
288 c = regions[i].getCoprocessorHost().
289 findCoprocessor(CoprocessorImpl.class.getName());
290 c2 = regions[i].getCoprocessorHost().
291 findCoprocessor(CoprocessorII.class.getName());
292
293 assertTrue(((CoprocessorImpl)c).getSharedData().get("test1") == o);
294 assertTrue(((CoprocessorII)c2).getSharedData().get("test2") == o2);
295 }
296
297 for (int i = 0; i < regions.length; i++) {
298 try {
299 Get g = new Get(regions[i].getStartKey());
300 regions[i].get(g, null);
301 fail();
302 } catch (DoNotRetryIOException xc) {
303 }
304 assertNull(regions[i].getCoprocessorHost().
305 findCoprocessor(CoprocessorII.class.getName()));
306 }
307 c = regions[0].getCoprocessorHost().
308 findCoprocessor(CoprocessorImpl.class.getName());
309 assertTrue(((CoprocessorImpl)c).getSharedData().get("test1") == o);
310 c = c2 = null;
311
312 System.gc();
313
314 region = reopenRegion(regions[0], CoprocessorImpl.class, CoprocessorII.class);
315 c = region.getCoprocessorHost().
316 findCoprocessor(CoprocessorImpl.class.getName());
317
318 assertTrue(((CoprocessorImpl)c).getSharedData().get("test1") == o);
319 c2 = region.getCoprocessorHost().
320 findCoprocessor(CoprocessorII.class.getName());
321
322
323 assertFalse(((CoprocessorII)c2).getSharedData().get("test2") == o2);
324 }
325
326 public void testCoprocessorInterface() throws IOException {
327 byte [] tableName = Bytes.toBytes("testtable");
328 byte [][] families = { fam1, fam2, fam3 };
329
330 Configuration hc = initSplit();
331 HRegion region = initHRegion(tableName, getName(), hc,
332 new Class<?>[]{CoprocessorImpl.class}, families);
333 for (int i = 0; i < 3; i++) {
334 addContent(region, fam3);
335 region.flushcache();
336 }
337
338 region.compactStores();
339
340 byte [] splitRow = region.checkSplit();
341
342 assertNotNull(splitRow);
343 HRegion [] regions = split(region, splitRow);
344 for (int i = 0; i < regions.length; i++) {
345 regions[i] = reopenRegion(regions[i], CoprocessorImpl.class);
346 }
347 region.close();
348 region.getLog().closeAndDelete();
349 Coprocessor c = region.getCoprocessorHost().
350 findCoprocessor(CoprocessorImpl.class.getName());
351
352
353 Scan s = new Scan();
354 RegionScanner scanner = regions[0].getCoprocessorHost().postScannerOpen(s, regions[0].getScanner(s));
355 assertTrue(scanner instanceof CustomScanner);
356
357 scanner.next(new ArrayList<KeyValue>());
358
359 assertTrue("Coprocessor not started", ((CoprocessorImpl)c).wasStarted());
360 assertTrue("Coprocessor not stopped", ((CoprocessorImpl)c).wasStopped());
361 assertTrue(((CoprocessorImpl)c).wasOpened());
362 assertTrue(((CoprocessorImpl)c).wasClosed());
363 assertTrue(((CoprocessorImpl)c).wasFlushed());
364 assertTrue(((CoprocessorImpl)c).wasCompacted());
365 assertTrue(((CoprocessorImpl)c).wasSplit());
366
367 for (int i = 0; i < regions.length; i++) {
368 regions[i].close();
369 regions[i].getLog().closeAndDelete();
370 c = region.getCoprocessorHost()
371 .findCoprocessor(CoprocessorImpl.class.getName());
372 assertTrue("Coprocessor not started", ((CoprocessorImpl)c).wasStarted());
373 assertTrue("Coprocessor not stopped", ((CoprocessorImpl)c).wasStopped());
374 assertTrue(((CoprocessorImpl)c).wasOpened());
375 assertTrue(((CoprocessorImpl)c).wasClosed());
376 assertTrue(((CoprocessorImpl)c).wasCompacted());
377 }
378 }
379
380 HRegion reopenRegion(final HRegion closedRegion, Class<?> ... implClasses)
381 throws IOException {
382
383 HRegion r = new HRegion(closedRegion);
384 r.initialize();
385
386
387
388
389
390 RegionCoprocessorHost host = new RegionCoprocessorHost(r, null, conf);
391 r.setCoprocessorHost(host);
392
393 for (Class<?> implClass : implClasses) {
394 host.load(implClass, Coprocessor.PRIORITY_USER, conf);
395 }
396
397
398
399
400
401
402 host.preOpen();
403 host.postOpen();
404 return r;
405 }
406
407 HRegion initHRegion (byte [] tableName, String callingMethod,
408 Configuration conf, Class<?> [] implClasses, byte [][] families)
409 throws IOException {
410 HTableDescriptor htd = new HTableDescriptor(tableName);
411 for(byte [] family : families) {
412 htd.addFamily(new HColumnDescriptor(family));
413 }
414 HRegionInfo info = new HRegionInfo(tableName, null, null, false);
415 Path path = new Path(DIR + callingMethod);
416 HRegion r = HRegion.createHRegion(info, path, conf, htd);
417
418
419 RegionCoprocessorHost host = new RegionCoprocessorHost(r, null, conf);
420 r.setCoprocessorHost(host);
421
422 for (Class<?> implClass : implClasses) {
423 host.load(implClass, Coprocessor.PRIORITY_USER, conf);
424 Coprocessor c = host.findCoprocessor(implClass.getName());
425 assertNotNull(c);
426 }
427
428
429 host.preOpen();
430 host.postOpen();
431 return r;
432 }
433
434 Configuration initSplit() {
435
436 TEST_UTIL.getConfiguration().setInt("hbase.hstore.compactionThreshold", 2);
437
438 TEST_UTIL.getConfiguration().setInt(
439 "hbase.master.lease.thread.wakefrequency", 5 * 1000);
440 TEST_UTIL.getConfiguration().setInt(
441 "hbase.regionserver.lease.period", 10 * 1000);
442
443 TEST_UTIL.getConfiguration().setLong("hbase.client.pause", 15 * 1000);
444
445
446 TEST_UTIL.getConfiguration().setLong(HConstants.HREGION_MAX_FILESIZE,
447 1024 * 128);
448 TEST_UTIL.getConfiguration().setBoolean("hbase.testing.nocluster",
449 true);
450
451 return TEST_UTIL.getConfiguration();
452 }
453
454 private HRegion [] split(final HRegion r, final byte [] splitRow)
455 throws IOException {
456
457 HRegion[] regions = new HRegion[2];
458
459 SplitTransaction st = new SplitTransaction(r, splitRow);
460 int i = 0;
461
462 if (!st.prepare()) {
463
464 assertTrue(false);
465 }
466 try {
467 Server mockServer = Mockito.mock(Server.class);
468 when(mockServer.getConfiguration()).thenReturn(
469 TEST_UTIL.getConfiguration());
470 PairOfSameType<HRegion> daughters = st.execute(mockServer, null);
471 for (HRegion each_daughter: daughters) {
472 regions[i] = each_daughter;
473 i++;
474 }
475 } catch (IOException ioe) {
476 LOG.info("Split transaction of " + r.getRegionNameAsString() +
477 " failed:" + ioe.getMessage());
478 assertTrue(false);
479 } catch (RuntimeException e) {
480 LOG.info("Failed rollback of failed split of " +
481 r.getRegionNameAsString() + e.getMessage());
482 }
483
484 assertTrue(i == 2);
485 return regions;
486 }
487
488 @org.junit.Rule
489 public org.apache.hadoop.hbase.ResourceCheckerJUnitRule cu =
490 new org.apache.hadoop.hbase.ResourceCheckerJUnitRule();
491 }
492
493
494