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