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