View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package org.apache.hadoop.hbase.regionserver;
20  
21  import static org.junit.Assert.assertEquals;
22  import static org.junit.Assert.assertFalse;
23  import static org.mockito.Mockito.mock;
24  import static org.mockito.Mockito.when;
25  
26  import org.apache.hadoop.conf.Configuration;
27  import org.apache.hadoop.fs.FileSystem;
28  import org.apache.hadoop.fs.Path;
29  import org.apache.hadoop.hbase.HBaseTestingUtility;
30  import org.apache.hadoop.hbase.HColumnDescriptor;
31  import org.apache.hadoop.hbase.HRegionInfo;
32  import org.apache.hadoop.hbase.HTableDescriptor;
33  import org.apache.hadoop.hbase.Stoppable;
34  import org.apache.hadoop.hbase.TableName;
35  import org.apache.hadoop.hbase.client.Put;
36  import org.apache.hadoop.hbase.testclassification.MediumTests;
37  import org.apache.hadoop.hbase.testclassification.RegionServerTests;
38  import org.apache.hadoop.hbase.util.Bytes;
39  import org.apache.hadoop.hbase.util.FSUtils;
40  import org.apache.hadoop.hbase.wal.WALFactory;
41  import org.junit.After;
42  import org.junit.Before;
43  import org.junit.Test;
44  import org.junit.experimental.categories.Category;
45  
46  import java.io.IOException;
47  import java.io.InterruptedIOException;
48  import java.util.ArrayList;
49  import java.util.Collection;
50  import java.util.List;
51  import java.util.concurrent.atomic.AtomicBoolean;
52  import java.util.concurrent.atomic.AtomicReference;
53  
54  /**
55   * Tests a race condition between archiving of compacted files in CompactedHFilesDischarger chore
56   * and HRegion.close();
57   */
58  @Category({RegionServerTests.class, MediumTests.class})
59  public class TestCompactionArchiveConcurrentClose {
60    public HBaseTestingUtility testUtil;
61  
62    private Path testDir;
63    private AtomicBoolean archived = new AtomicBoolean();
64  
65    @Before
66    public void setup() throws Exception {
67      testUtil = HBaseTestingUtility.createLocalHTU();
68      testDir = testUtil.getDataTestDir("TestStoreFileRefresherChore");
69      FSUtils.setRootDir(testUtil.getConfiguration(), testDir);
70    }
71  
72    @After
73    public void tearDown() throws Exception {
74      testUtil.cleanupTestDir();
75    }
76  
77    @Test
78    public void testStoreCloseAndDischargeRunningInParallel() throws Exception {
79      byte[] fam = Bytes.toBytes("f");
80      byte[] col = Bytes.toBytes("c");
81      byte[] val = Bytes.toBytes("val");
82  
83      TableName tableName = TableName.valueOf(getClass().getSimpleName());
84      HTableDescriptor htd = new HTableDescriptor(tableName);
85      htd.addFamily(new HColumnDescriptor(fam));
86      HRegionInfo info = new HRegionInfo(tableName, null, null, false);
87      final Region region = initHRegion(htd, info);
88      RegionServerServices rss = mock(RegionServerServices.class);
89      List<Region> regions = new ArrayList<Region>();
90      regions.add(region);
91      when(rss.getOnlineRegions()).thenReturn(regions);
92  
93      // Create the cleaner object
94      final CompactedHFilesDischarger cleaner =
95          new CompactedHFilesDischarger(1000, (Stoppable) null, rss, false);
96      // Add some data to the region and do some flushes
97      int batchSize = 10;
98      int fileCount = 10;
99      for (int f = 0; f < fileCount; f++) {
100       int start = f * batchSize;
101       for (int i = start; i < start + batchSize; i++) {
102         Put p = new Put(Bytes.toBytes("row" + i));
103         p.addColumn(fam, col, val);
104         region.put(p);
105       }
106       // flush them
107       region.flush(true);
108     }
109 
110     Store store = region.getStore(fam);
111     assertEquals(fileCount, store.getStorefilesCount());
112 
113     Collection<StoreFile> storefiles = store.getStorefiles();
114     // None of the files should be in compacted state.
115     for (StoreFile file : storefiles) {
116       assertFalse(file.isCompactedAway());
117     }
118     // Do compaction
119     region.compact(true);
120 
121     // now run the cleaner with a concurrent close
122     Thread cleanerThread = new Thread() {
123       public void run() {
124         cleaner.chore();
125       }
126     };
127     cleanerThread.start();
128     // wait for cleaner to pause
129     synchronized (archived) {
130       if (!archived.get()) {
131         archived.wait();
132       }
133     }
134     final AtomicReference<Exception> closeException = new AtomicReference<>();
135     Thread closeThread = new Thread() {
136       public void run() {
137         // wait for the chore to complete and call close
138         try {
139           ((HRegion) region).close();
140         } catch (IOException e) {
141           closeException.set(e);
142         }
143       }
144     };
145     closeThread.start();
146     // no error should occur after the execution of the test
147     closeThread.join();
148     cleanerThread.join();
149 
150     if (closeException.get() != null) {
151       throw closeException.get();
152     }
153   }
154 
155   private Region initHRegion(HTableDescriptor htd, HRegionInfo info)
156       throws IOException {
157     Configuration conf = testUtil.getConfiguration();
158     Path tableDir = FSUtils.getTableDir(testDir, htd.getTableName());
159 
160     HRegionFileSystem fs = new WaitingHRegionFileSystem(conf, tableDir.getFileSystem(conf),
161         tableDir, info);
162     final Configuration walConf = new Configuration(conf);
163     FSUtils.setRootDir(walConf, tableDir);
164     final WALFactory wals = new WALFactory(walConf, null, "log_" + info.getEncodedName());
165     HRegion region =
166         new HRegion(fs, wals.getWAL(info.getEncodedNameAsBytes(), info.getTable().getNamespace()),
167             conf, htd, null);
168 
169     region.initialize();
170 
171     return region;
172   }
173 
174   private class WaitingHRegionFileSystem extends HRegionFileSystem {
175 
176     public WaitingHRegionFileSystem(final Configuration conf, final FileSystem fs,
177         final Path tableDir, final HRegionInfo regionInfo) {
178       super(conf, fs, tableDir, regionInfo);
179     }
180 
181     @Override
182     public void removeStoreFiles(String familyName, Collection<StoreFile> storeFiles)
183         throws IOException {
184       super.removeStoreFiles(familyName, storeFiles);
185       archived.set(true);
186       synchronized (archived) {
187         archived.notifyAll();
188       }
189       try {
190         // unfortunately we can't use a stronger barrier here as the fix synchronizing
191         // the race condition will then block
192         Thread.sleep(100);
193       } catch (InterruptedException ie) {
194         throw new InterruptedIOException("Interrupted waiting for latch");
195       }
196     }
197   }
198 }