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.util.ArrayList;
23 import java.util.HashSet;
24 import java.util.List;
25 import java.util.NavigableMap;
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.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.hbase.ClusterId;
39 import org.apache.hadoop.hbase.HColumnDescriptor;
40 import org.apache.hadoop.hbase.HConstants;
41 import org.apache.hadoop.hbase.HRegionInfo;
42 import org.apache.hadoop.hbase.HTableDescriptor;
43 import org.apache.hadoop.hbase.RemoteExceptionHandler;
44 import org.apache.hadoop.hbase.Server;
45 import org.apache.hadoop.hbase.ServerName;
46 import org.apache.hadoop.hbase.backup.HFileArchiver;
47 import org.apache.hadoop.hbase.catalog.MetaReader;
48 import org.apache.hadoop.hbase.client.Result;
49 import org.apache.hadoop.hbase.exceptions.DeserializationException;
50 import org.apache.hadoop.hbase.exceptions.InvalidFamilyOperationException;
51 import org.apache.hadoop.hbase.exceptions.OrphanHLogAfterSplitException;
52 import org.apache.hadoop.hbase.fs.HFileSystem;
53 import org.apache.hadoop.hbase.regionserver.HRegion;
54 import org.apache.hadoop.hbase.regionserver.wal.HLog;
55 import org.apache.hadoop.hbase.regionserver.wal.HLogSplitter;
56 import org.apache.hadoop.hbase.regionserver.wal.HLogUtil;
57 import org.apache.hadoop.hbase.util.Bytes;
58 import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
59 import org.apache.hadoop.hbase.util.FSTableDescriptors;
60 import org.apache.hadoop.hbase.util.FSUtils;
61 import org.apache.zookeeper.KeeperException;
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 MetricsMaster metricsMaster;
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 boolean distributedLogSplitting;
93 final SplitLogManager splitLogManager;
94 private final MasterServices services;
95
96 private final static PathFilter META_FILTER = new PathFilter() {
97 public boolean accept(Path p) {
98 return HLogUtil.isMetaFile(p);
99 }
100 };
101
102 private final static PathFilter NON_META_FILTER = new PathFilter() {
103 public boolean accept(Path p) {
104 return !HLogUtil.isMetaFile(p);
105 }
106 };
107
108 public MasterFileSystem(Server master, MasterServices services,
109 MetricsMaster metricsMaster, boolean masterRecovery)
110 throws IOException {
111 this.conf = master.getConfiguration();
112 this.master = master;
113 this.services = services;
114 this.metricsMaster = metricsMaster;
115
116
117
118
119 this.rootdir = FSUtils.getRootDir(conf);
120 this.tempdir = new Path(this.rootdir, HConstants.HBASE_TEMP_DIRECTORY);
121
122
123 this.fs = this.rootdir.getFileSystem(conf);
124 FSUtils.setFsDefault(conf, new Path(this.fs.getUri()));
125
126 fs.setConf(conf);
127 this.splitLogManager = new SplitLogManager(master.getZooKeeper(), master.getConfiguration(),
128 master, services, master.getServerName());
129 this.distributedLogSplitting = conf.getBoolean(HConstants.DISTRIBUTED_LOG_SPLITTING_KEY, true);
130 if (this.distributedLogSplitting) {
131 this.splitLogManager.finishInitialization(masterRecovery);
132 }
133 this.distributedLogReplay = this.conf.getBoolean(HConstants.DISTRIBUTED_LOG_REPLAY_KEY,
134 HConstants.DEFAULT_DISTRIBUTED_LOG_REPLAY_CONFIG);
135
136
137 this.oldLogDir = createInitialFileSystemLayout();
138 HFileSystem.addLocationsOrderInterceptor(conf);
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 HLog.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 String sn = status.getPath().getName();
249
250 if (sn.endsWith(HLog.SPLITTING_EXT)) {
251 sn = sn.substring(0, sn.length() - HLog.SPLITTING_EXT.length());
252 }
253 ServerName serverName = ServerName.parseServerName(sn);
254 if (!onlineServers.contains(serverName)) {
255 LOG.info("Log folder " + status.getPath() + " doesn't belong "
256 + "to a known region server, splitting");
257 serverNames.add(serverName);
258 } else {
259 LOG.info("Log folder " + status.getPath() + " belongs to an existing region server");
260 }
261 }
262 retrySplitting = false;
263 } catch (IOException ioe) {
264 LOG.warn("Failed getting failed servers to be recovered.", ioe);
265 if (!checkFileSystem()) {
266 LOG.warn("Bad Filesystem, exiting");
267 Runtime.getRuntime().halt(1);
268 }
269 try {
270 if (retrySplitting) {
271 Thread.sleep(conf.getInt("hbase.hlog.split.failure.retry.interval", 30 * 1000));
272 }
273 } catch (InterruptedException e) {
274 LOG.warn("Interrupted, aborting since cannot return w/o splitting");
275 Thread.currentThread().interrupt();
276 retrySplitting = false;
277 Runtime.getRuntime().halt(1);
278 }
279 }
280 } while (retrySplitting);
281
282 return serverNames;
283 }
284
285 public void splitLog(final ServerName serverName) throws IOException {
286 Set<ServerName> serverNames = new HashSet<ServerName>();
287 serverNames.add(serverName);
288 splitLog(serverNames);
289 }
290
291
292
293
294
295
296 public void splitMetaLog(final ServerName serverName) throws IOException {
297 long splitTime = 0, splitLogSize = 0;
298 Set<ServerName> serverNames = new HashSet<ServerName>();
299 serverNames.add(serverName);
300 List<Path> logDirs = getLogDirs(serverNames);
301
302 splitLogManager.handleDeadWorkers(serverNames);
303 splitTime = EnvironmentEdgeManager.currentTimeMillis();
304 splitLogSize = splitLogManager.splitLogDistributed(serverNames, logDirs, META_FILTER);
305 splitTime = EnvironmentEdgeManager.currentTimeMillis() - splitTime;
306 if (this.metricsMaster != null) {
307 this.metricsMaster.addMetaWALSplit(splitTime, splitLogSize);
308 }
309 }
310
311 private List<Path> getLogDirs(final Set<ServerName> serverNames) throws IOException {
312 List<Path> logDirs = new ArrayList<Path>();
313 for (ServerName serverName: serverNames) {
314 Path logDir = new Path(this.rootdir, HLogUtil.getHLogDirectoryName(serverName.toString()));
315 Path splitDir = logDir.suffix(HLog.SPLITTING_EXT);
316
317 if (fs.exists(logDir)) {
318 if (!this.fs.rename(logDir, splitDir)) {
319 throw new IOException("Failed fs.rename for log split: " + logDir);
320 }
321 logDir = splitDir;
322 LOG.debug("Renamed region directory: " + splitDir);
323 } else if (!fs.exists(splitDir)) {
324 LOG.info("Log dir for server " + serverName + " does not exist");
325 continue;
326 }
327 logDirs.add(splitDir);
328 }
329 return logDirs;
330 }
331
332
333
334
335
336
337
338 public void prepareLogReplay(Set<ServerName> serverNames) throws IOException {
339 if (!this.distributedLogReplay) {
340 return;
341 }
342
343 for (ServerName serverName : serverNames) {
344 NavigableMap<HRegionInfo, Result> regions = this.getServerUserRegions(serverName);
345 if (regions == null) {
346 continue;
347 }
348 try {
349 this.splitLogManager.markRegionsRecoveringInZK(serverName, regions.keySet());
350 } catch (KeeperException e) {
351 throw new IOException(e);
352 }
353 }
354 }
355
356
357
358
359
360
361
362
363 public void prepareMetaLogReplay(ServerName serverName, Set<HRegionInfo> regions)
364 throws IOException {
365 if (!this.distributedLogReplay || (regions == null)) {
366 return;
367 }
368
369 try {
370 this.splitLogManager.markRegionsRecoveringInZK(serverName, regions);
371 } catch (KeeperException e) {
372 throw new IOException(e);
373 }
374 }
375
376 public void splitLog(final Set<ServerName> serverNames) throws IOException {
377 splitLog(serverNames, NON_META_FILTER);
378 }
379
380
381
382
383
384
385 void removeStaleRecoveringRegionsFromZK(final Set<ServerName> failedServers)
386 throws KeeperException {
387 this.splitLogManager.removeStaleRecoveringRegionsFromZK(failedServers);
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 if (distributedLogSplitting) {
402 splitLogManager.handleDeadWorkers(serverNames);
403 splitTime = EnvironmentEdgeManager.currentTimeMillis();
404 splitLogSize = splitLogManager.splitLogDistributed(serverNames, logDirs, filter);
405 splitTime = EnvironmentEdgeManager.currentTimeMillis() - splitTime;
406 } else {
407 for(Path logDir: logDirs){
408
409
410 this.splitLogLock.lock();
411 try {
412 HLogSplitter splitter = HLogSplitter.createLogSplitter(conf, rootdir, logDir, oldLogDir,
413 this.fs);
414 try {
415
416 FSUtils.waitOnSafeMode(conf, conf.getInt(HConstants.THREAD_WAKE_FREQUENCY, 1000));
417 splitter.splitLog();
418 } catch (OrphanHLogAfterSplitException e) {
419 LOG.warn("Retrying splitting because of:", e);
420
421 splitter = HLogSplitter.createLogSplitter(conf, rootdir, logDir,
422 oldLogDir, this.fs);
423 splitter.splitLog();
424 }
425 splitTime = splitter.getTime();
426 splitLogSize = splitter.getSize();
427 } finally {
428 this.splitLogLock.unlock();
429 }
430 }
431 }
432
433 if (this.metricsMaster != null) {
434 if (filter == this.META_FILTER) {
435 this.metricsMaster.addMetaWALSplit(splitTime, splitLogSize);
436 } else {
437 this.metricsMaster.addSplit(splitTime, splitLogSize);
438 }
439 }
440 }
441
442
443
444
445
446
447
448
449
450
451 private Path checkRootDir(final Path rd, final Configuration c,
452 final FileSystem fs)
453 throws IOException {
454
455 FSUtils.waitOnSafeMode(c, c.getInt(HConstants.THREAD_WAKE_FREQUENCY, 10 * 1000));
456
457 try {
458 if (!fs.exists(rd)) {
459 fs.mkdirs(rd);
460
461
462
463
464
465
466
467 FSUtils.setVersion(fs, rd, c.getInt(HConstants.THREAD_WAKE_FREQUENCY,
468 10 * 1000), c.getInt(HConstants.VERSION_FILE_WRITE_ATTEMPTS,
469 HConstants.DEFAULT_VERSION_FILE_WRITE_ATTEMPTS));
470 } else {
471 if (!fs.isDirectory(rd)) {
472 throw new IllegalArgumentException(rd.toString() + " is not a directory");
473 }
474
475 FSUtils.checkVersion(fs, rd, true, c.getInt(HConstants.THREAD_WAKE_FREQUENCY,
476 10 * 1000), c.getInt(HConstants.VERSION_FILE_WRITE_ATTEMPTS,
477 HConstants.DEFAULT_VERSION_FILE_WRITE_ATTEMPTS));
478 }
479 } catch (DeserializationException de) {
480 LOG.fatal("Please fix invalid configuration for " + HConstants.HBASE_DIR, de);
481 IOException ioe = new IOException();
482 ioe.initCause(de);
483 throw ioe;
484 } catch (IllegalArgumentException iae) {
485 LOG.fatal("Please fix invalid configuration for "
486 + HConstants.HBASE_DIR + " " + rd.toString(), iae);
487 throw iae;
488 }
489
490 if (!FSUtils.checkClusterIdExists(fs, rd, c.getInt(
491 HConstants.THREAD_WAKE_FREQUENCY, 10 * 1000))) {
492 FSUtils.setClusterId(fs, rd, new ClusterId(), c.getInt(HConstants.THREAD_WAKE_FREQUENCY, 10 * 1000));
493 }
494 clusterId = FSUtils.getClusterId(fs, rd);
495
496
497 if (!FSUtils.metaRegionExists(fs, rd)) {
498 bootstrap(rd, c);
499 }
500
501
502 FSTableDescriptors.createTableDescriptor(fs, rd, HTableDescriptor.META_TABLEDESC, false);
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 META region");
536 try {
537
538
539
540
541 HRegionInfo metaHRI = new HRegionInfo(HRegionInfo.FIRST_META_REGIONINFO);
542 setInfoFamilyCachingForMeta(false);
543 HRegion meta = HRegion.createHRegion(metaHRI, rd, c,
544 HTableDescriptor.META_TABLEDESC);
545 setInfoFamilyCachingForMeta(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 boolean b) {
558 for (HColumnDescriptor hcd:
559 HTableDescriptor.META_TABLEDESC.getColumnFamilies()) {
560 if (Bytes.equals(hcd.getName(), HConstants.CATALOG_FAMILY)) {
561 hcd.setBlockCacheEnabled(b);
562 hcd.setInMemory(b);
563 }
564 }
565 }
566
567
568 public void deleteRegion(HRegionInfo region) throws IOException {
569 HFileArchiver.archiveRegion(conf, fs, region);
570 }
571
572 public void deleteTable(byte[] tableName) throws IOException {
573 fs.delete(new Path(rootdir, Bytes.toString(tableName)), true);
574 }
575
576
577
578
579
580
581
582 public Path moveToTemp(final Path path) throws IOException {
583 Path tempPath = new Path(this.tempdir, path.getName());
584
585
586 if (!fs.exists(tempdir) && !fs.mkdirs(tempdir)) {
587 throw new IOException("HBase temp directory '" + tempdir + "' creation failure.");
588 }
589
590 if (!fs.rename(path, tempPath)) {
591 throw new IOException("Unable to move '" + path + "' to temp '" + tempPath + "'");
592 }
593
594 return tempPath;
595 }
596
597
598
599
600
601
602
603 public Path moveTableToTemp(byte[] tableName) throws IOException {
604 return moveToTemp(HTableDescriptor.getTableDir(this.rootdir, tableName));
605 }
606
607 public void updateRegionInfo(HRegionInfo region) {
608
609
610
611 }
612
613 public void deleteFamilyFromFS(HRegionInfo region, byte[] familyName)
614 throws IOException {
615
616 Path tableDir = new Path(rootdir, region.getTableNameAsString());
617 HFileArchiver.archiveFamily(fs, conf, region, tableDir, familyName);
618
619
620 Path familyDir = new Path(tableDir,
621 new Path(region.getEncodedName(), Bytes.toString(familyName)));
622 if (fs.delete(familyDir, true) == false) {
623 throw new IOException("Could not delete family "
624 + Bytes.toString(familyName) + " from FileSystem for region "
625 + region.getRegionNameAsString() + "(" + region.getEncodedName()
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 createTableDescriptor(HTableDescriptor htableDescriptor)
642 throws IOException {
643 FSTableDescriptors.createTableDescriptor(htableDescriptor, conf);
644 }
645
646
647
648
649
650
651
652
653 public HTableDescriptor deleteColumn(byte[] tableName, byte[] familyName)
654 throws IOException {
655 LOG.info("DeleteColumn. Table = " + Bytes.toString(tableName)
656 + " family = " + Bytes.toString(familyName));
657 HTableDescriptor htd = this.services.getTableDescriptors().get(tableName);
658 htd.removeFamily(familyName);
659 this.services.getTableDescriptors().add(htd);
660 return htd;
661 }
662
663
664
665
666
667
668
669
670 public HTableDescriptor modifyColumn(byte[] tableName, HColumnDescriptor hcd)
671 throws IOException {
672 LOG.info("AddModifyColumn. Table = " + Bytes.toString(tableName)
673 + " HCD = " + hcd.toString());
674
675 HTableDescriptor htd = this.services.getTableDescriptors().get(tableName);
676 byte [] familyName = hcd.getName();
677 if(!htd.hasFamily(familyName)) {
678 throw new InvalidFamilyOperationException("Family '" +
679 Bytes.toString(familyName) + "' doesn't exists so cannot be modified");
680 }
681 htd.addFamily(hcd);
682 this.services.getTableDescriptors().add(htd);
683 return htd;
684 }
685
686
687
688
689
690
691
692
693 public HTableDescriptor addColumn(byte[] tableName, HColumnDescriptor hcd)
694 throws IOException {
695 LOG.info("AddColumn. Table = " + Bytes.toString(tableName) + " HCD = " +
696 hcd.toString());
697 HTableDescriptor htd = this.services.getTableDescriptors().get(tableName);
698 if (htd == null) {
699 throw new InvalidFamilyOperationException("Family '" +
700 hcd.getNameAsString() + "' cannot be modified as HTD is null");
701 }
702 htd.addFamily(hcd);
703 this.services.getTableDescriptors().add(htd);
704 return htd;
705 }
706
707 private NavigableMap<HRegionInfo, Result> getServerUserRegions(ServerName serverName)
708 throws IOException {
709 if (!this.master.isStopped()) {
710 try {
711 this.master.getCatalogTracker().waitForMeta();
712 return MetaReader.getServerUserRegions(this.master.getCatalogTracker(), serverName);
713 } catch (InterruptedException e) {
714 Thread.currentThread().interrupt();
715 throw new IOException("Interrupted", e);
716 }
717 }
718 return null;
719 }
720 }