1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.hadoop.hbase;
19
20 import static org.junit.Assert.assertEquals;
21 import static org.junit.Assert.assertTrue;
22
23 import java.io.IOException;
24 import java.util.Collection;
25 import java.util.List;
26 import java.util.concurrent.CountDownLatch;
27
28 import org.apache.commons.logging.Log;
29 import org.apache.commons.logging.LogFactory;
30 import org.apache.hadoop.conf.Configuration;
31 import org.apache.hadoop.fs.FileSystem;
32 import org.apache.hadoop.fs.Path;
33 import org.apache.hadoop.hbase.client.Admin;
34 import org.apache.hadoop.hbase.client.HBaseAdmin;
35 import org.apache.hadoop.hbase.client.Table;
36 import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
37 import org.apache.hadoop.hbase.protobuf.generated.WALProtos.CompactionDescriptor;
38 import org.apache.hadoop.hbase.regionserver.ConstantSizeRegionSplitPolicy;
39 import org.apache.hadoop.hbase.regionserver.HRegion;
40 import org.apache.hadoop.hbase.regionserver.HRegionServer;
41 import org.apache.hadoop.hbase.regionserver.HStore;
42 import org.apache.hadoop.hbase.regionserver.Region;
43 import org.apache.hadoop.hbase.regionserver.RegionServerServices;
44 import org.apache.hadoop.hbase.regionserver.Store;
45 import org.apache.hadoop.hbase.regionserver.StoreFile;
46 import org.apache.hadoop.hbase.regionserver.compactions.CompactionContext;
47 import org.apache.hadoop.hbase.regionserver.throttle.ThroughputController;
48 import org.apache.hadoop.hbase.regionserver.wal.WALUtil;
49 import org.apache.hadoop.hbase.security.User;
50 import org.apache.hadoop.hbase.testclassification.MediumTests;
51 import org.apache.hadoop.hbase.util.Bytes;
52 import org.apache.hadoop.hbase.util.JVMClusterUtil.RegionServerThread;
53 import org.apache.hadoop.hbase.wal.WAL;
54 import org.junit.Test;
55 import org.junit.experimental.categories.Category;
56
57 import com.google.common.collect.Lists;
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77 @Category(MediumTests.class)
78 public class TestIOFencing {
79 private static final Log LOG = LogFactory.getLog(TestIOFencing.class);
80 static {
81
82
83
84
85
86
87
88
89 }
90
91 public abstract static class CompactionBlockerRegion extends HRegion {
92 volatile int compactCount = 0;
93 volatile CountDownLatch compactionsBlocked = new CountDownLatch(0);
94 volatile CountDownLatch compactionsWaiting = new CountDownLatch(0);
95
96 @SuppressWarnings("deprecation")
97 public CompactionBlockerRegion(Path tableDir, WAL log,
98 FileSystem fs, Configuration confParam, HRegionInfo info,
99 HTableDescriptor htd, RegionServerServices rsServices) {
100 super(tableDir, log, fs, confParam, info, htd, rsServices);
101 }
102
103 public void stopCompactions() {
104 compactionsBlocked = new CountDownLatch(1);
105 compactionsWaiting = new CountDownLatch(1);
106 }
107
108 public void allowCompactions() {
109 LOG.debug("allowing compactions");
110 compactionsBlocked.countDown();
111 }
112 public void waitForCompactionToBlock() throws IOException {
113 try {
114 LOG.debug("waiting for compaction to block");
115 compactionsWaiting.await();
116 LOG.debug("compaction block reached");
117 } catch (InterruptedException ex) {
118 throw new IOException(ex);
119 }
120 }
121
122 @Override
123 public boolean compact(CompactionContext compaction, Store store,
124 ThroughputController throughputController) throws IOException {
125 try {
126 return super.compact(compaction, store, throughputController);
127 } finally {
128 compactCount++;
129 }
130 }
131
132 @Override
133 public boolean compact(CompactionContext compaction, Store store,
134 ThroughputController throughputController, User user) throws IOException {
135 try {
136 return super.compact(compaction, store, throughputController, user);
137 } finally {
138 compactCount++;
139 }
140 }
141
142 public int countStoreFiles() {
143 int count = 0;
144 for (Store store : stores.values()) {
145 count += store.getStorefilesCount();
146 }
147 return count;
148 }
149 }
150
151
152
153
154
155 public static class BlockCompactionsInPrepRegion extends CompactionBlockerRegion {
156
157 public BlockCompactionsInPrepRegion(Path tableDir, WAL log,
158 FileSystem fs, Configuration confParam, HRegionInfo info,
159 HTableDescriptor htd, RegionServerServices rsServices) {
160 super(tableDir, log, fs, confParam, info, htd, rsServices);
161 }
162 @Override
163 protected void doRegionCompactionPrep() throws IOException {
164 compactionsWaiting.countDown();
165 try {
166 compactionsBlocked.await();
167 } catch (InterruptedException ex) {
168 throw new IOException();
169 }
170 super.doRegionCompactionPrep();
171 }
172 }
173
174
175
176
177
178
179 public static class BlockCompactionsInCompletionRegion extends CompactionBlockerRegion {
180 public BlockCompactionsInCompletionRegion(Path tableDir, WAL log,
181 FileSystem fs, Configuration confParam, HRegionInfo info,
182 HTableDescriptor htd, RegionServerServices rsServices) {
183 super(tableDir, log, fs, confParam, info, htd, rsServices);
184 }
185 @Override
186 protected HStore instantiateHStore(final HColumnDescriptor family) throws IOException {
187 return new BlockCompactionsInCompletionHStore(this, family, this.conf);
188 }
189 }
190
191 public static class BlockCompactionsInCompletionHStore extends HStore {
192 CompactionBlockerRegion r;
193 protected BlockCompactionsInCompletionHStore(HRegion region, HColumnDescriptor family,
194 Configuration confParam) throws IOException {
195 super(region, family, confParam);
196 r = (CompactionBlockerRegion) region;
197 }
198
199 @Override
200 protected void completeCompaction(Collection<StoreFile> compactedFiles) throws IOException {
201 try {
202 r.compactionsWaiting.countDown();
203 r.compactionsBlocked.await();
204 } catch (InterruptedException ex) {
205 throw new IOException(ex);
206 }
207 super.completeCompaction(compactedFiles);
208 }
209 }
210
211 private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
212 private final static TableName TABLE_NAME =
213 TableName.valueOf("tabletest");
214 private final static byte[] FAMILY = Bytes.toBytes("family");
215 private static final int FIRST_BATCH_COUNT = 4000;
216 private static final int SECOND_BATCH_COUNT = FIRST_BATCH_COUNT;
217
218
219
220
221
222
223
224 @Test
225 public void testFencingAroundCompaction() throws Exception {
226 doTest(BlockCompactionsInPrepRegion.class);
227 }
228
229
230
231
232
233
234
235 @Test
236 public void testFencingAroundCompactionAfterWALSync() throws Exception {
237 doTest(BlockCompactionsInCompletionRegion.class);
238 }
239
240 public void doTest(Class<?> regionClass) throws Exception {
241 Configuration c = TEST_UTIL.getConfiguration();
242
243 c.setClass(HConstants.REGION_IMPL, regionClass, HRegion.class);
244 c.setBoolean("dfs.support.append", true);
245
246 c.setLong("hbase.hregion.memstore.flush.size", 200000);
247 c.set(HConstants.HBASE_REGION_SPLIT_POLICY_KEY, ConstantSizeRegionSplitPolicy.class.getName());
248
249 c.setInt("hbase.hstore.compactionThreshold", 1000);
250 c.setLong("hbase.hstore.blockingStoreFiles", 1000);
251
252 c.setInt("hbase.regionserver.thread.splitcompactcheckfrequency", 1000);
253 LOG.info("Starting mini cluster");
254 TEST_UTIL.startMiniCluster(1);
255 CompactionBlockerRegion compactingRegion = null;
256 Admin admin = null;
257 try {
258 LOG.info("Creating admin");
259 admin = TEST_UTIL.getConnection().getAdmin();
260 LOG.info("Creating table");
261 TEST_UTIL.createTable(TABLE_NAME, FAMILY);
262 Table table = TEST_UTIL.getConnection().getTable(TABLE_NAME);
263 LOG.info("Loading test table");
264
265 List<HRegion> testRegions = TEST_UTIL.getMiniHBaseCluster().findRegionsForTable(TABLE_NAME);
266 assertEquals(1, testRegions.size());
267 compactingRegion = (CompactionBlockerRegion)testRegions.get(0);
268 LOG.info("Blocking compactions");
269 compactingRegion.stopCompactions();
270 long lastFlushTime = compactingRegion.getEarliestFlushTimeForAllStores();
271
272 TEST_UTIL.loadNumericRows(table, FAMILY, 0, FIRST_BATCH_COUNT);
273
274
275
276 HRegionInfo oldHri = new HRegionInfo(table.getName(),
277 HConstants.EMPTY_START_ROW, HConstants.EMPTY_END_ROW);
278 CompactionDescriptor compactionDescriptor = ProtobufUtil.toCompactionDescriptor(oldHri,
279 FAMILY, Lists.newArrayList(new Path("/a")), Lists.newArrayList(new Path("/b")),
280 new Path("store_dir"));
281 WALUtil.writeCompactionMarker(compactingRegion.getWAL(), table.getTableDescriptor(),
282 oldHri, compactionDescriptor, compactingRegion.getMVCC());
283
284
285 long startWaitTime = System.currentTimeMillis();
286 while (compactingRegion.getEarliestFlushTimeForAllStores() <= lastFlushTime ||
287 compactingRegion.countStoreFiles() <= 1) {
288 LOG.info("Waiting for the region to flush " +
289 compactingRegion.getRegionInfo().getRegionNameAsString());
290 Thread.sleep(1000);
291 assertTrue("Timed out waiting for the region to flush",
292 System.currentTimeMillis() - startWaitTime < 30000);
293 }
294 assertTrue(compactingRegion.countStoreFiles() > 1);
295 final byte REGION_NAME[] = compactingRegion.getRegionInfo().getRegionName();
296 LOG.info("Asking for compaction");
297 ((HBaseAdmin)admin).majorCompact(TABLE_NAME.getName());
298 LOG.info("Waiting for compaction to be about to start");
299 compactingRegion.waitForCompactionToBlock();
300 LOG.info("Starting a new server");
301 RegionServerThread newServerThread = TEST_UTIL.getMiniHBaseCluster().startRegionServer();
302 final HRegionServer newServer = newServerThread.getRegionServer();
303 LOG.info("Killing region server ZK lease");
304 TEST_UTIL.expireRegionServerSession(0);
305 CompactionBlockerRegion newRegion = null;
306 startWaitTime = System.currentTimeMillis();
307 LOG.info("Waiting for the new server to pick up the region " + Bytes.toString(REGION_NAME));
308
309
310 Waiter.waitFor(c, 60000, new Waiter.Predicate<Exception>() {
311 @Override
312 public boolean evaluate() throws Exception {
313 Region newRegion = newServer.getOnlineRegion(REGION_NAME);
314 return newRegion != null && !newRegion.isRecovering();
315 }
316 });
317
318 newRegion = (CompactionBlockerRegion)newServer.getOnlineRegion(REGION_NAME);
319
320
321
322 FileSystem fs = newRegion.getFilesystem();
323 for (String f: newRegion.getStoreFileList(new byte [][] {FAMILY})) {
324 assertTrue("After compaction, does not exist: " + f, fs.exists(new Path(f)));
325 }
326 LOG.info("Allowing compaction to proceed");
327 compactingRegion.allowCompactions();
328 while (compactingRegion.compactCount == 0) {
329 Thread.sleep(1000);
330 }
331
332
333 LOG.info("Compaction finished");
334
335
336
337 TEST_UTIL.loadNumericRows(table, FAMILY, FIRST_BATCH_COUNT, FIRST_BATCH_COUNT + SECOND_BATCH_COUNT);
338 ((HBaseAdmin)admin).majorCompact(TABLE_NAME.getName());
339 startWaitTime = System.currentTimeMillis();
340 while (newRegion.compactCount == 0) {
341 Thread.sleep(1000);
342 assertTrue("New region never compacted", System.currentTimeMillis() - startWaitTime < 180000);
343 }
344 assertEquals(FIRST_BATCH_COUNT + SECOND_BATCH_COUNT, TEST_UTIL.countRows(table));
345 } finally {
346 if (compactingRegion != null) {
347 compactingRegion.allowCompactions();
348 }
349 admin.close();
350 TEST_UTIL.shutdownMiniCluster();
351 }
352 }
353 }