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