001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.mapreduce; 019 020import static org.apache.hadoop.hbase.mapreduce.HFileOutputFormat2.MULTI_TABLE_HFILEOUTPUTFORMAT_CONF_KEY; 021import static org.hamcrest.CoreMatchers.equalTo; 022import static org.hamcrest.CoreMatchers.notNullValue; 023import static org.hamcrest.CoreMatchers.nullValue; 024import static org.hamcrest.MatcherAssert.assertThat; 025import static org.junit.jupiter.api.Assertions.assertEquals; 026import static org.junit.jupiter.api.Assertions.assertTrue; 027import static org.junit.jupiter.api.Assertions.fail; 028import static org.mockito.ArgumentMatchers.any; 029import static org.mockito.Mockito.doAnswer; 030import static org.mockito.Mockito.mock; 031import static org.mockito.Mockito.when; 032 033import java.io.ByteArrayOutputStream; 034import java.io.File; 035import java.io.IOException; 036import java.io.PrintStream; 037import java.nio.charset.StandardCharsets; 038import java.util.ArrayList; 039import java.util.concurrent.ThreadLocalRandom; 040import org.apache.hadoop.conf.Configuration; 041import org.apache.hadoop.fs.FSDataOutputStream; 042import org.apache.hadoop.fs.FileSystem; 043import org.apache.hadoop.fs.Path; 044import org.apache.hadoop.hbase.Cell; 045import org.apache.hadoop.hbase.CellUtil; 046import org.apache.hadoop.hbase.HBaseTestingUtil; 047import org.apache.hadoop.hbase.HConstants; 048import org.apache.hadoop.hbase.KeyValue; 049import org.apache.hadoop.hbase.NamespaceDescriptor; 050import org.apache.hadoop.hbase.SingleProcessHBaseCluster; 051import org.apache.hadoop.hbase.TableName; 052import org.apache.hadoop.hbase.client.Delete; 053import org.apache.hadoop.hbase.client.Get; 054import org.apache.hadoop.hbase.client.Put; 055import org.apache.hadoop.hbase.client.Result; 056import org.apache.hadoop.hbase.client.Table; 057import org.apache.hadoop.hbase.io.ImmutableBytesWritable; 058import org.apache.hadoop.hbase.mapreduce.WALPlayer.WALKeyValueMapper; 059import org.apache.hadoop.hbase.regionserver.TestRecoveredEdits; 060import org.apache.hadoop.hbase.testclassification.LargeTests; 061import org.apache.hadoop.hbase.testclassification.MapReduceTests; 062import org.apache.hadoop.hbase.tool.BulkLoadHFiles; 063import org.apache.hadoop.hbase.util.Bytes; 064import org.apache.hadoop.hbase.util.CommonFSUtils; 065import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 066import org.apache.hadoop.hbase.util.LauncherSecurityManager; 067import org.apache.hadoop.hbase.util.MapReduceExtendedCell; 068import org.apache.hadoop.hbase.wal.WAL; 069import org.apache.hadoop.hbase.wal.WALEdit; 070import org.apache.hadoop.hbase.wal.WALKey; 071import org.apache.hadoop.io.WritableComparable; 072import org.apache.hadoop.mapreduce.Mapper; 073import org.apache.hadoop.mapreduce.Mapper.Context; 074import org.apache.hadoop.util.ToolRunner; 075import org.junit.jupiter.api.AfterAll; 076import org.junit.jupiter.api.BeforeAll; 077import org.junit.jupiter.api.Tag; 078import org.junit.jupiter.api.Test; 079import org.junit.jupiter.api.TestInfo; 080import org.mockito.invocation.InvocationOnMock; 081import org.mockito.stubbing.Answer; 082 083/** 084 * Basic test for the WALPlayer M/R tool 085 */ 086@Tag(MapReduceTests.TAG) 087@Tag(LargeTests.TAG) 088public class TestWALPlayer { 089 090 private static final byte[] FAMILY = Bytes.toBytes("family"); 091 private static final byte[] COLUMN1 = Bytes.toBytes("c1"); 092 private static final byte[] COLUMN2 = Bytes.toBytes("c2"); 093 private static final byte[] ROW = Bytes.toBytes("row"); 094 095 private static final HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil(); 096 private static SingleProcessHBaseCluster cluster; 097 private static Path rootDir; 098 private static Path walRootDir; 099 private static FileSystem localFs; 100 private static FileSystem logFs; 101 private static Configuration conf; 102 private static FileSystem hdfs; 103 private static String bulkLoadOutputDir; 104 105 @BeforeAll 106 public static void beforeClass() throws Exception { 107 conf = TEST_UTIL.getConfiguration(); 108 rootDir = TEST_UTIL.createRootDir(); 109 walRootDir = TEST_UTIL.createWALRootDir(); 110 localFs = CommonFSUtils.getRootDirFileSystem(conf); 111 logFs = CommonFSUtils.getWALFileSystem(conf); 112 cluster = TEST_UTIL.startMiniCluster(); 113 hdfs = TEST_UTIL.getTestFileSystem(); 114 bulkLoadOutputDir = new Path(new Path(TEST_UTIL.getConfiguration().get("fs.defaultFS")), 115 Path.SEPARATOR + "bulkLoadOutput").toString(); 116 } 117 118 @AfterAll 119 public static void afterClass() throws Exception { 120 TEST_UTIL.shutdownMiniCluster(); 121 localFs.delete(rootDir, true); 122 logFs.delete(walRootDir, true); 123 } 124 125 /** 126 * Test that WALPlayer can replay recovered.edits files. 127 */ 128 @Test 129 public void testPlayingRecoveredEdit() throws Exception { 130 TableName tn = TableName.valueOf(TestRecoveredEdits.RECOVEREDEDITS_TABLENAME); 131 TEST_UTIL.createTable(tn, TestRecoveredEdits.RECOVEREDEDITS_COLUMNFAMILY); 132 // Copy testing recovered.edits file that is over under hbase-server test resources 133 // up into a dir in our little hdfs cluster here. 134 runWithDiskBasedSortingDisabledAndEnabled(() -> { 135 String hbaseServerTestResourcesEdits = 136 System.getProperty("test.build.classes") + "/../../../hbase-server/src/test/resources/" 137 + TestRecoveredEdits.RECOVEREDEDITS_PATH.getName(); 138 assertTrue(new File(hbaseServerTestResourcesEdits).exists()); 139 FileSystem dfs = TEST_UTIL.getDFSCluster().getFileSystem(); 140 // Target dir. 141 Path targetDir = new Path("edits").makeQualified(dfs.getUri(), dfs.getHomeDirectory()); 142 assertTrue(dfs.mkdirs(targetDir)); 143 dfs.copyFromLocalFile(new Path(hbaseServerTestResourcesEdits), targetDir); 144 assertEquals(0, 145 ToolRunner.run(new WALPlayer(this.conf), new String[] { targetDir.toString() })); 146 // I don't know how many edits are in this file for this table... so just check more than 1. 147 assertTrue(TEST_UTIL.countRows(tn) > 0); 148 dfs.delete(targetDir, true); 149 }); 150 } 151 152 /** 153 * Tests that when you write multiple cells with the same timestamp they are properly sorted by 154 * their sequenceId in WALPlayer/CellSortReducer so that the correct one wins when querying from 155 * the resulting bulkloaded HFiles. See HBASE-27649 156 */ 157 @Test 158 public void testWALPlayerBulkLoadWithOverriddenTimestamps(TestInfo testInfo) throws Exception { 159 final TableName tableName = TableName.valueOf(testInfo.getTestMethod().get().getName() + "1"); 160 final byte[] family = Bytes.toBytes("family"); 161 final byte[] column1 = Bytes.toBytes("c1"); 162 final byte[] column2 = Bytes.toBytes("c2"); 163 final byte[] row = Bytes.toBytes("row"); 164 final Table table = TEST_UTIL.createTable(tableName, family); 165 166 long now = EnvironmentEdgeManager.currentTime(); 167 // put a row into the first table 168 Put p = new Put(row); 169 p.addColumn(family, column1, now, column1); 170 p.addColumn(family, column2, now, column2); 171 172 table.put(p); 173 174 byte[] lastVal = null; 175 176 for (int i = 0; i < 50; i++) { 177 lastVal = Bytes.toBytes(ThreadLocalRandom.current().nextLong()); 178 p = new Put(row); 179 p.addColumn(family, column1, now, lastVal); 180 181 table.put(p); 182 183 // wal rolling is necessary to trigger the bug. otherwise no sorting 184 // needs to occur in the reducer because it's all sorted and coming from a single file. 185 if (i % 10 == 0) { 186 WAL log = cluster.getRegionServer(0).getWAL(null); 187 log.rollWriter(); 188 } 189 } 190 191 WAL log = cluster.getRegionServer(0).getWAL(null); 192 log.rollWriter(); 193 String walInputDir = new Path(cluster.getMaster().getMasterFileSystem().getWALRootDir(), 194 HConstants.HREGION_LOGDIR_NAME).toString(); 195 196 Configuration configuration = new Configuration(TEST_UTIL.getConfiguration()); 197 String outPath = "/tmp/" + testInfo.getTestMethod().get().getName(); 198 configuration.set(WALPlayer.BULK_OUTPUT_CONF_KEY, outPath); 199 configuration.setBoolean(WALPlayer.MULTI_TABLES_SUPPORT, true); 200 201 WALPlayer player = new WALPlayer(configuration); 202 final byte[] finalLastVal = lastVal; 203 204 runWithDiskBasedSortingDisabledAndEnabled(() -> { 205 assertEquals(0, ToolRunner.run(configuration, player, 206 new String[] { walInputDir, tableName.getNameAsString() })); 207 208 Get g = new Get(row); 209 Result result = table.get(g); 210 byte[] value = CellUtil.cloneValue(result.getColumnLatestCell(family, column1)); 211 assertThat(Bytes.toStringBinary(value), equalTo(Bytes.toStringBinary(finalLastVal))); 212 213 TEST_UTIL.truncateTable(tableName); 214 g = new Get(row); 215 result = table.get(g); 216 assertThat(result.listCells(), nullValue()); 217 218 BulkLoadHFiles.create(configuration).bulkLoad(tableName, 219 new Path(outPath, tableName.getNamespaceAsString() + "/" + tableName.getNameAsString())); 220 221 g = new Get(row); 222 result = table.get(g); 223 value = CellUtil.cloneValue(result.getColumnLatestCell(family, column1)); 224 225 assertThat(result.listCells(), notNullValue()); 226 assertThat(Bytes.toStringBinary(value), equalTo(Bytes.toStringBinary(finalLastVal))); 227 228 // cleanup 229 Path out = new Path(outPath); 230 FileSystem fs = out.getFileSystem(configuration); 231 assertTrue(fs.delete(out, true)); 232 }); 233 } 234 235 /** 236 * Simple end-to-end test 237 */ 238 @Test 239 public void testWALPlayer(TestInfo testInfo) throws Exception { 240 final TableName tableName1 = TableName.valueOf(testInfo.getTestMethod().get().getName() + "1"); 241 final TableName tableName2 = TableName.valueOf(testInfo.getTestMethod().get().getName() + "2"); 242 final byte[] FAMILY = Bytes.toBytes("family"); 243 final byte[] COLUMN1 = Bytes.toBytes("c1"); 244 final byte[] COLUMN2 = Bytes.toBytes("c2"); 245 final byte[] ROW = Bytes.toBytes("row"); 246 Table t1 = TEST_UTIL.createTable(tableName1, FAMILY); 247 Table t2 = TEST_UTIL.createTable(tableName2, FAMILY); 248 249 putRowIntoTable(t1); 250 251 // delete one column 252 Delete d = new Delete(ROW); 253 d.addColumns(FAMILY, COLUMN1); 254 t1.delete(d); 255 256 // replay the WAL, map table 1 to table 2 257 WAL log = cluster.getRegionServer(0).getWAL(null); 258 log.rollWriter(); 259 String walInputDir = new Path(cluster.getMaster().getMasterFileSystem().getWALRootDir(), 260 HConstants.HREGION_LOGDIR_NAME).toString(); 261 262 Configuration configuration = TEST_UTIL.getConfiguration(); 263 WALPlayer player = new WALPlayer(configuration); 264 265 runWithDiskBasedSortingDisabledAndEnabled(() -> { 266 String optionName = "_test_.name"; 267 configuration.set(optionName, "1000"); 268 player.setupTime(configuration, optionName); 269 assertEquals(1000, configuration.getLong(optionName, 0)); 270 assertEquals(0, ToolRunner.run(configuration, player, 271 new String[] { walInputDir, tableName1.getNameAsString(), tableName2.getNameAsString() })); 272 273 // verify the WAL was player into table 2 274 Get g = new Get(ROW); 275 Result r = t2.get(g); 276 assertEquals(1, r.size()); 277 assertTrue(CellUtil.matchingQualifier(r.rawCells()[0], COLUMN2)); 278 }); 279 } 280 281 /** 282 * Test WALKeyValueMapper setup and map 283 */ 284 @Test 285 public void testWALKeyValueMapper() throws Exception { 286 testWALKeyValueMapper(WALPlayer.TABLES_KEY); 287 } 288 289 @Test 290 public void testWALKeyValueMapperWithDeprecatedConfig() throws Exception { 291 testWALKeyValueMapper("hlog.input.tables"); 292 } 293 294 private void testWALKeyValueMapper(final String tableConfigKey) throws Exception { 295 Configuration configuration = new Configuration(); 296 configuration.set(tableConfigKey, "table"); 297 WALKeyValueMapper mapper = new WALKeyValueMapper(); 298 WALKey key = mock(WALKey.class); 299 when(key.getTableName()).thenReturn(TableName.valueOf("table")); 300 @SuppressWarnings("unchecked") 301 Mapper<WALKey, WALEdit, WritableComparable<?>, Cell>.Context context = mock(Context.class); 302 when(context.getConfiguration()).thenReturn(configuration); 303 304 WALEdit value = mock(WALEdit.class); 305 ArrayList<Cell> values = new ArrayList<>(); 306 KeyValue kv1 = new KeyValue(Bytes.toBytes("row"), Bytes.toBytes("family"), null); 307 308 values.add(kv1); 309 when(value.getCells()).thenReturn(values); 310 mapper.setup(context); 311 312 doAnswer(new Answer<Void>() { 313 314 @Override 315 public Void answer(InvocationOnMock invocation) throws Throwable { 316 ImmutableBytesWritable writer = (ImmutableBytesWritable) invocation.getArgument(0); 317 MapReduceExtendedCell key = (MapReduceExtendedCell) invocation.getArgument(1); 318 assertEquals("row", Bytes.toString(writer.get())); 319 assertEquals("row", Bytes.toString(CellUtil.cloneRow(key))); 320 return null; 321 } 322 }).when(context).write(any(), any()); 323 324 mapper.map(key, value, context); 325 326 } 327 328 /** 329 * Test main method 330 */ 331 @Test 332 public void testMainMethod() throws Exception { 333 334 PrintStream oldPrintStream = System.err; 335 SecurityManager SECURITY_MANAGER = System.getSecurityManager(); 336 LauncherSecurityManager newSecurityManager = new LauncherSecurityManager(); 337 System.setSecurityManager(newSecurityManager); 338 ByteArrayOutputStream data = new ByteArrayOutputStream(); 339 String[] args = {}; 340 System.setErr(new PrintStream(data)); 341 try { 342 System.setErr(new PrintStream(data)); 343 try { 344 WALPlayer.main(args); 345 fail("should be SecurityException"); 346 } catch (SecurityException e) { 347 assertEquals(-1, newSecurityManager.getExitCode()); 348 assertTrue(data.toString().contains("ERROR: Wrong number of arguments:")); 349 assertTrue(data.toString() 350 .contains("Usage: WALPlayer [options] <WAL inputdir>" + " [<tables> <tableMappings>]")); 351 assertTrue(data.toString().contains("-Dwal.bulk.output=/path/for/output")); 352 } 353 354 } finally { 355 System.setErr(oldPrintStream); 356 System.setSecurityManager(SECURITY_MANAGER); 357 } 358 } 359 360 private static void runWithDiskBasedSortingDisabledAndEnabled(TestMethod method) 361 throws Exception { 362 TEST_UTIL.getConfiguration().setBoolean(HFileOutputFormat2.DISK_BASED_SORTING_ENABLED_KEY, 363 false); 364 try { 365 method.run(); 366 } finally { 367 TEST_UTIL.getConfiguration().unset(HFileOutputFormat2.DISK_BASED_SORTING_ENABLED_KEY); 368 } 369 370 TEST_UTIL.getConfiguration().setBoolean(HFileOutputFormat2.DISK_BASED_SORTING_ENABLED_KEY, 371 true); 372 try { 373 method.run(); 374 } finally { 375 TEST_UTIL.getConfiguration().unset(HFileOutputFormat2.DISK_BASED_SORTING_ENABLED_KEY); 376 } 377 } 378 379 private interface TestMethod { 380 void run() throws Exception; 381 } 382 383 @Test 384 public void testIgnoreEmptyWALFiles() throws Exception { 385 Path inputDir = createEmptyWALFile("empty-wal-dir"); 386 FileSystem dfs = TEST_UTIL.getDFSCluster().getFileSystem(); 387 Path emptyWAL = new Path(inputDir, "empty.wal"); 388 389 assertTrue(dfs.exists(emptyWAL), "Empty WAL file should exist"); 390 assertEquals(0, dfs.getFileStatus(emptyWAL).getLen(), "WAL file should be 0 bytes"); 391 392 Configuration conf = new Configuration(TEST_UTIL.getConfiguration()); 393 conf.setBoolean(WALPlayer.IGNORE_EMPTY_FILES, true); 394 395 int exitCode = ToolRunner.run(conf, new WALPlayer(conf), new String[] { inputDir.toString() }); 396 assertEquals(0, exitCode, "WALPlayer should exit cleanly even with empty files"); 397 } 398 399 /** 400 * Verifies the HFile output format for WALPlayer has the following directory structure when 401 * {@value HFileOutputFormat2#MULTI_TABLE_HFILEOUTPUTFORMAT_CONF_KEY} is set to true:<br> 402 * <br> 403 * .../BULK_OUTPUT_CONF_KEY/namespace/tableName/columnFamily 404 */ 405 @Test 406 public void testWALPlayerMultiTableHFileOutputFormat(TestInfo testInfo) throws Exception { 407 String namespace = "ns_" + testInfo.getTestMethod().get().getName(); 408 TEST_UTIL.getAdmin().createNamespace(NamespaceDescriptor.create(namespace).build()); 409 final TableName tableName1 = TableName.valueOf(testInfo.getTestMethod().get().getName() + "1"); 410 final TableName tableName2 = 411 TableName.valueOf(namespace, testInfo.getTestMethod().get().getName() + "2"); 412 Table t1 = TEST_UTIL.createTable(tableName1, FAMILY); 413 Table t2 = TEST_UTIL.createTable(tableName2, FAMILY); 414 415 putRowIntoTable(t1); 416 putRowIntoTable(t2); 417 418 Configuration multiTableOutputConf = new Configuration(conf); 419 setConfSimilarToIncrementalBackupWALToHFilesMethod(testInfo, multiTableOutputConf); 420 421 // We are testing this config variable's effect on HFile output for the WALPlayer 422 multiTableOutputConf.setBoolean(MULTI_TABLE_HFILEOUTPUTFORMAT_CONF_KEY, true); 423 424 WALPlayer player = new WALPlayer(multiTableOutputConf); 425 String walInputDir = new Path(cluster.getMaster().getMasterFileSystem().getWALRootDir(), 426 HConstants.HREGION_LOGDIR_NAME).toString(); 427 String tables = tableName1.getNameAsString() + "," + tableName2.getNameAsString(); 428 429 ToolRunner.run(multiTableOutputConf, player, new String[] { walInputDir, tables }); 430 431 assertMultiTableOutputFormatDirStructure(tableName1, "default"); 432 assertMultiTableOutputFormatDirStructure(tableName2, namespace); 433 434 hdfs.delete(new Path(bulkLoadOutputDir), true); 435 } 436 437 /** 438 * Verifies the HFile output format for WALPlayer has the following directory structure when 439 * {@value HFileOutputFormat2#MULTI_TABLE_HFILEOUTPUTFORMAT_CONF_KEY} is set to false:<br> 440 * <br> 441 * .../BULK_OUTPUT_CONF_KEY/columnFamily <br> 442 * <br> 443 * Also verifies an exception occurs when the WALPlayer is run on multiple tables at once while 444 * hbase.mapreduce.use.multi.table.hfileoutputformat is set to false. 445 */ 446 @Test 447 public void testWALPlayerSingleTableHFileOutputFormat(TestInfo testInfo) throws Exception { 448 String namespace = "ns_" + testInfo.getTestMethod().get().getName(); 449 TEST_UTIL.getAdmin().createNamespace(NamespaceDescriptor.create(namespace).build()); 450 final TableName tableName1 = TableName.valueOf(testInfo.getTestMethod().get().getName() + "1"); 451 final TableName tableName2 = 452 TableName.valueOf(namespace, testInfo.getTestMethod().get().getName() + "2"); 453 Table t1 = TEST_UTIL.createTable(tableName1, FAMILY); 454 Table t2 = TEST_UTIL.createTable(tableName2, FAMILY); 455 456 putRowIntoTable(t1); 457 putRowIntoTable(t2); 458 459 String bulkLoadOutputDir = new Path(new Path(TEST_UTIL.getConfiguration().get("fs.defaultFS")), 460 Path.SEPARATOR + "bulkLoadOutput").toString(); 461 462 Configuration singleTableOutputConf = new Configuration(conf); 463 setConfSimilarToIncrementalBackupWALToHFilesMethod(testInfo, singleTableOutputConf); 464 465 // We are testing this config variable's effect on HFile output for the WALPlayer 466 singleTableOutputConf.setBoolean(MULTI_TABLE_HFILEOUTPUTFORMAT_CONF_KEY, false); 467 468 WALPlayer player = new WALPlayer(singleTableOutputConf); 469 470 String walInputDir = new Path(cluster.getMaster().getMasterFileSystem().getWALRootDir(), 471 HConstants.HREGION_LOGDIR_NAME).toString(); 472 String tables = tableName1.getNameAsString() + "," + tableName2.getNameAsString(); 473 474 // Expecting a failure here since we are running WALPlayer on multiple tables even though the 475 // multi-table HFile output format is disabled 476 try { 477 ToolRunner.run(singleTableOutputConf, player, new String[] { walInputDir, tables }); 478 fail("Expected a failure to occur due to using WALPlayer with multiple tables while having " 479 + MULTI_TABLE_HFILEOUTPUTFORMAT_CONF_KEY + " set to false"); 480 } catch (IOException e) { 481 String expectedMsg = "Expected table names list to have only one table since " 482 + MULTI_TABLE_HFILEOUTPUTFORMAT_CONF_KEY + " is set to false. Got the following " 483 + "list of tables instead: [testWALPlayerSingleTableHFileOutputFormat1, " + namespace 484 + ":testWALPlayerSingleTableHFileOutputFormat2]"; 485 assertTrue(e.getMessage().contains(expectedMsg)); 486 } 487 488 // Successfully run WALPlayer on just one table while having multi-table HFile output format 489 // disabled 490 ToolRunner.run(singleTableOutputConf, player, 491 new String[] { walInputDir, tableName1.getNameAsString() }); 492 493 Path bulkLoadOutputDirForTable = new Path(bulkLoadOutputDir, "family"); 494 assertTrue(hdfs.exists(bulkLoadOutputDirForTable), 495 "Expected path to exist: " + bulkLoadOutputDirForTable); 496 497 hdfs.delete(new Path(bulkLoadOutputDir), true); 498 } 499 500 private void putRowIntoTable(Table table) throws IOException { 501 Put p = new Put(ROW); 502 p.addColumn(FAMILY, COLUMN1, COLUMN1); 503 p.addColumn(FAMILY, COLUMN2, COLUMN2); 504 table.put(p); 505 } 506 507 private Path createEmptyWALFile(String walDir) throws IOException { 508 FileSystem dfs = TEST_UTIL.getDFSCluster().getFileSystem(); 509 Path inputDir = new Path("/" + walDir); 510 dfs.mkdirs(inputDir); 511 512 Path emptyWAL = new Path(inputDir, "empty.wal"); 513 FSDataOutputStream out = dfs.create(emptyWAL); 514 out.close(); // Explicitly closing the stream 515 516 return inputDir; 517 } 518 519 private void setConfSimilarToIncrementalBackupWALToHFilesMethod(TestInfo testInfo, 520 Configuration conf) { 521 conf.set(WALPlayer.BULK_OUTPUT_CONF_KEY, bulkLoadOutputDir); 522 conf.set(WALPlayer.INPUT_FILES_SEPARATOR_KEY, ";"); 523 conf.setBoolean(WALPlayer.MULTI_TABLES_SUPPORT, true); 524 conf.set("mapreduce.job.name", 525 testInfo.getTestMethod().get().getName() + "-" + System.currentTimeMillis()); 526 conf.setBoolean(HFileOutputFormat2.DISK_BASED_SORTING_ENABLED_KEY, true); 527 } 528 529 private void assertMultiTableOutputFormatDirStructure(TableName tableName, String namespace) 530 throws IOException { 531 Path qualifierAndFamilyDir = 532 new Path(tableName.getQualifierAsString(), new String(FAMILY, StandardCharsets.UTF_8)); 533 Path namespaceQualifierFamilyDir = new Path(namespace, qualifierAndFamilyDir); 534 Path bulkLoadOutputDirForTable = new Path(bulkLoadOutputDir, namespaceQualifierFamilyDir); 535 assertTrue(hdfs.exists(bulkLoadOutputDirForTable), 536 "Expected path to exist: " + bulkLoadOutputDirForTable); 537 } 538}