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.wal;
20
21 import static org.junit.Assert.assertEquals;
22 import static org.junit.Assert.assertFalse;
23 import static org.junit.Assert.assertNotNull;
24 import static org.junit.Assert.assertTrue;
25 import static org.junit.Assert.fail;
26
27 import java.io.IOException;
28 import java.util.ArrayList;
29 import java.util.Comparator;
30 import java.util.HashMap;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.UUID;
34 import java.util.concurrent.atomic.AtomicLong;
35
36 import org.apache.commons.lang.mutable.MutableBoolean;
37 import org.apache.commons.logging.Log;
38 import org.apache.commons.logging.LogFactory;
39 import org.apache.hadoop.conf.Configuration;
40 import org.apache.hadoop.fs.FileStatus;
41 import org.apache.hadoop.fs.FileSystem;
42 import org.apache.hadoop.fs.Path;
43 import org.apache.hadoop.hbase.CellScanner;
44 import org.apache.hadoop.hbase.Coprocessor;
45 import org.apache.hadoop.hbase.HBaseTestingUtility;
46 import org.apache.hadoop.hbase.HBaseConfiguration;
47 import org.apache.hadoop.hbase.HColumnDescriptor;
48 import org.apache.hadoop.hbase.HConstants;
49 import org.apache.hadoop.hbase.HRegionInfo;
50 import org.apache.hadoop.hbase.HTableDescriptor;
51 import org.apache.hadoop.hbase.KeyValue;
52 import org.apache.hadoop.hbase.testclassification.MediumTests;
53 import org.apache.hadoop.hbase.TableName;
54 import org.apache.hadoop.hbase.client.Get;
55 import org.apache.hadoop.hbase.client.Put;
56 import org.apache.hadoop.hbase.client.Result;
57 import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
58 import org.apache.hadoop.hbase.coprocessor.SampleRegionWALObserver;
59 import org.apache.hadoop.hbase.regionserver.HRegion;
60 import org.apache.hadoop.hbase.util.Bytes;
61 import org.apache.hadoop.hbase.util.EnvironmentEdge;
62 import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
63 import org.apache.hadoop.hbase.util.FSUtils;
64 import org.apache.hadoop.hbase.util.Threads;
65 import org.apache.hadoop.hbase.wal.DefaultWALProvider;
66 import org.apache.hadoop.hbase.wal.WAL;
67 import org.apache.hadoop.hbase.wal.WALKey;
68 import org.junit.After;
69 import org.junit.AfterClass;
70 import org.junit.Before;
71 import org.junit.BeforeClass;
72 import org.junit.Rule;
73 import org.junit.Test;
74 import org.junit.experimental.categories.Category;
75 import org.junit.rules.TestName;
76
77
78
79
80 @Category(MediumTests.class)
81 public class TestFSHLog {
82 protected static final Log LOG = LogFactory.getLog(TestFSHLog.class);
83
84 protected static Configuration conf;
85 protected static FileSystem fs;
86 protected static Path dir;
87 protected final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
88
89 @Rule
90 public final TestName currentTest = new TestName();
91
92 @Before
93 public void setUp() throws Exception {
94 FileStatus[] entries = fs.listStatus(new Path("/"));
95 for (FileStatus dir : entries) {
96 fs.delete(dir.getPath(), true);
97 }
98 final Path hbaseDir = TEST_UTIL.createRootDir();
99 dir = new Path(hbaseDir, currentTest.getMethodName());
100 }
101
102 @After
103 public void tearDown() throws Exception {
104 }
105
106 @BeforeClass
107 public static void setUpBeforeClass() throws Exception {
108
109 TEST_UTIL.getConfiguration().setInt("dfs.blocksize", 1024 * 1024);
110
111 TEST_UTIL.getConfiguration().setInt("dfs.namenode.heartbeat.recheck-interval", 5000);
112 TEST_UTIL.getConfiguration().setInt("dfs.heartbeat.interval", 1);
113 TEST_UTIL.getConfiguration().setInt("dfs.client.socket-timeout", 5000);
114
115
116 TEST_UTIL.getConfiguration()
117 .setInt("hbase.ipc.client.connect.max.retries", 1);
118 TEST_UTIL.getConfiguration().setInt(
119 "dfs.client.block.recovery.retries", 1);
120 TEST_UTIL.getConfiguration().setInt(
121 "hbase.ipc.client.connection.maxidletime", 500);
122 TEST_UTIL.getConfiguration().set(CoprocessorHost.WAL_COPROCESSOR_CONF_KEY,
123 SampleRegionWALObserver.class.getName());
124 TEST_UTIL.startMiniDFSCluster(3);
125
126 conf = TEST_UTIL.getConfiguration();
127 fs = TEST_UTIL.getDFSCluster().getFileSystem();
128 }
129
130 @AfterClass
131 public static void tearDownAfterClass() throws Exception {
132 TEST_UTIL.shutdownMiniCluster();
133 }
134
135
136
137
138 @Test
139 public void testWALCoprocessorLoaded() throws Exception {
140
141 FSHLog log = null;
142 try {
143 log = new FSHLog(fs, FSUtils.getRootDir(conf), dir.toString(),
144 HConstants.HREGION_OLDLOGDIR_NAME, conf, null, true, null, null);
145 WALCoprocessorHost host = log.getCoprocessorHost();
146 Coprocessor c = host.findCoprocessor(SampleRegionWALObserver.class.getName());
147 assertNotNull(c);
148 } finally {
149 if (log != null) {
150 log.close();
151 }
152 }
153 }
154
155 protected void addEdits(WAL log, HRegionInfo hri, TableName tableName,
156 int times, AtomicLong sequenceId) throws IOException {
157 HTableDescriptor htd = new HTableDescriptor();
158 htd.addFamily(new HColumnDescriptor("row"));
159
160 final byte [] row = Bytes.toBytes("row");
161 for (int i = 0; i < times; i++) {
162 long timestamp = System.currentTimeMillis();
163 WALEdit cols = new WALEdit();
164 cols.add(new KeyValue(row, row, row, timestamp, row));
165 log.append(htd, hri, new WALKey(hri.getEncodedNameAsBytes(), tableName, timestamp), cols,
166 sequenceId, true, null);
167 }
168 log.sync();
169 }
170
171
172
173
174
175
176 protected void flushRegion(WAL wal, byte[] regionEncodedName) {
177 wal.startCacheFlush(regionEncodedName);
178 wal.completeCacheFlush(regionEncodedName);
179 }
180
181
182
183
184
185
186 @Test
187 public void testWALComparator() throws Exception {
188 FSHLog wal1 = null;
189 FSHLog walMeta = null;
190 try {
191 wal1 = new FSHLog(fs, FSUtils.getRootDir(conf), dir.toString(),
192 HConstants.HREGION_OLDLOGDIR_NAME, conf, null, true, null, null);
193 LOG.debug("Log obtained is: " + wal1);
194 Comparator<Path> comp = wal1.LOG_NAME_COMPARATOR;
195 Path p1 = wal1.computeFilename(11);
196 Path p2 = wal1.computeFilename(12);
197
198 assertTrue(comp.compare(p1, p1) == 0);
199
200 assertTrue(comp.compare(p1, p2) < 0);
201 walMeta = new FSHLog(fs, FSUtils.getRootDir(conf), dir.toString(),
202 HConstants.HREGION_OLDLOGDIR_NAME, conf, null, true, null,
203 DefaultWALProvider.META_WAL_PROVIDER_ID);
204 Comparator<Path> compMeta = walMeta.LOG_NAME_COMPARATOR;
205
206 Path p1WithMeta = walMeta.computeFilename(11);
207 Path p2WithMeta = walMeta.computeFilename(12);
208 assertTrue(compMeta.compare(p1WithMeta, p1WithMeta) == 0);
209 assertTrue(compMeta.compare(p1WithMeta, p2WithMeta) < 0);
210
211 boolean ex = false;
212 try {
213 comp.compare(p1WithMeta, p2);
214 } catch (IllegalArgumentException e) {
215 ex = true;
216 }
217 assertTrue("Comparator doesn't complain while checking meta log files", ex);
218 boolean exMeta = false;
219 try {
220 compMeta.compare(p1WithMeta, p2);
221 } catch (IllegalArgumentException e) {
222 exMeta = true;
223 }
224 assertTrue("Meta comparator doesn't complain while checking log files", exMeta);
225 } finally {
226 if (wal1 != null) {
227 wal1.close();
228 }
229 if (walMeta != null) {
230 walMeta.close();
231 }
232 }
233 }
234
235
236
237
238
239
240
241
242
243
244 @Test
245 public void testFindMemStoresEligibleForFlush() throws Exception {
246 LOG.debug("testFindMemStoresEligibleForFlush");
247 Configuration conf1 = HBaseConfiguration.create(conf);
248 conf1.setInt("hbase.regionserver.maxlogs", 1);
249 FSHLog wal = new FSHLog(fs, FSUtils.getRootDir(conf1), dir.toString(),
250 HConstants.HREGION_OLDLOGDIR_NAME, conf1, null, true, null, null);
251 TableName t1 = TableName.valueOf("t1");
252 TableName t2 = TableName.valueOf("t2");
253 HRegionInfo hri1 = new HRegionInfo(t1, HConstants.EMPTY_START_ROW, HConstants.EMPTY_END_ROW);
254 HRegionInfo hri2 = new HRegionInfo(t2, HConstants.EMPTY_START_ROW, HConstants.EMPTY_END_ROW);
255
256 final AtomicLong sequenceId1 = new AtomicLong(1);
257 final AtomicLong sequenceId2 = new AtomicLong(1);
258
259 try {
260 addEdits(wal, hri1, t1, 2, sequenceId1);
261 wal.rollWriter();
262
263 addEdits(wal, hri1, t1, 2, sequenceId1);
264 wal.rollWriter();
265
266 assertTrue(wal.getNumRolledLogFiles() == 2);
267
268
269
270 byte[][] regionsToFlush = wal.findRegionsToForceFlush();
271 assertEquals(1, regionsToFlush.length);
272 assertEquals(hri1.getEncodedNameAsBytes(), regionsToFlush[0]);
273
274 addEdits(wal, hri2, t2, 2, sequenceId2);
275
276 regionsToFlush = wal.findRegionsToForceFlush();
277 assertEquals(regionsToFlush.length, 1);
278 assertEquals(hri1.getEncodedNameAsBytes(), regionsToFlush[0]);
279
280
281 flushRegion(wal, hri1.getEncodedNameAsBytes());
282 wal.rollWriter();
283
284 assertEquals(1, wal.getNumRolledLogFiles());
285
286 flushRegion(wal, hri2.getEncodedNameAsBytes());
287 wal.rollWriter(true);
288
289 assertEquals(0, wal.getNumRolledLogFiles());
290
291 addEdits(wal, hri1, t1, 2, sequenceId1);
292 addEdits(wal, hri2, t2, 2, sequenceId2);
293 wal.rollWriter();
294
295 assertEquals(1, wal.getNumRolledLogFiles());
296 addEdits(wal, hri1, t1, 2, sequenceId1);
297 wal.rollWriter();
298
299
300 regionsToFlush = wal.findRegionsToForceFlush();
301 assertEquals(2, regionsToFlush.length);
302
303 flushRegion(wal, hri1.getEncodedNameAsBytes());
304 flushRegion(wal, hri2.getEncodedNameAsBytes());
305 wal.rollWriter(true);
306 assertEquals(0, wal.getNumRolledLogFiles());
307
308 addEdits(wal, hri1, t1, 2, sequenceId1);
309
310 wal.startCacheFlush(hri1.getEncodedNameAsBytes());
311 wal.rollWriter();
312 wal.completeCacheFlush(hri1.getEncodedNameAsBytes());
313 assertEquals(1, wal.getNumRolledLogFiles());
314 } finally {
315 if (wal != null) {
316 wal.close();
317 }
318 }
319 }
320
321
322
323
324
325
326
327
328 @Test
329 public void testAllRegionsFlushed() {
330 LOG.debug("testAllRegionsFlushed");
331 Map<byte[], Long> oldestFlushingSeqNo = new HashMap<byte[], Long>();
332 Map<byte[], Long> oldestUnFlushedSeqNo = new HashMap<byte[], Long>();
333 Map<byte[], Long> seqNo = new HashMap<byte[], Long>();
334
335 TableName t1 = TableName.valueOf("t1");
336
337 HRegionInfo hri1 = new HRegionInfo(t1, HConstants.EMPTY_START_ROW, HConstants.EMPTY_END_ROW);
338
339 final AtomicLong sequenceId1 = new AtomicLong(1);
340
341 assertTrue(FSHLog.areAllRegionsFlushed(seqNo, oldestFlushingSeqNo, oldestUnFlushedSeqNo));
342
343 seqNo.put(hri1.getEncodedNameAsBytes(), sequenceId1.incrementAndGet());
344 oldestUnFlushedSeqNo.put(hri1.getEncodedNameAsBytes(), sequenceId1.get());
345
346 assertFalse(FSHLog.areAllRegionsFlushed(seqNo, oldestFlushingSeqNo, oldestUnFlushedSeqNo));
347
348 oldestUnFlushedSeqNo.clear();
349 oldestFlushingSeqNo.put(hri1.getEncodedNameAsBytes(), sequenceId1.get());
350 assertFalse(FSHLog.areAllRegionsFlushed(seqNo, oldestFlushingSeqNo, oldestUnFlushedSeqNo));
351
352 oldestFlushingSeqNo.clear();
353 oldestUnFlushedSeqNo.clear();
354 assertTrue(FSHLog.areAllRegionsFlushed(seqNo, oldestFlushingSeqNo, oldestUnFlushedSeqNo));
355
356 oldestUnFlushedSeqNo.put(hri1.getEncodedNameAsBytes(), 1000l);
357 seqNo.put(hri1.getEncodedNameAsBytes(), 1500l);
358 assertFalse(FSHLog.areAllRegionsFlushed(seqNo, oldestFlushingSeqNo, oldestUnFlushedSeqNo));
359
360
361
362 oldestFlushingSeqNo.put(hri1.getEncodedNameAsBytes(), 1200l);
363 oldestUnFlushedSeqNo.clear();
364 seqNo.put(hri1.getEncodedNameAsBytes(), 1199l);
365 assertTrue(FSHLog.areAllRegionsFlushed(seqNo, oldestFlushingSeqNo, oldestUnFlushedSeqNo));
366 }
367
368 @Test(expected=IOException.class)
369 public void testFailedToCreateWALIfParentRenamed() throws IOException {
370 final String name = "testFailedToCreateWALIfParentRenamed";
371 FSHLog log = new FSHLog(fs, FSUtils.getRootDir(conf), name, HConstants.HREGION_OLDLOGDIR_NAME,
372 conf, null, true, null, null);
373 long filenum = System.currentTimeMillis();
374 Path path = log.computeFilename(filenum);
375 log.createWriterInstance(path);
376 Path parent = path.getParent();
377 path = log.computeFilename(filenum + 1);
378 Path newPath = new Path(parent.getParent(), parent.getName() + "-splitting");
379 fs.rename(parent, newPath);
380 log.createWriterInstance(path);
381 fail("It should fail to create the new WAL");
382 }
383
384
385
386
387
388
389
390
391
392 @Test
393 public void testFlushSequenceIdIsGreaterThanAllEditsInHFile() throws IOException {
394 String testName = "testFlushSequenceIdIsGreaterThanAllEditsInHFile";
395 final TableName tableName = TableName.valueOf(testName);
396 final HRegionInfo hri = new HRegionInfo(tableName);
397 final byte[] rowName = tableName.getName();
398 final HTableDescriptor htd = new HTableDescriptor(tableName);
399 htd.addFamily(new HColumnDescriptor("f"));
400 HRegion r = HRegion.createHRegion(hri, TEST_UTIL.getDefaultRootDirPath(),
401 TEST_UTIL.getConfiguration(), htd);
402 HRegion.closeHRegion(r);
403 final int countPerFamily = 10;
404 final MutableBoolean goslow = new MutableBoolean(false);
405
406 FSHLog wal = new FSHLog(FileSystem.get(conf), TEST_UTIL.getDefaultRootDirPath(),
407 testName, conf) {
408 @Override
409 void atHeadOfRingBufferEventHandlerAppend() {
410 if (goslow.isTrue()) {
411 Threads.sleep(100);
412 LOG.debug("Sleeping before appending 100ms");
413 }
414 super.atHeadOfRingBufferEventHandlerAppend();
415 }
416 };
417 HRegion region = HRegion.openHRegion(TEST_UTIL.getConfiguration(),
418 TEST_UTIL.getTestFileSystem(), TEST_UTIL.getDefaultRootDirPath(), hri, htd, wal);
419 EnvironmentEdge ee = EnvironmentEdgeManager.getDelegate();
420 try {
421 List<Put> puts = null;
422 for (HColumnDescriptor hcd: htd.getFamilies()) {
423 puts =
424 TestWALReplay.addRegionEdits(rowName, hcd.getName(), countPerFamily, ee, region, "x");
425 }
426
427
428 final Get g = new Get(rowName);
429 Result result = region.get(g);
430 assertEquals(countPerFamily * htd.getFamilies().size(), result.size());
431
432
433 WALEdit edits = new WALEdit();
434 for (Put p: puts) {
435 CellScanner cs = p.cellScanner();
436 while (cs.advance()) {
437 edits.add(cs.current());
438 }
439 }
440
441 List<UUID> clusterIds = new ArrayList<UUID>();
442 clusterIds.add(UUID.randomUUID());
443
444 goslow.setValue(true);
445 for (int i = 0; i < countPerFamily; i++) {
446 final HRegionInfo info = region.getRegionInfo();
447 final WALKey logkey = new WALKey(info.getEncodedNameAsBytes(), tableName,
448 System.currentTimeMillis(), clusterIds, -1, -1);
449 wal.append(htd, info, logkey, edits, region.getSequenceId(), true, null);
450 }
451 region.flushcache();
452
453 long currentSequenceId = region.getSequenceId().get();
454
455 goslow.setValue(false);
456 synchronized (goslow) {
457 goslow.notifyAll();
458 }
459 assertTrue(currentSequenceId >= region.getSequenceId().get());
460 } finally {
461 region.close(true);
462 wal.close();
463 }
464 }
465
466 }