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