1
2
3
4
5
6
7
8
9
10
11 package org.apache.hadoop.hbase.regionserver.compactions;
12
13 import com.google.common.base.Preconditions;
14 import com.google.common.base.Predicate;
15 import com.google.common.collect.Collections2;
16 import com.google.common.collect.Lists;
17
18 import java.io.IOException;
19 import java.util.ArrayList;
20 import java.util.Collection;
21 import java.util.List;
22 import java.util.Random;
23
24 import org.apache.commons.logging.Log;
25 import org.apache.commons.logging.LogFactory;
26 import org.apache.hadoop.conf.Configuration;
27 import org.apache.hadoop.hbase.classification.InterfaceAudience;
28 import org.apache.hadoop.hbase.regionserver.StoreConfigInformation;
29 import org.apache.hadoop.hbase.regionserver.StoreFile;
30 import org.apache.hadoop.hbase.regionserver.StoreUtils;
31
32
33
34
35 @InterfaceAudience.Private
36 public abstract class SortedCompactionPolicy extends CompactionPolicy {
37
38 private static final Log LOG = LogFactory.getLog(SortedCompactionPolicy.class);
39
40 public SortedCompactionPolicy(Configuration conf, StoreConfigInformation storeConfigInfo) {
41 super(conf, storeConfigInfo);
42 }
43
44 public List<StoreFile> preSelectCompactionForCoprocessor(final Collection<StoreFile> candidates,
45 final List<StoreFile> filesCompacting) {
46 return getCurrentEligibleFiles(new ArrayList<StoreFile>(candidates), filesCompacting);
47 }
48
49
50
51
52
53
54
55 public CompactionRequest selectCompaction(Collection<StoreFile> candidateFiles,
56 final List<StoreFile> filesCompacting, final boolean isUserCompaction,
57 final boolean mayUseOffPeak, final boolean forceMajor) throws IOException {
58
59 ArrayList<StoreFile> candidateSelection = new ArrayList<StoreFile>(candidateFiles);
60
61
62
63 int futureFiles = filesCompacting.isEmpty() ? 0 : 1;
64 boolean mayBeStuck = (candidateFiles.size() - filesCompacting.size() + futureFiles)
65 >= storeConfigInfo.getBlockingFileCount();
66
67 candidateSelection = getCurrentEligibleFiles(candidateSelection, filesCompacting);
68 LOG.debug("Selecting compaction from " + candidateFiles.size() + " store files, " +
69 filesCompacting.size() + " compacting, " + candidateSelection.size() +
70 " eligible, " + storeConfigInfo.getBlockingFileCount() + " blocking");
71
72
73 boolean isAllFiles = candidateFiles.size() == candidateSelection.size();
74 if (!(forceMajor && isAllFiles)) {
75 candidateSelection = skipLargeFiles(candidateSelection, mayUseOffPeak);
76 isAllFiles = candidateFiles.size() == candidateSelection.size();
77 }
78
79
80
81 boolean isTryingMajor = (forceMajor && isAllFiles && isUserCompaction)
82 || (((forceMajor && isAllFiles) || shouldPerformMajorCompaction(candidateSelection))
83 && (candidateSelection.size() < comConf.getMaxFilesToCompact()));
84
85 boolean isAfterSplit = StoreUtils.hasReferences(candidateSelection);
86
87 CompactionRequest result = createCompactionRequest(candidateSelection,
88 isTryingMajor || isAfterSplit, mayUseOffPeak, mayBeStuck);
89
90 ArrayList<StoreFile> filesToCompact = Lists.newArrayList(result.getFiles());
91 removeExcessFiles(filesToCompact, isUserCompaction, isTryingMajor);
92 result.updateFiles(filesToCompact);
93
94 isAllFiles = (candidateFiles.size() == filesToCompact.size());
95 result.setOffPeak(!filesToCompact.isEmpty() && !isAllFiles && mayUseOffPeak);
96 result.setIsMajor(isTryingMajor && isAllFiles, isAllFiles);
97
98 return result;
99 }
100
101 protected abstract CompactionRequest createCompactionRequest(ArrayList<StoreFile>
102 candidateSelection, boolean tryingMajor, boolean mayUseOffPeak, boolean mayBeStuck)
103 throws IOException;
104
105
106
107
108
109 @Override
110 public abstract boolean shouldPerformMajorCompaction(final Collection<StoreFile> filesToCompact)
111 throws IOException;
112
113
114
115
116 private final Random random = new Random();
117
118
119
120
121
122 public long getNextMajorCompactTime(final Collection<StoreFile> filesToCompact) {
123
124 long ret = comConf.getMajorCompactionPeriod();
125 if (ret > 0) {
126
127 double jitterPct = comConf.getMajorCompactionJitter();
128 if (jitterPct > 0) {
129 long jitter = Math.round(ret * jitterPct);
130
131 Integer seed = StoreUtils.getDeterministicRandomSeed(filesToCompact);
132 if (seed != null) {
133
134 double rnd = -1;
135 synchronized (this) {
136 this.random.setSeed(seed);
137 rnd = this.random.nextDouble();
138 }
139 ret += jitter - Math.round(2L * jitter * rnd);
140 } else {
141 ret = 0;
142 }
143 }
144 }
145 return ret;
146 }
147
148
149
150
151
152 @Override
153 public boolean throttleCompaction(long compactionSize) {
154 return compactionSize > comConf.getThrottlePoint();
155 }
156
157 public abstract boolean needsCompaction(final Collection<StoreFile> storeFiles,
158 final List<StoreFile> filesCompacting);
159
160 protected ArrayList<StoreFile> getCurrentEligibleFiles(ArrayList<StoreFile> candidateFiles,
161 final List<StoreFile> filesCompacting) {
162
163 if (!filesCompacting.isEmpty()) {
164
165
166 StoreFile last = filesCompacting.get(filesCompacting.size() - 1);
167 int idx = candidateFiles.indexOf(last);
168 Preconditions.checkArgument(idx != -1);
169 candidateFiles.subList(0, idx + 1).clear();
170 }
171 return candidateFiles;
172 }
173
174
175
176
177
178
179 protected ArrayList<StoreFile> skipLargeFiles(ArrayList<StoreFile> candidates,
180 boolean mayUseOffpeak) {
181 int pos = 0;
182 while (pos < candidates.size() && !candidates.get(pos).isReference()
183 && (candidates.get(pos).getReader().length() > comConf.getMaxCompactSize(mayUseOffpeak))) {
184 ++pos;
185 }
186 if (pos > 0) {
187 LOG.debug("Some files are too large. Excluding " + pos
188 + " files from compaction candidates");
189 candidates.subList(0, pos).clear();
190 }
191 return candidates;
192 }
193
194
195
196
197
198 protected ArrayList<StoreFile> filterBulk(ArrayList<StoreFile> candidates) {
199 candidates.removeAll(Collections2.filter(candidates, new Predicate<StoreFile>() {
200 @Override
201 public boolean apply(StoreFile input) {
202 return input.excludeFromMinorCompaction();
203 }
204 }));
205 return candidates;
206 }
207
208
209
210
211 protected void removeExcessFiles(ArrayList<StoreFile> candidates,
212 boolean isUserCompaction, boolean isMajorCompaction) {
213 int excess = candidates.size() - comConf.getMaxFilesToCompact();
214 if (excess > 0) {
215 if (isMajorCompaction && isUserCompaction) {
216 LOG.debug("Warning, compacting more than " + comConf.getMaxFilesToCompact()
217 + " files because of a user-requested major compaction");
218 } else {
219 LOG.debug("Too many admissible files. Excluding " + excess
220 + " files from compaction candidates");
221 candidates.subList(comConf.getMaxFilesToCompact(), candidates.size()).clear();
222 }
223 }
224 }
225
226
227
228
229
230 protected ArrayList<StoreFile> checkMinFilesCriteria(ArrayList<StoreFile> candidates,
231 int minFiles) {
232 if (candidates.size() < minFiles) {
233 if (LOG.isDebugEnabled()) {
234 LOG.debug("Not compacting files because we only have " + candidates.size()
235 + " files ready for compaction. Need " + minFiles + " to initiate.");
236 }
237 candidates.clear();
238 }
239 return candidates;
240 }
241 }