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