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.regionserver; 019 020import static org.junit.jupiter.api.Assertions.assertEquals; 021import static org.junit.jupiter.api.Assertions.assertFalse; 022import static org.junit.jupiter.api.Assertions.assertNotNull; 023import static org.junit.jupiter.api.Assertions.assertTrue; 024import static org.mockito.ArgumentMatchers.any; 025import static org.mockito.ArgumentMatchers.eq; 026import static org.mockito.Mockito.doThrow; 027import static org.mockito.Mockito.mock; 028import static org.mockito.Mockito.spy; 029 030import java.io.IOException; 031import java.util.ArrayList; 032import java.util.Collection; 033import java.util.List; 034import org.apache.hadoop.conf.Configuration; 035import org.apache.hadoop.fs.FSDataOutputStream; 036import org.apache.hadoop.fs.FileSystem; 037import org.apache.hadoop.fs.Path; 038import org.apache.hadoop.hbase.HBaseTestingUtil; 039import org.apache.hadoop.hbase.Stoppable; 040import org.apache.hadoop.hbase.TableName; 041import org.apache.hadoop.hbase.backup.FailedArchiveException; 042import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder; 043import org.apache.hadoop.hbase.client.Put; 044import org.apache.hadoop.hbase.client.RegionInfo; 045import org.apache.hadoop.hbase.client.RegionInfoBuilder; 046import org.apache.hadoop.hbase.client.TableDescriptor; 047import org.apache.hadoop.hbase.client.TableDescriptorBuilder; 048import org.apache.hadoop.hbase.regionserver.storefiletracker.StoreFileTrackerForTest; 049import org.apache.hadoop.hbase.testclassification.MediumTests; 050import org.apache.hadoop.hbase.util.Bytes; 051import org.apache.hadoop.hbase.util.CommonFSUtils; 052import org.apache.hadoop.hbase.wal.WALFactory; 053import org.junit.jupiter.api.AfterEach; 054import org.junit.jupiter.api.BeforeEach; 055import org.junit.jupiter.api.Tag; 056import org.junit.jupiter.api.Test; 057import org.junit.jupiter.api.TestInfo; 058import org.mockito.Mockito; 059 060import org.apache.hbase.thirdparty.com.google.common.collect.ImmutableList; 061 062/** 063 * Tests that archiving compacted files behaves correctly when encountering exceptions. 064 */ 065@Tag(MediumTests.TAG) 066public class TestCompactionArchiveIOException { 067 068 private static final String ERROR_FILE = "fffffffffffffffffdeadbeef"; 069 070 public HBaseTestingUtil testUtil; 071 072 private Path testDir; 073 private String name; 074 075 @BeforeEach 076 public void setup(TestInfo testInfo) throws Exception { 077 this.name = testInfo.getTestMethod().get().getName(); 078 testUtil = new HBaseTestingUtil(); 079 testUtil.startMiniDFSCluster(1); 080 testDir = testUtil.getDataTestDirOnTestFS(); 081 CommonFSUtils.setRootDir(testUtil.getConfiguration(), testDir); 082 } 083 084 @AfterEach 085 public void tearDown() throws Exception { 086 testUtil.cleanupTestDir(); 087 testUtil.shutdownMiniDFSCluster(); 088 } 089 090 @Test 091 public void testRemoveCompactedFilesWithException() throws Exception { 092 byte[] fam = Bytes.toBytes("f"); 093 byte[] col = Bytes.toBytes("c"); 094 byte[] val = Bytes.toBytes("val"); 095 096 TableName tableName = TableName.valueOf(name); 097 TableDescriptor htd = TableDescriptorBuilder.newBuilder(tableName) 098 .setColumnFamily(ColumnFamilyDescriptorBuilder.of(fam)).build(); 099 RegionInfo info = RegionInfoBuilder.newBuilder(tableName).build(); 100 HRegion region = initHRegion(htd, info); 101 RegionServerServices rss = mock(RegionServerServices.class); 102 List<HRegion> regions = new ArrayList<>(); 103 regions.add(region); 104 Mockito.doReturn(regions).when(rss).getRegions(); 105 106 // Create the cleaner object 107 final CompactedHFilesDischarger cleaner = 108 new CompactedHFilesDischarger(1000, (Stoppable) null, rss, false); 109 // Add some data to the region and do some flushes 110 int batchSize = 10; 111 int fileCount = 10; 112 for (int f = 0; f < fileCount; f++) { 113 int start = f * batchSize; 114 for (int i = start; i < start + batchSize; i++) { 115 Put p = new Put(Bytes.toBytes("row" + i)); 116 p.addColumn(fam, col, val); 117 region.put(p); 118 } 119 // flush them 120 region.flush(true); 121 } 122 123 HStore store = region.getStore(fam); 124 assertEquals(fileCount, store.getStorefilesCount()); 125 126 Collection<HStoreFile> storefiles = store.getStorefiles(); 127 // None of the files should be in compacted state. 128 for (HStoreFile file : storefiles) { 129 assertFalse(file.isCompactedAway()); 130 } 131 132 StoreFileManager fileManager = store.getStoreEngine().getStoreFileManager(); 133 Collection<HStoreFile> initialCompactedFiles = fileManager.getCompactedfiles(); 134 assertTrue(initialCompactedFiles == null || initialCompactedFiles.isEmpty()); 135 136 // Do compaction 137 region.compact(true); 138 139 // all prior store files should now be compacted 140 Collection<HStoreFile> compactedFilesPreClean = fileManager.getCompactedfiles(); 141 assertNotNull(compactedFilesPreClean); 142 assertTrue(compactedFilesPreClean.size() > 0); 143 144 // add the dummy file to the store directory 145 HRegionFileSystem regionFS = region.getRegionFileSystem(); 146 Path errFile = regionFS.getStoreFilePath(Bytes.toString(fam), ERROR_FILE); 147 FSDataOutputStream out = regionFS.getFileSystem().create(errFile); 148 out.writeInt(1); 149 out.close(); 150 151 StoreFileTrackerForTest storeFileTrackerForTest = 152 new StoreFileTrackerForTest(store.getReadOnlyConfiguration(), true, store.getStoreContext()); 153 HStoreFile errStoreFile = 154 new MockHStoreFile(testUtil, errFile, 1, 0, false, 1, storeFileTrackerForTest); 155 fileManager.addCompactionResults(ImmutableList.of(errStoreFile), ImmutableList.of()); 156 157 // cleanup compacted files 158 cleaner.chore(); 159 160 // make sure the compacted files are cleared 161 Collection<HStoreFile> compactedFilesPostClean = fileManager.getCompactedfiles(); 162 assertEquals(1, compactedFilesPostClean.size()); 163 for (HStoreFile origFile : compactedFilesPreClean) { 164 assertFalse(compactedFilesPostClean.contains(origFile)); 165 } 166 167 // close the region 168 try { 169 region.close(); 170 } catch (FailedArchiveException e) { 171 // expected due to errorfile 172 assertEquals(1, e.getFailedFiles().size()); 173 assertEquals(ERROR_FILE, e.getFailedFiles().iterator().next().getName()); 174 } 175 } 176 177 private HRegion initHRegion(TableDescriptor htd, RegionInfo info) throws IOException { 178 Configuration conf = testUtil.getConfiguration(); 179 ChunkCreator.initialize(MemStoreLAB.CHUNK_SIZE_DEFAULT, false, 0, 0, 0, null, 180 MemStoreLAB.INDEX_CHUNK_SIZE_PERCENTAGE_DEFAULT); 181 Path tableDir = CommonFSUtils.getTableDir(testDir, htd.getTableName()); 182 Path regionDir = new Path(tableDir, info.getEncodedName()); 183 Path storeDir = new Path(regionDir, htd.getColumnFamilies()[0].getNameAsString()); 184 185 FileSystem errFS = spy(testUtil.getTestFileSystem()); 186 // Prior to HBASE-16964, when an exception is thrown archiving any compacted file, 187 // none of the other files are cleared from the compactedfiles list. 188 // Simulate this condition with a dummy file 189 doThrow(new IOException("Error for test")).when(errFS) 190 .rename(eq(new Path(storeDir, ERROR_FILE)), any()); 191 192 HRegionFileSystem fs = new HRegionFileSystem(conf, errFS, tableDir, info); 193 fs.createRegionOnFileSystem(conf, fs.getFileSystem(), tableDir, info); 194 final Configuration walConf = new Configuration(conf); 195 CommonFSUtils.setRootDir(walConf, tableDir); 196 final WALFactory wals = new WALFactory(walConf, "log_" + info.getEncodedName()); 197 HRegion region = new HRegion(fs, wals.getWAL(info), conf, htd, null); 198 199 region.initialize(); 200 201 return region; 202 } 203}