1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.master;
20
21 import java.io.IOException;
22 import java.io.InterruptedIOException;
23 import java.util.ArrayList;
24 import java.util.HashSet;
25 import java.util.List;
26 import java.util.Set;
27 import java.util.concurrent.locks.Lock;
28 import java.util.concurrent.locks.ReentrantLock;
29
30 import org.apache.commons.logging.Log;
31 import org.apache.commons.logging.LogFactory;
32 import org.apache.hadoop.hbase.classification.InterfaceAudience;
33 import org.apache.hadoop.conf.Configuration;
34 import org.apache.hadoop.fs.FileStatus;
35 import org.apache.hadoop.fs.FileSystem;
36 import org.apache.hadoop.fs.Path;
37 import org.apache.hadoop.fs.PathFilter;
38 import org.apache.hadoop.fs.permission.FsPermission;
39 import org.apache.hadoop.hbase.ClusterId;
40 import org.apache.hadoop.hbase.TableName;
41 import org.apache.hadoop.hbase.HColumnDescriptor;
42 import org.apache.hadoop.hbase.HConstants;
43 import org.apache.hadoop.hbase.HRegionInfo;
44 import org.apache.hadoop.hbase.HTableDescriptor;
45 import org.apache.hadoop.hbase.RemoteExceptionHandler;
46 import org.apache.hadoop.hbase.Server;
47 import org.apache.hadoop.hbase.ServerName;
48 import org.apache.hadoop.hbase.backup.HFileArchiver;
49 import org.apache.hadoop.hbase.exceptions.DeserializationException;
50 import org.apache.hadoop.hbase.fs.HFileSystem;
51 import org.apache.hadoop.hbase.protobuf.generated.ZooKeeperProtos.SplitLogTask.RecoveryMode;
52 import org.apache.hadoop.hbase.regionserver.HRegion;
53 import org.apache.hadoop.hbase.wal.DefaultWALProvider;
54 import org.apache.hadoop.hbase.wal.WALSplitter;
55 import org.apache.hadoop.hbase.util.Bytes;
56 import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
57 import org.apache.hadoop.hbase.util.FSTableDescriptors;
58 import org.apache.hadoop.hbase.util.FSUtils;
59
60 import com.google.common.annotations.VisibleForTesting;
61
62
63
64
65
66
67 @InterfaceAudience.Private
68 public class MasterFileSystem {
69 private static final Log LOG = LogFactory.getLog(MasterFileSystem.class.getName());
70
71 Configuration conf;
72
73 Server master;
74
75 private final MetricsMasterFileSystem metricsMasterFilesystem = new MetricsMasterFileSystem();
76
77 private ClusterId clusterId;
78
79 private final FileSystem fs;
80
81 private volatile boolean fsOk = true;
82
83 private final Path oldLogDir;
84
85 private final Path rootdir;
86
87 private final Path tempdir;
88
89 final Lock splitLogLock = new ReentrantLock();
90 final boolean distributedLogReplay;
91 final SplitLogManager splitLogManager;
92 private final MasterServices services;
93
94 final static PathFilter META_FILTER = new PathFilter() {
95 @Override
96 public boolean accept(Path p) {
97 return DefaultWALProvider.isMetaFile(p);
98 }
99 };
100
101 final static PathFilter NON_META_FILTER = new PathFilter() {
102 @Override
103 public boolean accept(Path p) {
104 return !DefaultWALProvider.isMetaFile(p);
105 }
106 };
107
108 public MasterFileSystem(Server master, MasterServices services)
109 throws IOException {
110 this.conf = master.getConfiguration();
111 this.master = master;
112 this.services = services;
113
114
115
116
117 this.rootdir = FSUtils.getRootDir(conf);
118 this.tempdir = new Path(this.rootdir, HConstants.HBASE_TEMP_DIRECTORY);
119
120
121 this.fs = this.rootdir.getFileSystem(conf);
122 FSUtils.setFsDefault(conf, new Path(this.fs.getUri()));
123
124 fs.setConf(conf);
125
126
127 this.oldLogDir = createInitialFileSystemLayout();
128 HFileSystem.addLocationsOrderInterceptor(conf);
129 this.splitLogManager =
130 new SplitLogManager(master, master.getConfiguration(), master, services,
131 master.getServerName());
132 this.distributedLogReplay = this.splitLogManager.isLogReplaying();
133 }
134
135 @VisibleForTesting
136 SplitLogManager getSplitLogManager() {
137 return this.splitLogManager;
138 }
139
140
141
142
143
144
145
146
147
148
149
150 private Path createInitialFileSystemLayout() throws IOException {
151
152 checkRootDir(this.rootdir, conf, this.fs);
153
154
155 checkTempDir(this.tempdir, conf, this.fs);
156
157 Path oldLogDir = new Path(this.rootdir, HConstants.HREGION_OLDLOGDIR_NAME);
158
159
160 if(!this.fs.exists(oldLogDir)) {
161 this.fs.mkdirs(oldLogDir);
162 }
163
164 return oldLogDir;
165 }
166
167 public FileSystem getFileSystem() {
168 return this.fs;
169 }
170
171
172
173
174
175 public Path getOldLogDir() {
176 return this.oldLogDir;
177 }
178
179
180
181
182
183
184 public boolean checkFileSystem() {
185 if (this.fsOk) {
186 try {
187 FSUtils.checkFileSystemAvailable(this.fs);
188 FSUtils.checkDfsSafeMode(this.conf);
189 } catch (IOException e) {
190 master.abort("Shutting down HBase cluster: file system not available", e);
191 this.fsOk = false;
192 }
193 }
194 return this.fsOk;
195 }
196
197
198
199
200 public Path getRootDir() {
201 return this.rootdir;
202 }
203
204
205
206
207 public Path getTempDir() {
208 return this.tempdir;
209 }
210
211
212
213
214 public ClusterId getClusterId() {
215 return clusterId;
216 }
217
218
219
220
221
222 Set<ServerName> getFailedServersFromLogFolders() {
223 boolean retrySplitting = !conf.getBoolean("hbase.hlog.split.skip.errors",
224 WALSplitter.SPLIT_SKIP_ERRORS_DEFAULT);
225
226 Set<ServerName> serverNames = new HashSet<ServerName>();
227 Path logsDirPath = new Path(this.rootdir, HConstants.HREGION_LOGDIR_NAME);
228
229 do {
230 if (master.isStopped()) {
231 LOG.warn("Master stopped while trying to get failed servers.");
232 break;
233 }
234 try {
235 if (!this.fs.exists(logsDirPath)) return serverNames;
236 FileStatus[] logFolders = FSUtils.listStatus(this.fs, logsDirPath, null);
237
238
239 Set<ServerName> onlineServers = ((HMaster) master).getServerManager().getOnlineServers()
240 .keySet();
241
242 if (logFolders == null || logFolders.length == 0) {
243 LOG.debug("No log files to split, proceeding...");
244 return serverNames;
245 }
246 for (FileStatus status : logFolders) {
247 FileStatus[] curLogFiles = FSUtils.listStatus(this.fs, status.getPath(), null);
248 if (curLogFiles == null || curLogFiles.length == 0) {
249
250 continue;
251 }
252 final ServerName serverName = DefaultWALProvider.getServerNameFromWALDirectoryName(
253 status.getPath());
254 if (null == serverName) {
255 LOG.warn("Log folder " + status.getPath() + " doesn't look like its name includes a " +
256 "region server name; leaving in place. If you see later errors about missing " +
257 "write ahead logs they may be saved in this location.");
258 } else if (!onlineServers.contains(serverName)) {
259 LOG.info("Log folder " + status.getPath() + " doesn't belong "
260 + "to a known region server, splitting");
261 serverNames.add(serverName);
262 } else {
263 LOG.info("Log folder " + status.getPath() + " belongs to an existing region server");
264 }
265 }
266 retrySplitting = false;
267 } catch (IOException ioe) {
268 LOG.warn("Failed getting failed servers to be recovered.", ioe);
269 if (!checkFileSystem()) {
270 LOG.warn("Bad Filesystem, exiting");
271 Runtime.getRuntime().halt(1);
272 }
273 try {
274 if (retrySplitting) {
275 Thread.sleep(conf.getInt("hbase.hlog.split.failure.retry.interval", 30 * 1000));
276 }
277 } catch (InterruptedException e) {
278 LOG.warn("Interrupted, aborting since cannot return w/o splitting");
279 Thread.currentThread().interrupt();
280 retrySplitting = false;
281 Runtime.getRuntime().halt(1);
282 }
283 }
284 } while (retrySplitting);
285
286 return serverNames;
287 }
288
289 public void splitLog(final ServerName serverName) throws IOException {
290 Set<ServerName> serverNames = new HashSet<ServerName>();
291 serverNames.add(serverName);
292 splitLog(serverNames);
293 }
294
295
296
297
298
299
300 public void splitMetaLog(final ServerName serverName) throws IOException {
301 Set<ServerName> serverNames = new HashSet<ServerName>();
302 serverNames.add(serverName);
303 splitMetaLog(serverNames);
304 }
305
306
307
308
309
310
311 public void splitMetaLog(final Set<ServerName> serverNames) throws IOException {
312 splitLog(serverNames, META_FILTER);
313 }
314
315 @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="UL_UNRELEASED_LOCK", justification=
316 "We only release this lock when we set it. Updates to code that uses it should verify use " +
317 "of the guard boolean.")
318 private List<Path> getLogDirs(final Set<ServerName> serverNames) throws IOException {
319 List<Path> logDirs = new ArrayList<Path>();
320 boolean needReleaseLock = false;
321 if (!this.services.isInitialized()) {
322
323 this.splitLogLock.lock();
324 needReleaseLock = true;
325 }
326 try {
327 for (ServerName serverName : serverNames) {
328 Path logDir = new Path(this.rootdir,
329 DefaultWALProvider.getWALDirectoryName(serverName.toString()));
330 Path splitDir = logDir.suffix(DefaultWALProvider.SPLITTING_EXT);
331
332 if (fs.exists(logDir)) {
333 if (!this.fs.rename(logDir, splitDir)) {
334 throw new IOException("Failed fs.rename for log split: " + logDir);
335 }
336 logDir = splitDir;
337 LOG.debug("Renamed region directory: " + splitDir);
338 } else if (!fs.exists(splitDir)) {
339 LOG.info("Log dir for server " + serverName + " does not exist");
340 continue;
341 }
342 logDirs.add(splitDir);
343 }
344 } catch (IOException ioe) {
345 if (!checkFileSystem()) {
346 this.services.abort("Aborting due to filesystem unavailable", ioe);
347 throw ioe;
348 }
349 } finally {
350 if (needReleaseLock) {
351 this.splitLogLock.unlock();
352 }
353 }
354 return logDirs;
355 }
356
357
358
359
360
361
362
363 public void prepareLogReplay(ServerName serverName, Set<HRegionInfo> regions) throws IOException {
364 if (!this.distributedLogReplay) {
365 return;
366 }
367
368 if (regions == null || regions.isEmpty()) {
369 return;
370 }
371 this.splitLogManager.markRegionsRecovering(serverName, regions);
372 }
373
374 public void splitLog(final Set<ServerName> serverNames) throws IOException {
375 splitLog(serverNames, NON_META_FILTER);
376 }
377
378
379
380
381
382
383 void removeStaleRecoveringRegionsFromZK(final Set<ServerName> failedServers)
384 throws IOException, InterruptedIOException {
385 this.splitLogManager.removeStaleRecoveringRegions(failedServers);
386 }
387
388
389
390
391
392
393
394
395
396 public void splitLog(final Set<ServerName> serverNames, PathFilter filter) throws IOException {
397 long splitTime = 0, splitLogSize = 0;
398 List<Path> logDirs = getLogDirs(serverNames);
399
400 splitLogManager.handleDeadWorkers(serverNames);
401 splitTime = EnvironmentEdgeManager.currentTime();
402 splitLogSize = splitLogManager.splitLogDistributed(serverNames, logDirs, filter);
403 splitTime = EnvironmentEdgeManager.currentTime() - splitTime;
404
405 if (this.metricsMasterFilesystem != null) {
406 if (filter == META_FILTER) {
407 this.metricsMasterFilesystem.addMetaWALSplit(splitTime, splitLogSize);
408 } else {
409 this.metricsMasterFilesystem.addSplit(splitTime, splitLogSize);
410 }
411 }
412 }
413
414
415
416
417
418
419
420
421
422
423 @SuppressWarnings("deprecation")
424 private Path checkRootDir(final Path rd, final Configuration c,
425 final FileSystem fs)
426 throws IOException {
427
428 FSUtils.waitOnSafeMode(c, c.getInt(HConstants.THREAD_WAKE_FREQUENCY, 10 * 1000));
429
430 boolean isSecurityEnabled = "kerberos".equalsIgnoreCase(c.get("hbase.security.authentication"));
431 FsPermission rootDirPerms = new FsPermission(c.get("hbase.rootdir.perms", "700"));
432
433
434 try {
435 if (!fs.exists(rd)) {
436 if (isSecurityEnabled) {
437 fs.mkdirs(rd, rootDirPerms);
438 } else {
439 fs.mkdirs(rd);
440 }
441
442
443
444
445
446
447
448 FSUtils.setVersion(fs, rd, c.getInt(HConstants.THREAD_WAKE_FREQUENCY,
449 10 * 1000), c.getInt(HConstants.VERSION_FILE_WRITE_ATTEMPTS,
450 HConstants.DEFAULT_VERSION_FILE_WRITE_ATTEMPTS));
451 } else {
452 if (!fs.isDirectory(rd)) {
453 throw new IllegalArgumentException(rd.toString() + " is not a directory");
454 }
455 if (isSecurityEnabled && !rootDirPerms.equals(fs.getFileStatus(rd).getPermission())) {
456
457 LOG.warn("Found rootdir permissions NOT matching expected \"hbase.rootdir.perms\" for "
458 + "rootdir=" + rd.toString() + " permissions=" + fs.getFileStatus(rd).getPermission()
459 + " and \"hbase.rootdir.perms\" configured as "
460 + c.get("hbase.rootdir.perms", "700") + ". Automatically setting the permissions. You"
461 + " can change the permissions by setting \"hbase.rootdir.perms\" in hbase-site.xml "
462 + "and restarting the master");
463 fs.setPermission(rd, rootDirPerms);
464 }
465
466 FSUtils.checkVersion(fs, rd, true, c.getInt(HConstants.THREAD_WAKE_FREQUENCY,
467 10 * 1000), c.getInt(HConstants.VERSION_FILE_WRITE_ATTEMPTS,
468 HConstants.DEFAULT_VERSION_FILE_WRITE_ATTEMPTS));
469 }
470 } catch (DeserializationException de) {
471 LOG.fatal("Please fix invalid configuration for " + HConstants.HBASE_DIR, de);
472 IOException ioe = new IOException();
473 ioe.initCause(de);
474 throw ioe;
475 } catch (IllegalArgumentException iae) {
476 LOG.fatal("Please fix invalid configuration for "
477 + HConstants.HBASE_DIR + " " + rd.toString(), iae);
478 throw iae;
479 }
480
481 if (!FSUtils.checkClusterIdExists(fs, rd, c.getInt(
482 HConstants.THREAD_WAKE_FREQUENCY, 10 * 1000))) {
483 FSUtils.setClusterId(fs, rd, new ClusterId(), c.getInt(HConstants.THREAD_WAKE_FREQUENCY, 10 * 1000));
484 }
485 clusterId = FSUtils.getClusterId(fs, rd);
486
487
488 if (!FSUtils.metaRegionExists(fs, rd)) {
489 bootstrap(rd, c);
490 } else {
491
492 org.apache.hadoop.hbase.util.FSTableDescriptorMigrationToSubdir
493 .migrateFSTableDescriptorsIfNecessary(fs, rd);
494 }
495
496
497
498
499
500 FSTableDescriptors fsd = new FSTableDescriptors(c, fs, rd);
501 fsd.createTableDescriptor(
502 new HTableDescriptor(fsd.get(TableName.META_TABLE_NAME)));
503
504 return rd;
505 }
506
507
508
509
510
511 private void checkTempDir(final Path tmpdir, final Configuration c, final FileSystem fs)
512 throws IOException {
513
514 if (fs.exists(tmpdir)) {
515
516
517 for (Path tabledir: FSUtils.getTableDirs(fs, tmpdir)) {
518 for (Path regiondir: FSUtils.getRegionDirs(fs, tabledir)) {
519 HFileArchiver.archiveRegion(fs, this.rootdir, tabledir, regiondir);
520 }
521 }
522 if (!fs.delete(tmpdir, true)) {
523 throw new IOException("Unable to clean the temp directory: " + tmpdir);
524 }
525 }
526
527
528 if (!fs.mkdirs(tmpdir)) {
529 throw new IOException("HBase temp directory '" + tmpdir + "' creation failure.");
530 }
531 }
532
533 private static void bootstrap(final Path rd, final Configuration c)
534 throws IOException {
535 LOG.info("BOOTSTRAP: creating hbase:meta region");
536 try {
537
538
539
540
541 HRegionInfo metaHRI = new HRegionInfo(HRegionInfo.FIRST_META_REGIONINFO);
542 HTableDescriptor metaDescriptor = new FSTableDescriptors(c).get(TableName.META_TABLE_NAME);
543 setInfoFamilyCachingForMeta(metaDescriptor, false);
544 HRegion meta = HRegion.createHRegion(metaHRI, rd, c, metaDescriptor, null, true, true);
545 setInfoFamilyCachingForMeta(metaDescriptor, true);
546 HRegion.closeHRegion(meta);
547 } catch (IOException e) {
548 e = RemoteExceptionHandler.checkIOException(e);
549 LOG.error("bootstrap", e);
550 throw e;
551 }
552 }
553
554
555
556
557 public static void setInfoFamilyCachingForMeta(final HTableDescriptor metaDescriptor,
558 final boolean b) {
559 for (HColumnDescriptor hcd: metaDescriptor.getColumnFamilies()) {
560 if (Bytes.equals(hcd.getName(), HConstants.CATALOG_FAMILY)) {
561 hcd.setBlockCacheEnabled(b);
562 hcd.setInMemory(b);
563 }
564 }
565 }
566
567 public void deleteFamilyFromFS(HRegionInfo region, byte[] familyName)
568 throws IOException {
569
570 Path tableDir = FSUtils.getTableDir(rootdir, region.getTable());
571 HFileArchiver.archiveFamily(fs, conf, region, tableDir, familyName);
572
573
574 Path familyDir = new Path(tableDir,
575 new Path(region.getEncodedName(), Bytes.toString(familyName)));
576 if (fs.delete(familyDir, true) == false) {
577 if (fs.exists(familyDir)) {
578 throw new IOException("Could not delete family "
579 + Bytes.toString(familyName) + " from FileSystem for region "
580 + region.getRegionNameAsString() + "(" + region.getEncodedName()
581 + ")");
582 }
583 }
584 }
585
586 public void stop() {
587 if (splitLogManager != null) {
588 this.splitLogManager.stop();
589 }
590 }
591
592
593
594
595
596
597 public void setLogRecoveryMode() throws IOException {
598 this.splitLogManager.setRecoveryMode(false);
599 }
600
601 public RecoveryMode getLogRecoveryMode() {
602 return this.splitLogManager.getRecoveryMode();
603 }
604
605 public void logFileSystemState(Log log) throws IOException {
606 FSUtils.logFileSystemState(fs, rootdir, log);
607 }
608 }