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.junit.Assert.assertEquals;
23 import static org.junit.Assert.assertNull;
24
25 import java.io.IOException;
26 import java.util.Collections;
27 import java.util.List;
28 import java.util.NavigableSet;
29 import java.util.concurrent.CountDownLatch;
30
31 import org.apache.hadoop.conf.Configuration;
32 import org.apache.hadoop.fs.FileSystem;
33 import org.apache.hadoop.fs.Path;
34 import org.apache.hadoop.hbase.Cell;
35 import org.apache.hadoop.hbase.Coprocessor;
36 import org.apache.hadoop.hbase.HBaseTestingUtility;
37 import org.apache.hadoop.hbase.HColumnDescriptor;
38 import org.apache.hadoop.hbase.HConstants;
39 import org.apache.hadoop.hbase.HRegionInfo;
40 import org.apache.hadoop.hbase.HTableDescriptor;
41 import org.apache.hadoop.hbase.TableName;
42 import org.apache.hadoop.hbase.client.Admin;
43 import org.apache.hadoop.hbase.client.Get;
44 import org.apache.hadoop.hbase.client.HTable;
45 import org.apache.hadoop.hbase.client.IsolationLevel;
46 import org.apache.hadoop.hbase.client.Put;
47 import org.apache.hadoop.hbase.client.Result;
48 import org.apache.hadoop.hbase.client.Scan;
49 import org.apache.hadoop.hbase.client.Table;
50 import org.apache.hadoop.hbase.filter.FilterBase;
51 import org.apache.hadoop.hbase.regionserver.HRegion;
52 import org.apache.hadoop.hbase.regionserver.HRegionServer;
53 import org.apache.hadoop.hbase.regionserver.HStore;
54 import org.apache.hadoop.hbase.regionserver.InternalScanner;
55 import org.apache.hadoop.hbase.regionserver.KeyValueScanner;
56 import org.apache.hadoop.hbase.regionserver.Region;
57 import org.apache.hadoop.hbase.regionserver.RegionCoprocessorHost;
58 import org.apache.hadoop.hbase.regionserver.RegionServerServices;
59 import org.apache.hadoop.hbase.regionserver.ScanType;
60 import org.apache.hadoop.hbase.regionserver.Store;
61 import org.apache.hadoop.hbase.regionserver.StoreScanner;
62 import org.apache.hadoop.hbase.regionserver.compactions.CompactionContext;
63 import org.apache.hadoop.hbase.regionserver.throttle.ThroughputController;
64 import org.apache.hadoop.hbase.security.User;
65 import org.apache.hadoop.hbase.testclassification.MediumTests;
66 import org.apache.hadoop.hbase.util.Bytes;
67 import org.apache.hadoop.hbase.wal.WAL;
68 import org.junit.Test;
69 import org.junit.experimental.categories.Category;
70
71 @Category(MediumTests.class)
72 public class TestRegionObserverScannerOpenHook {
73 private static HBaseTestingUtility UTIL = new HBaseTestingUtility();
74 static final Path DIR = UTIL.getDataTestDir();
75
76 public static class NoDataFilter extends FilterBase {
77
78 @Override
79 public ReturnCode filterKeyValue(Cell ignored) throws IOException {
80 return ReturnCode.SKIP;
81 }
82
83 @Override
84 public boolean filterAllRemaining() throws IOException {
85 return true;
86 }
87
88 @Override
89 public boolean filterRow() throws IOException {
90 return true;
91 }
92 }
93
94
95
96
97
98 public static class EmptyRegionObsever extends BaseRegionObserver {
99 }
100
101
102
103
104 public static class NoDataFromScan extends BaseRegionObserver {
105 @Override
106 public KeyValueScanner preStoreScannerOpen(ObserverContext<RegionCoprocessorEnvironment> c,
107 Store store, Scan scan, NavigableSet<byte[]> targetCols, KeyValueScanner s)
108 throws IOException {
109 scan.setFilter(new NoDataFilter());
110 return new StoreScanner(store, store.getScanInfo(), scan, targetCols,
111 ((HStore)store).getHRegion().getReadpoint(IsolationLevel.READ_COMMITTED));
112 }
113 }
114
115
116
117
118 public static class NoDataFromFlush extends BaseRegionObserver {
119 @Override
120 public InternalScanner preFlushScannerOpen(ObserverContext<RegionCoprocessorEnvironment> c,
121 Store store, KeyValueScanner memstoreScanner, InternalScanner s) throws IOException {
122 Scan scan = new Scan();
123 scan.setFilter(new NoDataFilter());
124 return new StoreScanner(store, store.getScanInfo(), scan,
125 Collections.singletonList(memstoreScanner), ScanType.COMPACT_RETAIN_DELETES,
126 store.getSmallestReadPoint(), HConstants.OLDEST_TIMESTAMP);
127 }
128 }
129
130
131
132
133
134 public static class NoDataFromCompaction extends BaseRegionObserver {
135 @Override
136 public InternalScanner preCompactScannerOpen(ObserverContext<RegionCoprocessorEnvironment> c,
137 Store store, List<? extends KeyValueScanner> scanners, ScanType scanType,
138 long earliestPutTs, InternalScanner s) throws IOException {
139 Scan scan = new Scan();
140 scan.setFilter(new NoDataFilter());
141 return new StoreScanner(store, store.getScanInfo(), scan, scanners,
142 ScanType.COMPACT_RETAIN_DELETES, store.getSmallestReadPoint(),
143 HConstants.OLDEST_TIMESTAMP);
144 }
145 }
146
147 Region initHRegion(byte[] tableName, String callingMethod, Configuration conf,
148 byte[]... families) throws IOException {
149 HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(tableName));
150 for (byte[] family : families) {
151 htd.addFamily(new HColumnDescriptor(family));
152 }
153 HRegionInfo info = new HRegionInfo(htd.getTableName(), null, null, false);
154 Path path = new Path(DIR + callingMethod);
155 HRegion r = HRegion.createHRegion(info, path, conf, htd);
156
157
158
159
160 RegionCoprocessorHost host = new RegionCoprocessorHost(r, null, conf);
161 r.setCoprocessorHost(host);
162 return r;
163 }
164
165 @Test
166 public void testRegionObserverScanTimeStacking() throws Exception {
167 byte[] ROW = Bytes.toBytes("testRow");
168 byte[] TABLE = Bytes.toBytes(getClass().getName());
169 byte[] A = Bytes.toBytes("A");
170 byte[][] FAMILIES = new byte[][] { A };
171
172
173 Configuration conf = new HBaseTestingUtility().getConfiguration();
174 Region region = initHRegion(TABLE, getClass().getName(), conf, FAMILIES);
175 RegionCoprocessorHost h = region.getCoprocessorHost();
176 h.load(NoDataFromScan.class, Coprocessor.PRIORITY_HIGHEST, conf);
177 h.load(EmptyRegionObsever.class, Coprocessor.PRIORITY_USER, conf);
178
179 Put put = new Put(ROW);
180 put.add(A, A, A);
181 region.put(put);
182
183 Get get = new Get(ROW);
184 Result r = region.get(get);
185 assertNull(
186 "Got an unexpected number of rows - no data should be returned with the NoDataFromScan coprocessor. Found: "
187 + r, r.listCells());
188 }
189
190 @Test
191 public void testRegionObserverFlushTimeStacking() throws Exception {
192 byte[] ROW = Bytes.toBytes("testRow");
193 byte[] TABLE = Bytes.toBytes(getClass().getName());
194 byte[] A = Bytes.toBytes("A");
195 byte[][] FAMILIES = new byte[][] { A };
196
197
198 Configuration conf = new HBaseTestingUtility().getConfiguration();
199 Region region = initHRegion(TABLE, getClass().getName(), conf, FAMILIES);
200 RegionCoprocessorHost h = region.getCoprocessorHost();
201 h.load(NoDataFromFlush.class, Coprocessor.PRIORITY_HIGHEST, conf);
202 h.load(EmptyRegionObsever.class, Coprocessor.PRIORITY_USER, conf);
203
204
205 Put put = new Put(ROW);
206 put.add(A, A, A);
207 region.put(put);
208 region.flush(true);
209 Get get = new Get(ROW);
210 Result r = region.get(get);
211 assertNull(
212 "Got an unexpected number of rows - no data should be returned with the NoDataFromScan coprocessor. Found: "
213 + r, r.listCells());
214 }
215
216
217
218
219 public static class CompactionCompletionNotifyingRegion extends HRegion {
220 private static volatile CountDownLatch compactionStateChangeLatch = null;
221
222 @SuppressWarnings("deprecation")
223 public CompactionCompletionNotifyingRegion(Path tableDir, WAL log,
224 FileSystem fs, Configuration confParam, HRegionInfo info,
225 HTableDescriptor htd, RegionServerServices rsServices) {
226 super(tableDir, log, fs, confParam, info, htd, rsServices);
227 }
228
229 public CountDownLatch getCompactionStateChangeLatch() {
230 if (compactionStateChangeLatch == null) compactionStateChangeLatch = new CountDownLatch(1);
231 return compactionStateChangeLatch;
232 }
233
234 @Override
235 public boolean compact(CompactionContext compaction, Store store,
236 ThroughputController throughputController) throws IOException {
237 boolean ret = super.compact(compaction, store, throughputController);
238 if (ret) compactionStateChangeLatch.countDown();
239 return ret;
240 }
241
242 @Override
243 public boolean compact(CompactionContext compaction, Store store,
244 ThroughputController throughputController, User user) throws IOException {
245 boolean ret = super.compact(compaction, store, throughputController, user);
246 if (ret) compactionStateChangeLatch.countDown();
247 return ret;
248 }
249 }
250
251
252
253
254
255
256 @Test
257 public void testRegionObserverCompactionTimeStacking() throws Exception {
258
259 Configuration conf = UTIL.getConfiguration();
260 conf.setClass(HConstants.REGION_IMPL, CompactionCompletionNotifyingRegion.class, HRegion.class);
261 conf.setInt("hbase.hstore.compaction.min", 2);
262 UTIL.startMiniCluster();
263 String tableName = "testRegionObserverCompactionTimeStacking";
264 byte[] ROW = Bytes.toBytes("testRow");
265 byte[] A = Bytes.toBytes("A");
266 HTableDescriptor desc = new HTableDescriptor(TableName.valueOf(tableName));
267 desc.addFamily(new HColumnDescriptor(A));
268 desc.addCoprocessor(EmptyRegionObsever.class.getName(), null, Coprocessor.PRIORITY_USER, null);
269 desc.addCoprocessor(NoDataFromCompaction.class.getName(), null, Coprocessor.PRIORITY_HIGHEST,
270 null);
271
272 Admin admin = UTIL.getHBaseAdmin();
273 admin.createTable(desc);
274
275 Table table = new HTable(conf, desc.getTableName());
276
277
278 Put put = new Put(ROW);
279 put.add(A, A, A);
280 table.put(put);
281
282 HRegionServer rs = UTIL.getRSForFirstRegionInTable(desc.getTableName());
283 List<Region> regions = rs.getOnlineRegions(desc.getTableName());
284 assertEquals("More than 1 region serving test table with 1 row", 1, regions.size());
285 Region region = regions.get(0);
286 admin.flushRegion(region.getRegionInfo().getRegionName());
287 CountDownLatch latch = ((CompactionCompletionNotifyingRegion)region)
288 .getCompactionStateChangeLatch();
289
290
291 put = new Put(Bytes.toBytes("anotherrow"));
292 put.add(A, A, A);
293 table.put(put);
294 admin.flushRegion(region.getRegionInfo().getRegionName());
295
296
297
298 latch.await();
299
300 Get get = new Get(ROW);
301 Result r = table.get(get);
302 assertNull(
303 "Got an unexpected number of rows - no data should be returned with the NoDataFromScan coprocessor. Found: "
304 + r, r.listCells());
305
306 get = new Get(Bytes.toBytes("anotherrow"));
307 r = table.get(get);
308 assertNull(
309 "Got an unexpected number of rows - no data should be returned with the NoDataFromScan coprocessor Found: "
310 + r, r.listCells());
311
312 table.close();
313 UTIL.shutdownMiniCluster();
314 }
315 }