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