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
23 import java.security.SecureRandom;
24 import java.util.ArrayList;
25 import java.util.Collections;
26 import java.util.HashMap;
27 import java.util.HashSet;
28 import java.util.Iterator;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Random;
32 import java.util.Set;
33 import java.util.concurrent.atomic.AtomicBoolean;
34
35 import org.apache.hadoop.hbase.TableNotFoundException;
36 import org.apache.hadoop.hbase.client.Admin;
37 import org.apache.hadoop.hbase.client.RetriesExhaustedWithDetailsException;
38 import org.apache.hadoop.hbase.client.Table;
39 import org.apache.hadoop.hbase.testclassification.LargeTests;
40 import org.apache.hadoop.hbase.util.ByteStringer;
41 import org.apache.commons.logging.Log;
42 import org.apache.commons.logging.LogFactory;
43 import org.apache.hadoop.conf.Configuration;
44 import org.apache.hadoop.fs.FileSystem;
45 import org.apache.hadoop.fs.FileUtil;
46 import org.apache.hadoop.fs.Path;
47 import org.apache.hadoop.hbase.Cell;
48 import org.apache.hadoop.hbase.CellUtil;
49 import org.apache.hadoop.hbase.HBaseTestingUtility;
50 import org.apache.hadoop.hbase.HConstants;
51 import org.apache.hadoop.hbase.HRegionInfo;
52 import org.apache.hadoop.hbase.KeyValue;
53 import org.apache.hadoop.hbase.Stoppable;
54 import org.apache.hadoop.hbase.TableName;
55 import org.apache.hadoop.hbase.client.Connection;
56 import org.apache.hadoop.hbase.client.ConnectionFactory;
57 import org.apache.hadoop.hbase.client.Get;
58 import org.apache.hadoop.hbase.client.RegionLocator;
59 import org.apache.hadoop.hbase.client.Result;
60 import org.apache.hadoop.hbase.client.ResultScanner;
61 import org.apache.hadoop.hbase.client.Scan;
62 import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
63 import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.WALEntry;
64 import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.UUID;
65 import org.apache.hadoop.hbase.protobuf.generated.WALProtos;
66 import org.apache.hadoop.hbase.protobuf.generated.WALProtos.WALKey;
67 import org.apache.hadoop.hbase.regionserver.wal.WALEdit;
68 import org.apache.hadoop.hbase.util.Bytes;
69 import org.apache.hadoop.hbase.util.FSUtils;
70 import org.apache.hadoop.hbase.util.HFileTestUtil;
71 import org.junit.AfterClass;
72 import org.junit.Assert;
73 import org.junit.Before;
74 import org.junit.BeforeClass;
75 import org.junit.Test;
76 import org.junit.experimental.categories.Category;
77
78 @Category(LargeTests.class)
79 public class TestReplicationSink {
80 private static final Log LOG = LogFactory.getLog(TestReplicationSink.class);
81 private static final int BATCH_SIZE = 10;
82
83 protected final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
84
85 protected static ReplicationSink SINK;
86
87 protected static final TableName TABLE_NAME1 = TableName.valueOf("table1");
88 protected static final TableName TABLE_NAME2 = TableName.valueOf("table2");
89
90 protected static final byte[] FAM_NAME1 = Bytes.toBytes("info1");
91 protected static final byte[] FAM_NAME2 = Bytes.toBytes("info2");
92
93 protected static Table table1;
94 protected static Stoppable STOPPABLE = new Stoppable() {
95 final AtomicBoolean stop = new AtomicBoolean(false);
96
97 @Override
98 public boolean isStopped() {
99 return this.stop.get();
100 }
101
102 @Override
103 public void stop(String why) {
104 LOG.info("STOPPING BECAUSE: " + why);
105 this.stop.set(true);
106 }
107
108 };
109
110 protected static Table table2;
111 protected static String baseNamespaceDir;
112 protected static String hfileArchiveDir;
113 protected static String replicationClusterId;
114
115
116
117
118 @BeforeClass
119 public static void setUpBeforeClass() throws Exception {
120 TEST_UTIL.getConfiguration().setBoolean("dfs.support.append", true);
121 TEST_UTIL.getConfiguration().setBoolean(HConstants.REPLICATION_ENABLE_KEY,
122 HConstants.REPLICATION_ENABLE_DEFAULT);
123 TEST_UTIL.getConfiguration().set("hbase.replication.source.fs.conf.provider",
124 TestSourceFSConfigurationProvider.class.getCanonicalName());
125 TEST_UTIL.startMiniCluster(3);
126 SINK = new ReplicationSink(new Configuration(TEST_UTIL.getConfiguration()));
127 table1 = TEST_UTIL.createTable(TABLE_NAME1, FAM_NAME1);
128 table2 = TEST_UTIL.createTable(TABLE_NAME2, FAM_NAME2);
129 Path rootDir = FSUtils.getRootDir(TEST_UTIL.getConfiguration());
130 baseNamespaceDir = new Path(rootDir, new Path(HConstants.BASE_NAMESPACE_DIR)).toString();
131 hfileArchiveDir = new Path(rootDir, new Path(HConstants.HFILE_ARCHIVE_DIRECTORY)).toString();
132 replicationClusterId = "12345";
133 }
134
135
136
137
138 @AfterClass
139 public static void tearDownAfterClass() throws Exception {
140 STOPPABLE.stop("Shutting down");
141 TEST_UTIL.shutdownMiniCluster();
142 }
143
144
145
146
147 @Before
148 public void setUp() throws Exception {
149 table1 = TEST_UTIL.deleteTableData(TABLE_NAME1);
150 table2 = TEST_UTIL.deleteTableData(TABLE_NAME2);
151 }
152
153
154
155
156
157 @Test
158 public void testBatchSink() throws Exception {
159 List<WALEntry> entries = new ArrayList<WALEntry>(BATCH_SIZE);
160 List<Cell> cells = new ArrayList<Cell>();
161 for(int i = 0; i < BATCH_SIZE; i++) {
162 entries.add(createEntry(TABLE_NAME1, i, KeyValue.Type.Put, cells));
163 }
164 SINK.replicateEntries(entries, CellUtil.createCellScanner(cells.iterator()),
165 replicationClusterId, baseNamespaceDir, hfileArchiveDir);
166 Scan scan = new Scan();
167 ResultScanner scanRes = table1.getScanner(scan);
168 assertEquals(BATCH_SIZE, scanRes.next(BATCH_SIZE).length);
169 }
170
171
172
173
174
175 @Test
176 public void testMixedPutDelete() throws Exception {
177 List<WALEntry> entries = new ArrayList<WALEntry>(BATCH_SIZE/2);
178 List<Cell> cells = new ArrayList<Cell>();
179 for(int i = 0; i < BATCH_SIZE/2; i++) {
180 entries.add(createEntry(TABLE_NAME1, i, KeyValue.Type.Put, cells));
181 }
182 SINK.replicateEntries(entries, CellUtil.createCellScanner(cells), replicationClusterId,
183 baseNamespaceDir, hfileArchiveDir);
184
185 entries = new ArrayList<WALEntry>(BATCH_SIZE);
186 cells = new ArrayList<Cell>();
187 for(int i = 0; i < BATCH_SIZE; i++) {
188 entries.add(createEntry(TABLE_NAME1, i,
189 i % 2 != 0 ? KeyValue.Type.Put: KeyValue.Type.DeleteColumn, cells));
190 }
191
192 SINK.replicateEntries(entries, CellUtil.createCellScanner(cells.iterator()),
193 replicationClusterId, baseNamespaceDir, hfileArchiveDir);
194 Scan scan = new Scan();
195 ResultScanner scanRes = table1.getScanner(scan);
196 assertEquals(BATCH_SIZE/2, scanRes.next(BATCH_SIZE).length);
197 }
198
199 @Test
200 public void testLargeEditsPutDelete() throws Exception {
201 List<WALEntry> entries = new ArrayList<>();
202 List<Cell> cells = new ArrayList<>();
203 for (int i = 0; i < 5510; i++) {
204 entries.add(createEntry(TABLE_NAME1, i, KeyValue.Type.Put, cells));
205 }
206 SINK.replicateEntries(entries, CellUtil.createCellScanner(cells), replicationClusterId,
207 baseNamespaceDir, hfileArchiveDir);
208
209 ResultScanner resultScanner = table1.getScanner(new Scan());
210 int totalRows = 0;
211 while (resultScanner.next() != null) {
212 totalRows++;
213 }
214 assertEquals(5510, totalRows);
215
216 entries = new ArrayList<>();
217 cells = new ArrayList<>();
218 for (int i = 0; i < 11000; i++) {
219 entries.add(
220 createEntry(TABLE_NAME1, i, i % 2 != 0 ? KeyValue.Type.Put : KeyValue.Type.DeleteColumn,
221 cells));
222 }
223 SINK.replicateEntries(entries, CellUtil.createCellScanner(cells), replicationClusterId,
224 baseNamespaceDir, hfileArchiveDir);
225 resultScanner = table1.getScanner(new Scan());
226 totalRows = 0;
227 while (resultScanner.next() != null) {
228 totalRows++;
229 }
230 assertEquals(5500, totalRows);
231 }
232
233
234
235
236
237 @Test
238 public void testMixedPutTables() throws Exception {
239 List<WALEntry> entries = new ArrayList<WALEntry>(BATCH_SIZE/2);
240 List<Cell> cells = new ArrayList<Cell>();
241 for(int i = 0; i < BATCH_SIZE; i++) {
242 entries.add(createEntry( i % 2 == 0 ? TABLE_NAME2 : TABLE_NAME1,
243 i, KeyValue.Type.Put, cells));
244 }
245
246 SINK.replicateEntries(entries, CellUtil.createCellScanner(cells.iterator()),
247 replicationClusterId, baseNamespaceDir, hfileArchiveDir);
248 Scan scan = new Scan();
249 ResultScanner scanRes = table2.getScanner(scan);
250 for(Result res : scanRes) {
251 assertEquals(0, Bytes.toInt(res.getRow()) % 2);
252 }
253 scanRes = table1.getScanner(scan);
254 for(Result res : scanRes) {
255 assertEquals(1, Bytes.toInt(res.getRow()) % 2);
256 }
257 }
258
259
260
261
262
263 @Test
264 public void testMixedDeletes() throws Exception {
265 List<WALEntry> entries = new ArrayList<WALEntry>(3);
266 List<Cell> cells = new ArrayList<Cell>();
267 for(int i = 0; i < 3; i++) {
268 entries.add(createEntry(TABLE_NAME1, i, KeyValue.Type.Put, cells));
269 }
270 SINK.replicateEntries(entries, CellUtil.createCellScanner(cells.iterator()),
271 replicationClusterId, baseNamespaceDir, hfileArchiveDir);
272 entries = new ArrayList<WALEntry>(3);
273 cells = new ArrayList<Cell>();
274 entries.add(createEntry(TABLE_NAME1, 0, KeyValue.Type.DeleteColumn, cells));
275 entries.add(createEntry(TABLE_NAME1, 1, KeyValue.Type.DeleteFamily, cells));
276 entries.add(createEntry(TABLE_NAME1, 2, KeyValue.Type.DeleteColumn, cells));
277
278 SINK.replicateEntries(entries, CellUtil.createCellScanner(cells.iterator()),
279 replicationClusterId, baseNamespaceDir, hfileArchiveDir);
280
281 Scan scan = new Scan();
282 ResultScanner scanRes = table1.getScanner(scan);
283 assertEquals(0, scanRes.next(3).length);
284 }
285
286
287
288
289
290
291 @Test
292 public void testApplyDeleteBeforePut() throws Exception {
293 List<WALEntry> entries = new ArrayList<WALEntry>(5);
294 List<Cell> cells = new ArrayList<Cell>();
295 for(int i = 0; i < 2; i++) {
296 entries.add(createEntry(TABLE_NAME1, i, KeyValue.Type.Put, cells));
297 }
298 entries.add(createEntry(TABLE_NAME1, 1, KeyValue.Type.DeleteFamily, cells));
299 for(int i = 3; i < 5; i++) {
300 entries.add(createEntry(TABLE_NAME1, i, KeyValue.Type.Put, cells));
301 }
302 SINK.replicateEntries(entries, CellUtil.createCellScanner(cells.iterator()),
303 replicationClusterId, baseNamespaceDir, hfileArchiveDir);
304 Get get = new Get(Bytes.toBytes(1));
305 Result res = table1.get(get);
306 assertEquals(0, res.size());
307 }
308
309 @Test
310 public void testRethrowRetriesExhaustedWithDetailsException() throws Exception {
311 TableName notExistTable = TableName.valueOf("notExistTable");
312 List<WALEntry> entries = new ArrayList<>();
313 List<Cell> cells = new ArrayList<>();
314 for (int i = 0; i < 10; i++) {
315 entries.add(createEntry(notExistTable, i, KeyValue.Type.Put, cells));
316 }
317 try {
318 SINK.replicateEntries(entries, CellUtil.createCellScanner(cells.iterator()),
319 replicationClusterId, baseNamespaceDir, hfileArchiveDir);
320 Assert.fail("Should re-throw TableNotFoundException.");
321 } catch (TableNotFoundException e) {
322 }
323 entries.clear();
324 cells.clear();
325 for (int i = 0; i < 10; i++) {
326 entries.add(createEntry(TABLE_NAME1, i, KeyValue.Type.Put, cells));
327 }
328 try (Connection conn = ConnectionFactory.createConnection(TEST_UTIL.getConfiguration())) {
329 try (Admin admin = conn.getAdmin()) {
330 admin.disableTable(TABLE_NAME1);
331 try {
332 SINK.replicateEntries(entries, CellUtil.createCellScanner(cells.iterator()),
333 replicationClusterId, baseNamespaceDir, hfileArchiveDir);
334 Assert.fail("Should re-throw RetriesExhaustedWithDetailsException.");
335 } catch (RetriesExhaustedWithDetailsException e) {
336 } finally {
337 admin.enableTable(TABLE_NAME1);
338 }
339 }
340 }
341 }
342
343
344
345
346 @Test
347 public void testReplicateEntriesForHFiles() throws Exception {
348 Path dir = TEST_UTIL.getDataTestDirOnTestFS("testReplicateEntries");
349 Path familyDir = new Path(dir, Bytes.toString(FAM_NAME1));
350 int numRows = 10;
351
352 List<Path> p = new ArrayList<>(1);
353
354
355 Random rng = new SecureRandom();
356 Set<Integer> numbers = new HashSet<>();
357 while (numbers.size() < 50) {
358 numbers.add(rng.nextInt(1000));
359 }
360 List<Integer> numberList = new ArrayList<>(numbers);
361 Collections.sort(numberList);
362 Map<String, Long> storeFilesSize = new HashMap<String, Long>(1);
363
364
365 Configuration conf = TEST_UTIL.getConfiguration();
366 FileSystem fs = dir.getFileSystem(conf);
367 Iterator<Integer> numbersItr = numberList.iterator();
368 for (int i = 0; i < 25; i++) {
369 Path hfilePath = new Path(familyDir, "hfile_" + i);
370 HFileTestUtil.createHFile(conf, fs, hfilePath, FAM_NAME1, FAM_NAME1,
371 Bytes.toBytes(numbersItr.next()), Bytes.toBytes(numbersItr.next()), numRows);
372 p.add(hfilePath);
373 storeFilesSize.put(hfilePath.getName(), fs.getFileStatus(hfilePath).getLen());
374 }
375
376
377 Map<byte[], List<Path>> storeFiles = new HashMap<>(1);
378 storeFiles.put(FAM_NAME1, p);
379 WALEdit edit = null;
380 WALProtos.BulkLoadDescriptor loadDescriptor = null;
381
382 try (Connection c = ConnectionFactory.createConnection(conf);
383 RegionLocator l = c.getRegionLocator(TABLE_NAME1)) {
384 HRegionInfo regionInfo = l.getAllRegionLocations().get(0).getRegionInfo();
385 loadDescriptor =
386 ProtobufUtil.toBulkLoadDescriptor(TABLE_NAME1,
387 ByteStringer.wrap(regionInfo.getEncodedNameAsBytes()), storeFiles, storeFilesSize, 1);
388 edit = WALEdit.createBulkLoadEvent(regionInfo, loadDescriptor);
389 }
390 List<WALEntry> entries = new ArrayList<WALEntry>(1);
391
392
393 WALEntry.Builder builder = createWALEntryBuilder(TABLE_NAME1);
394
395
396 for (int i = 0; i < 25; i++) {
397 String pathToHfileFromNS =
398 new StringBuilder(100).append(TABLE_NAME1.getNamespaceAsString()).append(Path.SEPARATOR)
399 .append(Bytes.toString(TABLE_NAME1.getName())).append(Path.SEPARATOR)
400 .append(Bytes.toString(loadDescriptor.getEncodedRegionName().toByteArray()))
401 .append(Path.SEPARATOR).append(Bytes.toString(FAM_NAME1)).append(Path.SEPARATOR)
402 .append("hfile_" + i).toString();
403 String dst = baseNamespaceDir + Path.SEPARATOR + pathToHfileFromNS;
404
405 FileUtil.copy(fs, p.get(0), fs, new Path(dst), false, conf);
406 }
407
408 entries.add(builder.build());
409 try (ResultScanner scanner = table1.getScanner(new Scan())) {
410
411 assertEquals(0, scanner.next(numRows).length);
412 }
413
414 SINK.replicateEntries(entries, CellUtil.createCellScanner(edit.getCells().iterator()),
415 replicationClusterId, baseNamespaceDir, hfileArchiveDir);
416 try (ResultScanner scanner = table1.getScanner(new Scan())) {
417
418 assertEquals(numRows, scanner.next(numRows).length);
419 }
420 }
421
422 private WALEntry createEntry(TableName table, int row, KeyValue.Type type, List<Cell> cells) {
423 byte[] fam = table.equals(TABLE_NAME1) ? FAM_NAME1 : FAM_NAME2;
424 byte[] rowBytes = Bytes.toBytes(row);
425
426
427 try {
428 Thread.sleep(1);
429 } catch (InterruptedException e) {
430 LOG.info("Was interrupted while sleep, meh", e);
431 }
432 final long now = System.currentTimeMillis();
433 KeyValue kv = null;
434 if(type.getCode() == KeyValue.Type.Put.getCode()) {
435 kv = new KeyValue(rowBytes, fam, fam, now,
436 KeyValue.Type.Put, Bytes.toBytes(row));
437 } else if (type.getCode() == KeyValue.Type.DeleteColumn.getCode()) {
438 kv = new KeyValue(rowBytes, fam, fam,
439 now, KeyValue.Type.DeleteColumn);
440 } else if (type.getCode() == KeyValue.Type.DeleteFamily.getCode()) {
441 kv = new KeyValue(rowBytes, fam, null,
442 now, KeyValue.Type.DeleteFamily);
443 }
444 WALEntry.Builder builder = createWALEntryBuilder(table);
445 cells.add(kv);
446
447 return builder.build();
448 }
449
450 private WALEntry.Builder createWALEntryBuilder(TableName table) {
451 WALEntry.Builder builder = WALEntry.newBuilder();
452 builder.setAssociatedCellCount(1);
453 WALKey.Builder keyBuilder = WALKey.newBuilder();
454 UUID.Builder uuidBuilder = UUID.newBuilder();
455 uuidBuilder.setLeastSigBits(HConstants.DEFAULT_CLUSTER_ID.getLeastSignificantBits());
456 uuidBuilder.setMostSigBits(HConstants.DEFAULT_CLUSTER_ID.getMostSignificantBits());
457 keyBuilder.setClusterId(uuidBuilder.build());
458 keyBuilder.setTableName(ByteStringer.wrap(table.getName()));
459 keyBuilder.setWriteTime(System.currentTimeMillis());
460 keyBuilder.setEncodedRegionName(ByteStringer.wrap(HConstants.EMPTY_BYTE_ARRAY));
461 keyBuilder.setLogSequenceNumber(-1);
462 builder.setKey(keyBuilder.build());
463 return builder;
464 }
465 }