1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.hadoop.hbase.master;
21
22 import java.io.IOException;
23 import java.util.Comparator;
24 import java.util.Map;
25 import java.util.TreeMap;
26 import java.util.concurrent.atomic.AtomicInteger;
27
28 import org.apache.commons.logging.Log;
29 import org.apache.commons.logging.LogFactory;
30 import org.apache.hadoop.fs.FileStatus;
31 import org.apache.hadoop.fs.FileSystem;
32 import org.apache.hadoop.fs.Path;
33 import org.apache.hadoop.fs.PathFilter;
34 import org.apache.hadoop.hbase.Chore;
35 import org.apache.hadoop.hbase.HColumnDescriptor;
36 import org.apache.hadoop.hbase.HConstants;
37 import org.apache.hadoop.hbase.HRegionInfo;
38 import org.apache.hadoop.hbase.Server;
39 import org.apache.hadoop.hbase.catalog.MetaEditor;
40 import org.apache.hadoop.hbase.catalog.MetaReader;
41 import org.apache.hadoop.hbase.client.Result;
42 import org.apache.hadoop.hbase.regionserver.HRegion;
43 import org.apache.hadoop.hbase.regionserver.Store;
44 import org.apache.hadoop.hbase.regionserver.StoreFile;
45 import org.apache.hadoop.hbase.util.Bytes;
46 import org.apache.hadoop.hbase.util.Pair;
47 import org.apache.hadoop.hbase.util.Writables;
48
49
50
51
52
53 class CatalogJanitor extends Chore {
54 private static final Log LOG = LogFactory.getLog(CatalogJanitor.class.getName());
55 private final Server server;
56 private final MasterServices services;
57 private boolean enabled = true;
58
59 CatalogJanitor(final Server server, final MasterServices services) {
60 super(server.getServerName() + "-CatalogJanitor",
61 server.getConfiguration().getInt("hbase.catalogjanitor.interval", 300000),
62 server);
63 this.server = server;
64 this.services = services;
65 }
66
67 @Override
68 protected boolean initialChore() {
69 try {
70 if (this.enabled) scan();
71 } catch (IOException e) {
72 LOG.warn("Failed initial scan of catalog table", e);
73 return false;
74 }
75 return true;
76 }
77
78
79
80
81 public void setEnabled(final boolean enabled) {
82 this.enabled = enabled;
83 }
84
85 @Override
86 protected void chore() {
87 try {
88 scan();
89 } catch (IOException e) {
90 LOG.warn("Failed scan of catalog table", e);
91 }
92 }
93
94
95
96
97
98
99 void scan() throws IOException {
100
101 final AtomicInteger count = new AtomicInteger(0);
102
103
104 final Map<HRegionInfo, Result> splitParents =
105 new TreeMap<HRegionInfo, Result>(new Comparator<HRegionInfo> () {
106 @Override
107 public int compare(HRegionInfo left, HRegionInfo right) {
108
109
110 if (left == null) return -1;
111 if (right == null) return 1;
112
113 int result = Bytes.compareTo(left.getTableDesc().getName(),
114 right.getTableDesc().getName());
115 if (result != 0) return result;
116
117 result = Bytes.compareTo(left.getStartKey(), right.getStartKey());
118 if (result != 0) return result;
119
120 result = Bytes.compareTo(left.getEndKey(), right.getEndKey());
121 if (result != 0) return -result;
122 return result;
123 }
124 });
125
126 MetaReader.Visitor visitor = new MetaReader.Visitor() {
127 @Override
128 public boolean visit(Result r) throws IOException {
129 if (r == null || r.isEmpty()) return true;
130 count.incrementAndGet();
131 HRegionInfo info = getHRegionInfo(r);
132 if (info == null) return true;
133 if (info.isSplitParent()) splitParents.put(info, r);
134
135 return true;
136 }
137 };
138
139 MetaReader.fullScan(this.server.getCatalogTracker(), visitor);
140
141 int cleaned = 0;
142 for (Map.Entry<HRegionInfo, Result> e : splitParents.entrySet()) {
143 if (cleanParent(e.getKey(), e.getValue())) cleaned++;
144 }
145 if (cleaned != 0) {
146 LOG.info("Scanned " + count.get() + " catalog row(s) and gc'd " + cleaned +
147 " unreferenced parent region(s)");
148 } else if (LOG.isDebugEnabled()) {
149 LOG.debug("Scanned " + count.get() + " catalog row(s) and gc'd " + cleaned +
150 " unreferenced parent region(s)");
151 }
152 }
153
154
155
156
157
158
159
160
161 static HRegionInfo getHRegionInfo(final Result result)
162 throws IOException {
163 byte [] bytes =
164 result.getValue(HConstants.CATALOG_FAMILY, HConstants.REGIONINFO_QUALIFIER);
165 if (bytes == null) {
166 LOG.warn("REGIONINFO_QUALIFIER is empty in " + result);
167 return null;
168 }
169 return Writables.getHRegionInfo(bytes);
170 }
171
172
173
174
175
176
177
178
179
180
181
182 boolean cleanParent(final HRegionInfo parent, Result rowContent)
183 throws IOException {
184 boolean result = false;
185
186 Pair<Boolean, Boolean> a =
187 checkDaughter(parent, rowContent, HConstants.SPLITA_QUALIFIER);
188 Pair<Boolean, Boolean> b =
189 checkDaughter(parent, rowContent, HConstants.SPLITB_QUALIFIER);
190 if ((a.getFirst() && !a.getSecond()) && (b.getFirst() && !b.getSecond())) {
191 LOG.debug("Deleting region " + parent.getRegionNameAsString() +
192 " because daughter splits no longer hold references");
193
194
195 if (this.services.getAssignmentManager() != null) {
196
197
198 this.services.getAssignmentManager().regionOffline(parent);
199 }
200 FileSystem fs = this.services.getMasterFileSystem().getFileSystem();
201 Path rootdir = this.services.getMasterFileSystem().getRootDir();
202 HRegion.deleteRegion(fs, rootdir, parent);
203 MetaEditor.deleteRegion(this.server.getCatalogTracker(), parent);
204 result = true;
205 }
206 return result;
207 }
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222 Pair<Boolean, Boolean> checkDaughter(final HRegionInfo parent,
223 final Result rowContent, final byte [] qualifier)
224 throws IOException {
225 HRegionInfo hri = getDaughterRegionInfo(rowContent, qualifier);
226 Pair<Boolean, Boolean> result =
227 checkDaughterInFs(parent, rowContent, hri, qualifier);
228 if (result.getFirst() && !result.getSecond()) {
229
230
231
232
233
234 removeDaughterFromParent(parent, hri, qualifier);
235 }
236 return result;
237 }
238
239
240
241
242
243
244
245
246
247 private HRegionInfo getDaughterRegionInfo(final Result result,
248 final byte [] which)
249 throws IOException {
250 byte [] bytes = result.getValue(HConstants.CATALOG_FAMILY, which);
251 return Writables.getHRegionInfoOrNull(bytes);
252 }
253
254
255
256
257
258
259
260
261
262
263
264 private void removeDaughterFromParent(final HRegionInfo parent,
265 final HRegionInfo split, final byte [] qualifier)
266 throws IOException {
267 MetaEditor.deleteDaughterReferenceInParent(this.server.getCatalogTracker(),
268 parent, qualifier, split);
269 }
270
271
272
273
274
275
276
277
278
279
280
281
282
283 Pair<Boolean, Boolean> checkDaughterInFs(final HRegionInfo parent,
284 final Result rowContent, final HRegionInfo split,
285 final byte [] qualifier)
286 throws IOException {
287 boolean references = false;
288 boolean exists = false;
289 if (split == null) {
290 return new Pair<Boolean, Boolean>(Boolean.FALSE, Boolean.FALSE);
291 }
292 FileSystem fs = this.services.getMasterFileSystem().getFileSystem();
293 Path rootdir = this.services.getMasterFileSystem().getRootDir();
294 Path tabledir = new Path(rootdir, split.getTableDesc().getNameAsString());
295 Path regiondir = new Path(tabledir, split.getEncodedName());
296 exists = fs.exists(regiondir);
297 if (!exists) {
298 LOG.warn("Daughter regiondir does not exist: " + regiondir.toString());
299 return new Pair<Boolean, Boolean>(exists, Boolean.FALSE);
300 }
301 for (HColumnDescriptor family: split.getTableDesc().getFamilies()) {
302 Path p = Store.getStoreHomedir(tabledir, split.getEncodedName(),
303 family.getName());
304
305 FileStatus [] ps = fs.listStatus(p,
306 new PathFilter () {
307 public boolean accept(Path path) {
308 return StoreFile.isReference(path);
309 }
310 }
311 );
312
313 if (ps != null && ps.length > 0) {
314 references = true;
315 break;
316 }
317 }
318 return new Pair<Boolean, Boolean>(Boolean.valueOf(exists),
319 Boolean.valueOf(references));
320 }
321 }