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  package org.apache.hadoop.hbase.regionserver.compactions;
19  
20  import static org.apache.hadoop.hbase.regionserver.StripeStoreFileManager.OPEN_KEY;
21  import static org.apache.hadoop.hbase.regionserver.compactions.TestCompactor.createDummyRequest;
22  import static org.junit.Assert.assertEquals;
23  import static org.mockito.Matchers.any;
24  import static org.mockito.Matchers.anyBoolean;
25  import static org.mockito.Matchers.anyLong;
26  import static org.mockito.Mockito.mock;
27  import static org.mockito.Mockito.when;
28  
29  import java.io.IOException;
30  import java.util.ArrayList;
31  import java.util.Arrays;
32  import java.util.List;
33  
34  import org.apache.hadoop.conf.Configuration;
35  import org.apache.hadoop.fs.FileSystem;
36  import org.apache.hadoop.fs.Path;
37  import org.apache.hadoop.hbase.CellUtil;
38  import org.apache.hadoop.hbase.HBaseConfiguration;
39  import org.apache.hadoop.hbase.HColumnDescriptor;
40  import org.apache.hadoop.hbase.HRegionInfo;
41  import org.apache.hadoop.hbase.KeyValue;
42  import org.apache.hadoop.hbase.KeyValue.KVComparator;
43  import org.apache.hadoop.hbase.TableName;
44  import org.apache.hadoop.hbase.io.compress.Compression;
45  import org.apache.hadoop.hbase.regionserver.InternalScanner;
46  import org.apache.hadoop.hbase.regionserver.ScanInfo;
47  import org.apache.hadoop.hbase.regionserver.ScanType;
48  import org.apache.hadoop.hbase.regionserver.Store;
49  import org.apache.hadoop.hbase.regionserver.StoreFileScanner;
50  import org.apache.hadoop.hbase.regionserver.compactions.TestCompactor.Scanner;
51  import org.apache.hadoop.hbase.regionserver.compactions.TestCompactor.StoreFileWritersCapture;
52  import org.apache.hadoop.hbase.regionserver.throttle.NoLimitThroughputController;
53  import org.apache.hadoop.hbase.testclassification.RegionServerTests;
54  import org.apache.hadoop.hbase.testclassification.SmallTests;
55  import org.apache.hadoop.hbase.util.Bytes;
56  import org.junit.Test;
57  import org.junit.experimental.categories.Category;
58  import org.junit.runner.RunWith;
59  import org.junit.runners.Parameterized;
60  import org.junit.runners.Parameterized.Parameter;
61  import org.junit.runners.Parameterized.Parameters;
62  
63  @RunWith(Parameterized.class)
64  @Category({ RegionServerTests.class, SmallTests.class })
65  public class TestStripeCompactor {
66    private static final byte[] NAME_OF_THINGS = Bytes.toBytes("foo");
67    private static final TableName TABLE_NAME = TableName.valueOf(NAME_OF_THINGS, NAME_OF_THINGS);
68  
69    private static final byte[] KEY_B = Bytes.toBytes("bbb");
70    private static final byte[] KEY_C = Bytes.toBytes("ccc");
71    private static final byte[] KEY_D = Bytes.toBytes("ddd");
72  
73    private static final KeyValue KV_A = kvAfter(Bytes.toBytes("aaa"));
74    private static final KeyValue KV_B = kvAfter(KEY_B);
75    private static final KeyValue KV_C = kvAfter(KEY_C);
76    private static final KeyValue KV_D = kvAfter(KEY_D);
77  
78    @Parameters(name = "{index}: usePrivateReaders={0}")
79    public static Iterable<Object[]> data() {
80      return Arrays.asList(new Object[] { true }, new Object[] { false });
81    }
82  
83    @Parameter
84    public boolean usePrivateReaders;
85  
86    private static KeyValue kvAfter(byte[] key) {
87      return new KeyValue(Arrays.copyOf(key, key.length + 1), 0L);
88    }
89  
90    @SuppressWarnings("unchecked")
91    private static <T> T[] a(T... a) {
92      return a;
93    }
94  
95    private static KeyValue[] e() {
96      return TestStripeCompactor.<KeyValue> a();
97    }
98  
99    @Test
100   public void testBoundaryCompactions() throws Exception {
101     // General verification
102     verifyBoundaryCompaction(a(KV_A, KV_A, KV_B, KV_B, KV_C, KV_D),
103       a(OPEN_KEY, KEY_B, KEY_D, OPEN_KEY), a(a(KV_A, KV_A), a(KV_B, KV_B, KV_C), a(KV_D)));
104     verifyBoundaryCompaction(a(KV_B, KV_C), a(KEY_B, KEY_C, KEY_D), a(a(KV_B), a(KV_C)));
105     verifyBoundaryCompaction(a(KV_B, KV_C), a(KEY_B, KEY_D), new KeyValue[][] { a(KV_B, KV_C) });
106   }
107 
108   @Test
109   public void testBoundaryCompactionEmptyFiles() throws Exception {
110     // No empty file if there're already files.
111     verifyBoundaryCompaction(a(KV_B), a(KEY_B, KEY_C, KEY_D, OPEN_KEY), a(a(KV_B), null, null),
112       null, null, false);
113     verifyBoundaryCompaction(a(KV_A, KV_C), a(OPEN_KEY, KEY_B, KEY_C, KEY_D),
114       a(a(KV_A), null, a(KV_C)), null, null, false);
115     // But should be created if there are no file.
116     verifyBoundaryCompaction(e(), a(OPEN_KEY, KEY_B, KEY_C, OPEN_KEY), a(null, null, e()), null,
117       null, false);
118     // In major range if there's major range.
119     verifyBoundaryCompaction(e(), a(OPEN_KEY, KEY_B, KEY_C, OPEN_KEY), a(null, e(), null), KEY_B,
120       KEY_C, false);
121     verifyBoundaryCompaction(e(), a(OPEN_KEY, KEY_B, KEY_C, OPEN_KEY), a(e(), e(), null), OPEN_KEY,
122       KEY_C, false);
123     // Major range should have files regardless of KVs.
124     verifyBoundaryCompaction(a(KV_A), a(OPEN_KEY, KEY_B, KEY_C, KEY_D, OPEN_KEY),
125       a(a(KV_A), e(), e(), null), KEY_B, KEY_D, false);
126     verifyBoundaryCompaction(a(KV_C), a(OPEN_KEY, KEY_B, KEY_C, KEY_D, OPEN_KEY),
127       a(null, null, a(KV_C), e()), KEY_C, OPEN_KEY, false);
128 
129   }
130 
131   private void verifyBoundaryCompaction(KeyValue[] input, byte[][] boundaries, KeyValue[][] output)
132       throws Exception {
133     verifyBoundaryCompaction(input, boundaries, output, null, null, true);
134   }
135 
136   private void verifyBoundaryCompaction(KeyValue[] input, byte[][] boundaries, KeyValue[][] output,
137       byte[] majorFrom, byte[] majorTo, boolean allFiles) throws Exception {
138     StoreFileWritersCapture writers = new StoreFileWritersCapture();
139     StripeCompactor sc = createCompactor(writers, input);
140     List<Path> paths = sc.compact(createDummyRequest(), Arrays.asList(boundaries), majorFrom,
141       majorTo, NoLimitThroughputController.INSTANCE, null);
142     writers.verifyKvs(output, allFiles, true);
143     if (allFiles) {
144       assertEquals(output.length, paths.size());
145       writers.verifyBoundaries(boundaries);
146     }
147   }
148 
149   @Test
150   public void testSizeCompactions() throws Exception {
151     // General verification with different sizes.
152     verifySizeCompaction(a(KV_A, KV_A, KV_B, KV_C, KV_D), 3, 2, OPEN_KEY, OPEN_KEY,
153       a(a(KV_A, KV_A), a(KV_B, KV_C), a(KV_D)));
154     verifySizeCompaction(a(KV_A, KV_B, KV_C, KV_D), 4, 1, OPEN_KEY, OPEN_KEY,
155       a(a(KV_A), a(KV_B), a(KV_C), a(KV_D)));
156     verifySizeCompaction(a(KV_B, KV_C), 2, 1, KEY_B, KEY_D, a(a(KV_B), a(KV_C)));
157     // Verify row boundaries are preserved.
158     verifySizeCompaction(a(KV_A, KV_A, KV_A, KV_C, KV_D), 3, 2, OPEN_KEY, OPEN_KEY,
159       a(a(KV_A, KV_A, KV_A), a(KV_C, KV_D)));
160     verifySizeCompaction(a(KV_A, KV_B, KV_B, KV_C), 3, 1, OPEN_KEY, OPEN_KEY,
161       a(a(KV_A), a(KV_B, KV_B), a(KV_C)));
162     // Too much data, count limits the number of files.
163     verifySizeCompaction(a(KV_A, KV_B, KV_C, KV_D), 2, 1, OPEN_KEY, OPEN_KEY,
164       a(a(KV_A), a(KV_B, KV_C, KV_D)));
165     verifySizeCompaction(a(KV_A, KV_B, KV_C), 1, Long.MAX_VALUE, OPEN_KEY, KEY_D,
166       new KeyValue[][] { a(KV_A, KV_B, KV_C) });
167     // Too little data/large count, no extra files.
168     verifySizeCompaction(a(KV_A, KV_B, KV_C, KV_D), Integer.MAX_VALUE, 2, OPEN_KEY, OPEN_KEY,
169       a(a(KV_A, KV_B), a(KV_C, KV_D)));
170   }
171 
172   private void verifySizeCompaction(KeyValue[] input, int targetCount, long targetSize, byte[] left,
173       byte[] right, KeyValue[][] output) throws Exception {
174     StoreFileWritersCapture writers = new StoreFileWritersCapture();
175     StripeCompactor sc = createCompactor(writers, input);
176     List<Path> paths = sc.compact(createDummyRequest(), targetCount, targetSize, left, right, null,
177       null, NoLimitThroughputController.INSTANCE, null);
178     assertEquals(output.length, paths.size());
179     writers.verifyKvs(output, true, true);
180     List<byte[]> boundaries = new ArrayList<byte[]>();
181     boundaries.add(left);
182     for (int i = 1; i < output.length; ++i) {
183       boundaries.add(CellUtil.cloneRow(output[i][0]));
184     }
185     boundaries.add(right);
186     writers.verifyBoundaries(boundaries.toArray(new byte[][] {}));
187   }
188 
189   private StripeCompactor createCompactor(StoreFileWritersCapture writers, KeyValue[] input)
190       throws Exception {
191     Configuration conf = HBaseConfiguration.create();
192     conf.setBoolean("hbase.regionserver.compaction.private.readers", usePrivateReaders);
193     final Scanner scanner = new Scanner(input);
194 
195     // Create store mock that is satisfactory for compactor.
196     HColumnDescriptor col = new HColumnDescriptor(NAME_OF_THINGS);
197     ScanInfo si = new ScanInfo(conf, col, Long.MAX_VALUE, 0, new KVComparator());
198     Store store = mock(Store.class);
199     when(store.getFamily()).thenReturn(col);
200     when(store.getScanInfo()).thenReturn(si);
201     when(store.areWritesEnabled()).thenReturn(true);
202     when(store.getFileSystem()).thenReturn(mock(FileSystem.class));
203     when(store.getRegionInfo()).thenReturn(new HRegionInfo(TABLE_NAME));
204     when(store.createWriterInTmp(anyLong(), any(Compression.Algorithm.class), anyBoolean(),
205       anyBoolean(), anyBoolean(), anyBoolean(), anyLong())).thenAnswer(writers);
206     when(store.getComparator()).thenReturn(new KVComparator());
207 
208     return new StripeCompactor(conf, store) {
209       @Override
210       protected InternalScanner createScanner(Store store, List<StoreFileScanner> scanners,
211           long smallestReadPoint, long earliestPutTs, byte[] dropDeletesFromRow,
212           byte[] dropDeletesToRow) throws IOException {
213         return scanner;
214       }
215 
216       @Override
217       protected InternalScanner createScanner(Store store, List<StoreFileScanner> scanners,
218           ScanType scanType, long smallestReadPoint, long earliestPutTs) throws IOException {
219         return scanner;
220       }
221     };
222   }
223 }