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 org.apache.commons.logging.Log;
23 import org.apache.commons.logging.LogFactory;
24 import org.apache.hadoop.conf.Configuration;
25 import org.apache.hadoop.fs.FileSystem;
26 import org.apache.hadoop.fs.Path;
27 import org.apache.hadoop.hbase.*;
28 import org.apache.hadoop.hbase.regionserver.HRegion;
29 import org.apache.hadoop.hbase.client.Put;
30 import org.apache.hadoop.hbase.regionserver.wal.HLog;
31 import org.apache.hadoop.hbase.regionserver.wal.HLogFactory;
32 import org.apache.hadoop.hbase.regionserver.wal.HLogSplitter;
33 import org.apache.hadoop.hbase.regionserver.wal.WALCoprocessorHost;
34 import org.apache.hadoop.hbase.regionserver.wal.WALEdit;
35 import org.apache.hadoop.hbase.security.User;
36 import org.apache.hadoop.hbase.testclassification.MediumTests;
37 import org.apache.hadoop.hbase.util.Bytes;
38 import org.apache.hadoop.hbase.util.FSUtils;
39 import org.apache.hadoop.hbase.util.EnvironmentEdge;
40 import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
41 import org.junit.After;
42 import org.junit.AfterClass;
43 import org.junit.Before;
44 import org.junit.BeforeClass;
45 import org.junit.Test;
46 import org.junit.experimental.categories.Category;
47
48 import java.io.IOException;
49 import java.security.PrivilegedExceptionAction;
50 import java.util.Arrays;
51 import java.util.List;
52 import java.util.Map;
53 import java.util.concurrent.atomic.AtomicLong;
54
55 import static org.junit.Assert.*;
56
57
58
59
60
61
62 @Category(MediumTests.class)
63 public class TestWALObserver {
64 private static final Log LOG = LogFactory.getLog(TestWALObserver.class);
65 private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
66
67 private static byte[] TEST_TABLE = Bytes.toBytes("observedTable");
68 private static byte[][] TEST_FAMILY = { Bytes.toBytes("fam1"),
69 Bytes.toBytes("fam2"), Bytes.toBytes("fam3"), };
70 private static byte[][] TEST_QUALIFIER = { Bytes.toBytes("q1"),
71 Bytes.toBytes("q2"), Bytes.toBytes("q3"), };
72 private static byte[][] TEST_VALUE = { Bytes.toBytes("v1"),
73 Bytes.toBytes("v2"), Bytes.toBytes("v3"), };
74 private static byte[] TEST_ROW = Bytes.toBytes("testRow");
75
76 private Configuration conf;
77 private FileSystem fs;
78 private Path dir;
79 private Path hbaseRootDir;
80 private String logName;
81 private Path oldLogDir;
82 private Path logDir;
83
84 @BeforeClass
85 public static void setupBeforeClass() throws Exception {
86 Configuration conf = TEST_UTIL.getConfiguration();
87 conf.set(CoprocessorHost.WAL_COPROCESSOR_CONF_KEY,
88 SampleRegionWALObserver.class.getName());
89 conf.set(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY,
90 SampleRegionWALObserver.class.getName());
91 conf.setBoolean("dfs.support.append", true);
92 conf.setInt("dfs.client.block.recovery.retries", 2);
93
94 TEST_UTIL.startMiniCluster(1);
95 Path hbaseRootDir = TEST_UTIL.getDFSCluster().getFileSystem()
96 .makeQualified(new Path("/hbase"));
97 LOG.info("hbase.rootdir=" + hbaseRootDir);
98 FSUtils.setRootDir(conf, hbaseRootDir);
99 }
100
101 @AfterClass
102 public static void teardownAfterClass() throws Exception {
103 TEST_UTIL.shutdownMiniCluster();
104 }
105
106 @Before
107 public void setUp() throws Exception {
108 this.conf = HBaseConfiguration.create(TEST_UTIL.getConfiguration());
109
110 this.fs = TEST_UTIL.getDFSCluster().getFileSystem();
111 this.hbaseRootDir = FSUtils.getRootDir(conf);
112 this.dir = new Path(this.hbaseRootDir, TestWALObserver.class.getName());
113 this.oldLogDir = new Path(this.hbaseRootDir,
114 HConstants.HREGION_OLDLOGDIR_NAME);
115 this.logDir = new Path(this.hbaseRootDir, HConstants.HREGION_LOGDIR_NAME);
116 this.logName = HConstants.HREGION_LOGDIR_NAME;
117
118 if (TEST_UTIL.getDFSCluster().getFileSystem().exists(this.hbaseRootDir)) {
119 TEST_UTIL.getDFSCluster().getFileSystem().delete(this.hbaseRootDir, true);
120 }
121 }
122
123 @After
124 public void tearDown() throws Exception {
125 TEST_UTIL.getDFSCluster().getFileSystem().delete(this.hbaseRootDir, true);
126 }
127
128
129
130
131
132
133 @Test
134 public void testWALObserverWriteToWAL() throws Exception {
135
136 HRegionInfo hri = createBasic3FamilyHRegionInfo(Bytes.toString(TEST_TABLE));
137 final HTableDescriptor htd = createBasic3FamilyHTD(Bytes
138 .toString(TEST_TABLE));
139
140 Path basedir = new Path(this.hbaseRootDir, Bytes.toString(TEST_TABLE));
141 deleteDir(basedir);
142 fs.mkdirs(new Path(basedir, hri.getEncodedName()));
143 final AtomicLong sequenceId = new AtomicLong(0);
144
145 HLog log = HLogFactory.createHLog(this.fs, hbaseRootDir,
146 TestWALObserver.class.getName(), this.conf);
147 SampleRegionWALObserver cp = getCoprocessor(log);
148
149
150
151
152 cp.setTestValues(TEST_TABLE, TEST_ROW, TEST_FAMILY[0], TEST_QUALIFIER[0],
153 TEST_FAMILY[1], TEST_QUALIFIER[1], TEST_FAMILY[2], TEST_QUALIFIER[2]);
154
155 assertFalse(cp.isPreWALWriteCalled());
156 assertFalse(cp.isPostWALWriteCalled());
157
158
159
160
161 Put p = creatPutWith2Families(TEST_ROW);
162
163 Map<byte[], List<Cell>> familyMap = p.getFamilyCellMap();
164 WALEdit edit = new WALEdit();
165 addFamilyMapToWALEdit(familyMap, edit);
166
167 boolean foundFamily0 = false;
168 boolean foundFamily2 = false;
169 boolean modifiedFamily1 = false;
170
171 List<KeyValue> kvs = edit.getKeyValues();
172
173 for (KeyValue kv : kvs) {
174 if (Arrays.equals(kv.getFamily(), TEST_FAMILY[0])) {
175 foundFamily0 = true;
176 }
177 if (Arrays.equals(kv.getFamily(), TEST_FAMILY[2])) {
178 foundFamily2 = true;
179 }
180 if (Arrays.equals(kv.getFamily(), TEST_FAMILY[1])) {
181 if (!Arrays.equals(kv.getValue(), TEST_VALUE[1])) {
182 modifiedFamily1 = true;
183 }
184 }
185 }
186 assertTrue(foundFamily0);
187 assertFalse(foundFamily2);
188 assertFalse(modifiedFamily1);
189
190
191 long now = EnvironmentEdgeManager.currentTimeMillis();
192 log.append(hri, hri.getTable(), edit, now, htd, sequenceId);
193
194
195 foundFamily0 = false;
196 foundFamily2 = false;
197 modifiedFamily1 = false;
198 for (KeyValue kv : kvs) {
199 if (Arrays.equals(kv.getFamily(), TEST_FAMILY[0])) {
200 foundFamily0 = true;
201 }
202 if (Arrays.equals(kv.getFamily(), TEST_FAMILY[2])) {
203 foundFamily2 = true;
204 }
205 if (Arrays.equals(kv.getFamily(), TEST_FAMILY[1])) {
206 if (!Arrays.equals(kv.getValue(), TEST_VALUE[1])) {
207 modifiedFamily1 = true;
208 }
209 }
210 }
211 assertFalse(foundFamily0);
212 assertTrue(foundFamily2);
213 assertTrue(modifiedFamily1);
214
215 assertTrue(cp.isPreWALWriteCalled());
216 assertTrue(cp.isPostWALWriteCalled());
217 }
218
219
220
221
222 @Test
223 public void testWALCoprocessorReplay() throws Exception {
224
225
226 TableName tableName = TableName.valueOf("testWALCoprocessorReplay");
227 final HTableDescriptor htd = getBasic3FamilyHTableDescriptor(tableName);
228 final AtomicLong sequenceId = new AtomicLong(0);
229
230
231
232
233 final HRegionInfo hri = new HRegionInfo(tableName, null, null);
234
235 final Path basedir =
236 FSUtils.getTableDir(this.hbaseRootDir, tableName);
237 deleteDir(basedir);
238 fs.mkdirs(new Path(basedir, hri.getEncodedName()));
239
240 final Configuration newConf = HBaseConfiguration.create(this.conf);
241
242
243 HLog wal = createWAL(this.conf);
244
245 WALEdit edit = new WALEdit();
246 long now = EnvironmentEdgeManager.currentTimeMillis();
247
248 final int countPerFamily = 1000;
249
250 for (HColumnDescriptor hcd : htd.getFamilies()) {
251
252
253 addWALEdits(tableName, hri, TEST_ROW, hcd.getName(), countPerFamily,
254 EnvironmentEdgeManager.getDelegate(), wal, htd, sequenceId);
255 }
256 wal.append(hri, tableName, edit, now, htd, sequenceId);
257
258 wal.sync();
259
260 User user = HBaseTestingUtility.getDifferentUser(newConf,
261 ".replay.wal.secondtime");
262 user.runAs(new PrivilegedExceptionAction() {
263 public Object run() throws Exception {
264 Path p = runWALSplit(newConf);
265 LOG.info("WALSplit path == " + p);
266 FileSystem newFS = FileSystem.get(newConf);
267
268 HLog wal2 = createWAL(newConf);
269 HRegion region = HRegion.openHRegion(newConf, FileSystem.get(newConf), hbaseRootDir,
270 hri, htd, wal2, TEST_UTIL.getHBaseCluster().getRegionServer(0), null);
271 long seqid2 = region.getOpenSeqNum();
272
273 SampleRegionWALObserver cp2 =
274 (SampleRegionWALObserver)region.getCoprocessorHost().findCoprocessor(
275 SampleRegionWALObserver.class.getName());
276
277 assertNotNull(cp2);
278 assertTrue(cp2.isPreWALRestoreCalled());
279 assertTrue(cp2.isPostWALRestoreCalled());
280 region.close();
281 wal2.closeAndDelete();
282 return null;
283 }
284 });
285 }
286
287
288
289
290
291
292 @Test
293 public void testWALObserverLoaded() throws Exception {
294 HLog log = HLogFactory.createHLog(fs, hbaseRootDir,
295 TestWALObserver.class.getName(), conf);
296 assertNotNull(getCoprocessor(log));
297 }
298
299 private SampleRegionWALObserver getCoprocessor(HLog wal) throws Exception {
300 WALCoprocessorHost host = wal.getCoprocessorHost();
301 Coprocessor c = host.findCoprocessor(SampleRegionWALObserver.class
302 .getName());
303 return (SampleRegionWALObserver) c;
304 }
305
306
307
308
309
310
311
312 private HRegionInfo createBasic3FamilyHRegionInfo(final String tableName) {
313 HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(tableName));
314
315 for (int i = 0; i < TEST_FAMILY.length; i++) {
316 HColumnDescriptor a = new HColumnDescriptor(TEST_FAMILY[i]);
317 htd.addFamily(a);
318 }
319 return new HRegionInfo(htd.getTableName(), null, null, false);
320 }
321
322
323
324
325 private void deleteDir(final Path p) throws IOException {
326 if (this.fs.exists(p)) {
327 if (!this.fs.delete(p, true)) {
328 throw new IOException("Failed remove of " + p);
329 }
330 }
331 }
332
333 private Put creatPutWith2Families(byte[] row) throws IOException {
334 Put p = new Put(row);
335 for (int i = 0; i < TEST_FAMILY.length - 1; i++) {
336 p.add(TEST_FAMILY[i], TEST_QUALIFIER[i], TEST_VALUE[i]);
337 }
338 return p;
339 }
340
341
342
343
344
345
346
347
348
349 private void addFamilyMapToWALEdit(Map<byte[], List<Cell>> familyMap,
350 WALEdit walEdit) {
351 for (List<Cell> edits : familyMap.values()) {
352 for (Cell cell : edits) {
353
354 walEdit.add((KeyValue)cell);
355 }
356 }
357 }
358
359 private Path runWALSplit(final Configuration c) throws IOException {
360 List<Path> splits = HLogSplitter.split(
361 hbaseRootDir, logDir, oldLogDir, FileSystem.get(c), c);
362
363 assertEquals(1, splits.size());
364
365 assertTrue(fs.exists(splits.get(0)));
366 LOG.info("Split file=" + splits.get(0));
367 return splits.get(0);
368 }
369
370 private HLog createWAL(final Configuration c) throws IOException {
371 return HLogFactory.createHLog(FileSystem.get(c), hbaseRootDir, logName, c);
372 }
373
374 private void addWALEdits(final TableName tableName, final HRegionInfo hri,
375 final byte[] rowName, final byte[] family, final int count,
376 EnvironmentEdge ee, final HLog wal, final HTableDescriptor htd, final AtomicLong sequenceId)
377 throws IOException {
378 String familyStr = Bytes.toString(family);
379 for (int j = 0; j < count; j++) {
380 byte[] qualifierBytes = Bytes.toBytes(Integer.toString(j));
381 byte[] columnBytes = Bytes.toBytes(familyStr + ":" + Integer.toString(j));
382 WALEdit edit = new WALEdit();
383 edit.add(new KeyValue(rowName, family, qualifierBytes, ee
384 .currentTimeMillis(), columnBytes));
385 wal.append(hri, tableName, edit, ee.currentTimeMillis(), htd, sequenceId);
386 }
387 }
388
389 private HTableDescriptor getBasic3FamilyHTableDescriptor(
390 final TableName tableName) {
391 HTableDescriptor htd = new HTableDescriptor(tableName);
392
393 for (int i = 0; i < TEST_FAMILY.length; i++) {
394 HColumnDescriptor a = new HColumnDescriptor(TEST_FAMILY[i]);
395 htd.addFamily(a);
396 }
397 return htd;
398 }
399
400 private HTableDescriptor createBasic3FamilyHTD(final String tableName) {
401 HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(tableName));
402 HColumnDescriptor a = new HColumnDescriptor(Bytes.toBytes("a"));
403 htd.addFamily(a);
404 HColumnDescriptor b = new HColumnDescriptor(Bytes.toBytes("b"));
405 htd.addFamily(b);
406 HColumnDescriptor c = new HColumnDescriptor(Bytes.toBytes("c"));
407 htd.addFamily(c);
408 return htd;
409 }
410
411 }