1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.replication.regionserver;
20
21 import static org.junit.Assert.assertEquals;
22 import static org.junit.Assert.assertFalse;
23 import static org.junit.Assert.assertNotEquals;
24 import static org.junit.Assert.assertNotNull;
25 import static org.junit.Assert.assertTrue;
26 import static org.junit.Assert.fail;
27
28 import java.io.IOException;
29 import java.util.ArrayList;
30 import java.util.List;
31 import java.util.NavigableMap;
32 import java.util.NoSuchElementException;
33 import java.util.TreeMap;
34 import java.util.concurrent.PriorityBlockingQueue;
35
36 import org.apache.hadoop.conf.Configuration;
37 import org.apache.hadoop.fs.FileSystem;
38 import org.apache.hadoop.fs.Path;
39 import org.apache.hadoop.hbase.Cell;
40 import org.apache.hadoop.hbase.HBaseTestingUtility;
41 import org.apache.hadoop.hbase.HConstants;
42 import org.apache.hadoop.hbase.HRegionInfo;
43 import org.apache.hadoop.hbase.HTableDescriptor;
44 import org.apache.hadoop.hbase.KeyValue;
45 import org.apache.hadoop.hbase.TableName;
46 import org.apache.hadoop.hbase.regionserver.MultiVersionConcurrencyControl;
47 import org.apache.hadoop.hbase.regionserver.wal.WALActionsListener;
48 import org.apache.hadoop.hbase.regionserver.wal.WALEdit;
49 import org.apache.hadoop.hbase.replication.ReplicationQueueInfo;
50 import org.apache.hadoop.hbase.replication.WALEntryFilter;
51 import org.apache.hadoop.hbase.replication.regionserver.ReplicationSourceWALReaderThread.WALEntryBatch;
52 import org.apache.hadoop.hbase.testclassification.LargeTests;
53 import org.apache.hadoop.hbase.testclassification.ReplicationTests;
54 import org.apache.hadoop.hbase.util.Bytes;
55 import org.apache.hadoop.hbase.wal.WAL;
56 import org.apache.hadoop.hbase.wal.WAL.Entry;
57 import org.apache.hadoop.hbase.wal.WALFactory;
58 import org.apache.hadoop.hbase.wal.WALKey;
59 import org.apache.hadoop.hdfs.MiniDFSCluster;
60 import org.junit.After;
61 import org.junit.AfterClass;
62 import org.junit.Before;
63 import org.junit.BeforeClass;
64 import org.junit.Rule;
65 import org.junit.Test;
66 import org.junit.experimental.categories.Category;
67 import org.junit.rules.TestName;
68 import org.junit.runner.RunWith;
69 import org.mockito.Mockito;
70 import org.mockito.runners.MockitoJUnitRunner;
71
72 @RunWith(MockitoJUnitRunner.class)
73 @Category({ ReplicationTests.class, LargeTests.class })
74 public class TestWALEntryStream {
75
76 private static HBaseTestingUtility TEST_UTIL;
77 private static Configuration conf;
78 private static FileSystem fs;
79 private static MiniDFSCluster cluster;
80 private static final TableName tableName = TableName.valueOf("tablename");
81 private static final byte[] family = Bytes.toBytes("column");
82 private static final byte[] qualifier = Bytes.toBytes("qualifier");
83 private static final HRegionInfo info =
84 new HRegionInfo(tableName, HConstants.EMPTY_START_ROW, HConstants.LAST_ROW, false);
85 private static final HTableDescriptor htd = new HTableDescriptor(tableName);
86 private static NavigableMap<byte[], Integer> scopes;
87
88 private WAL log;
89 PriorityBlockingQueue<Path> walQueue;
90 private PathWatcher pathWatcher;
91
92 @Rule
93 public TestName tn = new TestName();
94 private final MultiVersionConcurrencyControl mvcc = new MultiVersionConcurrencyControl();
95
96 @BeforeClass
97 public static void setUpBeforeClass() throws Exception {
98 TEST_UTIL = new HBaseTestingUtility();
99 conf = TEST_UTIL.getConfiguration();
100 TEST_UTIL.startMiniDFSCluster(3);
101
102 cluster = TEST_UTIL.getDFSCluster();
103 fs = cluster.getFileSystem();
104 scopes = new TreeMap<byte[], Integer>(Bytes.BYTES_COMPARATOR);
105 for (byte[] fam : htd.getFamiliesKeys()) {
106 scopes.put(fam, 0);
107 }
108 }
109
110 @AfterClass
111 public static void tearDownAfterClass() throws Exception {
112 TEST_UTIL.shutdownMiniCluster();
113 }
114
115 @Before
116 public void setUp() throws Exception {
117 walQueue = new PriorityBlockingQueue<>();
118 List<WALActionsListener> listeners = new ArrayList<WALActionsListener>();
119 pathWatcher = new PathWatcher();
120 listeners.add(pathWatcher);
121 final WALFactory wals = new WALFactory(conf, listeners, tn.getMethodName());
122 log = wals.getWAL(info.getEncodedNameAsBytes(), info.getTable().getNamespace());
123 }
124
125 @After
126 public void tearDown() throws Exception {
127 log.close();
128 }
129
130
131 @Test
132 public void testDifferentCounts() throws Exception {
133 int[] NB_ROWS = { 1500, 60000 };
134 int[] NB_KVS = { 1, 100 };
135
136 Boolean[] BOOL_VALS = { false, true };
137
138 for (int nbRows : NB_ROWS) {
139 for (int walEditKVs : NB_KVS) {
140 for (boolean isCompressionEnabled : BOOL_VALS) {
141 TEST_UTIL.getConfiguration().setBoolean(HConstants.ENABLE_WAL_COMPRESSION,
142 isCompressionEnabled);
143 mvcc.advanceTo(1);
144
145 for (int i = 0; i < nbRows; i++) {
146 appendToLogPlus(walEditKVs);
147 }
148
149 log.rollWriter();
150
151 try (WALEntryStream entryStream =
152 new WALEntryStream(walQueue, fs, conf, new MetricsSource("1"))) {
153 int i = 0;
154 for (WAL.Entry e : entryStream) {
155 assertNotNull(e);
156 i++;
157 }
158 assertEquals(nbRows, i);
159
160
161 assertFalse(entryStream.hasNext());
162 }
163
164 log.close();
165 setUp();
166 }
167 }
168 }
169 }
170
171
172
173
174 @Test
175 public void testAppendsWithRolls() throws Exception {
176 appendToLog();
177
178 long oldPos;
179 try (WALEntryStream entryStream =
180 new WALEntryStream(walQueue, fs, conf, new MetricsSource("1"))) {
181
182 assertTrue(entryStream.hasNext());
183 WAL.Entry entry = entryStream.next();
184 assertNotNull(entry);
185 assertFalse(entryStream.hasNext());
186 try {
187 entry = entryStream.next();
188 fail();
189 } catch (NoSuchElementException e) {
190
191 }
192 oldPos = entryStream.getPosition();
193 }
194
195 appendToLog();
196
197 try (WALEntryStream entryStream =
198 new WALEntryStream(walQueue, fs, conf, oldPos, new MetricsSource("1"))) {
199
200 WAL.Entry entry = entryStream.next();
201 assertNotEquals(oldPos, entryStream.getPosition());
202 assertNotNull(entry);
203 oldPos = entryStream.getPosition();
204 }
205
206
207 appendToLog();
208 log.rollWriter();
209 appendToLog();
210
211 try (WALEntryStream entryStream =
212 new WALEntryStream(walQueue, fs, conf, oldPos, new MetricsSource("1"))) {
213 WAL.Entry entry = entryStream.next();
214 assertNotEquals(oldPos, entryStream.getPosition());
215 assertNotNull(entry);
216
217
218 entry = entryStream.next();
219 assertNotEquals(oldPos, entryStream.getPosition());
220 assertNotNull(entry);
221
222
223 assertFalse(entryStream.hasNext());
224 oldPos = entryStream.getPosition();
225 }
226 }
227
228
229
230
231
232 @Test
233 public void testLogrollWhileStreaming() throws Exception {
234 appendToLog("1");
235 appendToLog("2");
236 try (WALEntryStream entryStream =
237 new WALEntryStream(walQueue, fs, conf, new MetricsSource("1"))) {
238 assertEquals("1", getRow(entryStream.next()));
239
240 appendToLog("3");
241 log.rollWriter();
242 appendToLog("4");
243
244 assertEquals("2", getRow(entryStream.next()));
245 assertEquals(2, walQueue.size());
246
247 assertEquals("3", getRow(entryStream.next()));
248
249 assertEquals("4", getRow(entryStream.next()));
250 assertEquals(1, walQueue.size());
251 assertFalse(entryStream.hasNext());
252 }
253 }
254
255
256
257
258 @Test
259 public void testNewEntriesWhileStreaming() throws Exception {
260 appendToLog("1");
261 try (WALEntryStream entryStream =
262 new WALEntryStream(walQueue, fs, conf, 0, new MetricsSource("1"))) {
263 entryStream.next();
264
265
266 appendToLog("2");
267 appendToLog("3");
268
269
270 assertFalse(entryStream.hasNext());
271
272
273 entryStream.reset();
274 assertEquals("2", getRow(entryStream.next()));
275 assertEquals("3", getRow(entryStream.next()));
276 assertFalse(entryStream.hasNext());
277 }
278 }
279
280 @Test
281 public void testResumeStreamingFromPosition() throws Exception {
282 long lastPosition = 0;
283 appendToLog("1");
284 try (WALEntryStream entryStream =
285 new WALEntryStream(walQueue, fs, conf, 0, new MetricsSource("1"))) {
286 entryStream.next();
287 appendToLog("2");
288 appendToLog("3");
289 lastPosition = entryStream.getPosition();
290 }
291
292 try (WALEntryStream entryStream =
293 new WALEntryStream(walQueue, fs, conf, lastPosition, new MetricsSource("1"))) {
294 assertEquals("2", getRow(entryStream.next()));
295 assertEquals("3", getRow(entryStream.next()));
296 assertFalse(entryStream.hasNext());
297 assertEquals(1, walQueue.size());
298 }
299 }
300
301
302
303
304
305 @Test
306 public void testPosition() throws Exception {
307 long lastPosition = 0;
308 appendEntriesToLog(3);
309
310 try (WALEntryStream entryStream =
311 new WALEntryStream(walQueue, fs, conf, lastPosition, new MetricsSource("1"))) {
312 entryStream.next();
313 lastPosition = entryStream.getPosition();
314 }
315
316 try (WALEntryStream entryStream =
317 new WALEntryStream(walQueue, fs, conf, lastPosition, new MetricsSource("1"))) {
318 assertNotNull(entryStream.next());
319 assertNotNull(entryStream.next());
320 assertFalse(entryStream.hasNext());
321 }
322 }
323
324
325 @Test
326 public void testEmptyStream() throws Exception {
327 try (WALEntryStream entryStream =
328 new WALEntryStream(walQueue, fs, conf, 0, new MetricsSource("1"))) {
329 assertFalse(entryStream.hasNext());
330 }
331 }
332
333 @Test
334 public void testReplicationSourceWALReaderThread() throws Exception {
335 appendEntriesToLog(3);
336
337 long position;
338 try (WALEntryStream entryStream =
339 new WALEntryStream(walQueue, fs, conf, new MetricsSource("1"))) {
340 entryStream.next();
341 entryStream.next();
342 entryStream.next();
343 position = entryStream.getPosition();
344 }
345
346
347 ReplicationSourceManager mockSourceManager = Mockito.mock(ReplicationSourceManager.class);
348 ReplicationSourceWALReaderThread batcher = new ReplicationSourceWALReaderThread(mockSourceManager, getQueueInfo(),walQueue, 0,
349 fs, conf, getDummyFilter(), new MetricsSource("1"));
350 Path walPath = walQueue.peek();
351 batcher.start();
352 WALEntryBatch entryBatch = batcher.take();
353
354
355 assertNotNull(entryBatch);
356 assertEquals(3, entryBatch.getWalEntries().size());
357 assertEquals(position, entryBatch.getLastWalPosition());
358 assertEquals(walPath, entryBatch.getLastWalPath());
359 assertEquals(3, entryBatch.getNbRowKeys());
360
361 appendToLog("foo");
362 entryBatch = batcher.take();
363 assertEquals(1, entryBatch.getNbEntries());
364 assertEquals(getRow(entryBatch.getWalEntries().get(0)), "foo");
365 }
366
367 private String getRow(WAL.Entry entry) {
368 Cell cell = entry.getEdit().getCells().get(0);
369 return Bytes.toString(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength());
370 }
371
372 private void appendToLog(String key) throws IOException {
373 final long txid = log.append(htd, info,
374 new WALKey(info.getEncodedNameAsBytes(), tableName, System.currentTimeMillis(), mvcc),
375 getWALEdit(key), true);
376 log.sync(txid);
377 }
378
379 private void appendEntriesToLog(int count) throws IOException {
380 for (int i = 0; i < count; i++) {
381 appendToLog();
382 }
383 }
384
385 private void appendToLog() throws IOException {
386 appendToLogPlus(1);
387 }
388
389 private void appendToLogPlus(int count) throws IOException {
390 final long txid = log.append(htd, info,
391 new WALKey(info.getEncodedNameAsBytes(), tableName, System.currentTimeMillis(), mvcc),
392 getWALEdits(count), true);
393 log.sync(txid);
394 }
395
396 private WALEdit getWALEdits(int count) {
397 WALEdit edit = new WALEdit();
398 for (int i = 0; i < count; i++) {
399 edit.add(new KeyValue(Bytes.toBytes(System.currentTimeMillis()), family, qualifier,
400 System.currentTimeMillis(), qualifier));
401 }
402 return edit;
403 }
404
405 private WALEdit getWALEdit(String row) {
406 WALEdit edit = new WALEdit();
407 edit.add(
408 new KeyValue(Bytes.toBytes(row), family, qualifier, System.currentTimeMillis(), qualifier));
409 return edit;
410 }
411
412 private WALEntryFilter getDummyFilter() {
413 return new WALEntryFilter() {
414
415 @Override
416 public Entry filter(Entry entry) {
417 return entry;
418 }
419 };
420 }
421
422 private ReplicationQueueInfo getQueueInfo() {
423 return new ReplicationQueueInfo("1");
424 }
425
426 class PathWatcher extends WALActionsListener.Base {
427
428 Path currentPath;
429
430 @Override
431 public void preLogRoll(Path oldPath, Path newPath) throws IOException {
432 walQueue.add(newPath);
433 currentPath = newPath;
434 }
435 }
436
437 }