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