001/** 002 * 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 */ 019package org.apache.hadoop.hbase.master; 020 021import java.io.IOException; 022import java.util.ArrayList; 023import java.util.Collections; 024import java.util.HashSet; 025import java.util.List; 026import java.util.Set; 027import java.util.concurrent.locks.Lock; 028import java.util.concurrent.locks.ReentrantLock; 029import java.util.stream.Collectors; 030import java.util.stream.Stream; 031import org.apache.hadoop.conf.Configuration; 032import org.apache.hadoop.fs.FileStatus; 033import org.apache.hadoop.fs.FileSystem; 034import org.apache.hadoop.fs.Path; 035import org.apache.hadoop.fs.PathFilter; 036import org.apache.hadoop.hbase.HConstants; 037import org.apache.hadoop.hbase.ServerName; 038import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 039import org.apache.hadoop.hbase.util.FSUtils; 040import org.apache.hadoop.hbase.wal.AbstractFSWALProvider; 041import org.apache.hadoop.hbase.wal.WALSplitter; 042import org.apache.yetus.audience.InterfaceAudience; 043import org.slf4j.Logger; 044import org.slf4j.LoggerFactory; 045import org.apache.hbase.thirdparty.com.google.common.annotations.VisibleForTesting; 046 047/** 048 * This class abstracts a bunch of operations the HMaster needs 049 * when splitting log files e.g. finding log files, dirs etc. 050 */ 051@InterfaceAudience.Private 052public class MasterWalManager { 053 private static final Logger LOG = LoggerFactory.getLogger(MasterWalManager.class); 054 055 final static PathFilter META_FILTER = new PathFilter() { 056 @Override 057 public boolean accept(Path p) { 058 return AbstractFSWALProvider.isMetaFile(p); 059 } 060 }; 061 062 final static PathFilter NON_META_FILTER = new PathFilter() { 063 @Override 064 public boolean accept(Path p) { 065 return !AbstractFSWALProvider.isMetaFile(p); 066 } 067 }; 068 069 // metrics for master 070 // TODO: Rename it, since those metrics are split-manager related 071 private final MetricsMasterFileSystem metricsMasterFilesystem = new MetricsMasterFileSystem(); 072 073 // Keep around for convenience. 074 private final MasterServices services; 075 private final Configuration conf; 076 private final FileSystem fs; 077 078 // The Path to the old logs dir 079 private final Path oldLogDir; 080 private final Path rootDir; 081 082 // create the split log lock 083 private final Lock splitLogLock = new ReentrantLock(); 084 private final SplitLogManager splitLogManager; 085 086 // Is the fileystem ok? 087 private volatile boolean fsOk = true; 088 089 public MasterWalManager(MasterServices services) throws IOException { 090 this(services.getConfiguration(), services.getMasterFileSystem().getWALFileSystem(), 091 services.getMasterFileSystem().getWALRootDir(), services); 092 } 093 094 public MasterWalManager(Configuration conf, FileSystem fs, Path rootDir, MasterServices services) 095 throws IOException { 096 this.fs = fs; 097 this.conf = conf; 098 this.rootDir = rootDir; 099 this.services = services; 100 this.splitLogManager = new SplitLogManager(services, conf); 101 102 this.oldLogDir = new Path(rootDir, HConstants.HREGION_OLDLOGDIR_NAME); 103 } 104 105 public void stop() { 106 if (splitLogManager != null) { 107 splitLogManager.stop(); 108 } 109 } 110 111 @VisibleForTesting 112 SplitLogManager getSplitLogManager() { 113 return this.splitLogManager; 114 } 115 116 /** 117 * Get the directory where old logs go 118 * @return the dir 119 */ 120 Path getOldLogDir() { 121 return this.oldLogDir; 122 } 123 124 public FileSystem getFileSystem() { 125 return this.fs; 126 } 127 128 /** 129 * Checks to see if the file system is still accessible. 130 * If not, sets closed 131 * @return false if file system is not available 132 */ 133 private boolean checkFileSystem() { 134 if (this.fsOk) { 135 try { 136 FSUtils.checkFileSystemAvailable(this.fs); 137 FSUtils.checkDfsSafeMode(this.conf); 138 } catch (IOException e) { 139 services.abort("Shutting down HBase cluster: file system not available", e); 140 this.fsOk = false; 141 } 142 } 143 return this.fsOk; 144 } 145 146 public Set<ServerName> getLiveServersFromWALDir() throws IOException { 147 Path walDirPath = new Path(rootDir, HConstants.HREGION_LOGDIR_NAME); 148 FileStatus[] walDirForLiveServers = FSUtils.listStatus(fs, walDirPath, 149 p -> !p.getName().endsWith(AbstractFSWALProvider.SPLITTING_EXT)); 150 if (walDirForLiveServers == null) { 151 return Collections.emptySet(); 152 } 153 return Stream.of(walDirForLiveServers).map(s -> { 154 ServerName serverName = AbstractFSWALProvider.getServerNameFromWALDirectoryName(s.getPath()); 155 if (serverName == null) { 156 LOG.warn("Log folder {} doesn't look like its name includes a " + 157 "region server name; leaving in place. If you see later errors about missing " + 158 "write ahead logs they may be saved in this location.", s.getPath()); 159 return null; 160 } 161 return serverName; 162 }).filter(s -> s != null).collect(Collectors.toSet()); 163 } 164 165 /** 166 * Inspect the log directory to find dead servers which need recovery work 167 * @return A set of ServerNames which aren't running but still have WAL files left in file system 168 * @deprecated With proc-v2, we can record the crash server with procedure store, so do not need 169 * to scan the wal directory to find out the splitting wal directory any more. Leave 170 * it here only because {@code RecoverMetaProcedure}(which is also deprecated) uses 171 * it. 172 */ 173 @Deprecated 174 public Set<ServerName> getFailedServersFromLogFolders() { 175 boolean retrySplitting = !conf.getBoolean("hbase.hlog.split.skip.errors", 176 WALSplitter.SPLIT_SKIP_ERRORS_DEFAULT); 177 178 Set<ServerName> serverNames = new HashSet<>(); 179 Path logsDirPath = new Path(this.rootDir, HConstants.HREGION_LOGDIR_NAME); 180 181 do { 182 if (services.isStopped()) { 183 LOG.warn("Master stopped while trying to get failed servers."); 184 break; 185 } 186 try { 187 if (!this.fs.exists(logsDirPath)) return serverNames; 188 FileStatus[] logFolders = FSUtils.listStatus(this.fs, logsDirPath, null); 189 // Get online servers after getting log folders to avoid log folder deletion of newly 190 // checked in region servers . see HBASE-5916 191 Set<ServerName> onlineServers = services.getServerManager().getOnlineServers().keySet(); 192 193 if (logFolders == null || logFolders.length == 0) { 194 LOG.debug("No log files to split, proceeding..."); 195 return serverNames; 196 } 197 for (FileStatus status : logFolders) { 198 FileStatus[] curLogFiles = FSUtils.listStatus(this.fs, status.getPath(), null); 199 if (curLogFiles == null || curLogFiles.length == 0) { 200 // Empty log folder. No recovery needed 201 continue; 202 } 203 final ServerName serverName = AbstractFSWALProvider.getServerNameFromWALDirectoryName( 204 status.getPath()); 205 if (null == serverName) { 206 LOG.warn("Log folder " + status.getPath() + " doesn't look like its name includes a " + 207 "region server name; leaving in place. If you see later errors about missing " + 208 "write ahead logs they may be saved in this location."); 209 } else if (!onlineServers.contains(serverName)) { 210 LOG.info("Log folder " + status.getPath() + " doesn't belong " 211 + "to a known region server, splitting"); 212 serverNames.add(serverName); 213 } else { 214 LOG.info("Log folder " + status.getPath() + " belongs to an existing region server"); 215 } 216 } 217 retrySplitting = false; 218 } catch (IOException ioe) { 219 LOG.warn("Failed getting failed servers to be recovered.", ioe); 220 if (!checkFileSystem()) { 221 LOG.warn("Bad Filesystem, exiting"); 222 Runtime.getRuntime().halt(1); 223 } 224 try { 225 if (retrySplitting) { 226 Thread.sleep(conf.getInt("hbase.hlog.split.failure.retry.interval", 30 * 1000)); 227 } 228 } catch (InterruptedException e) { 229 LOG.warn("Interrupted, aborting since cannot return w/o splitting"); 230 Thread.currentThread().interrupt(); 231 retrySplitting = false; 232 Runtime.getRuntime().halt(1); 233 } 234 } 235 } while (retrySplitting); 236 237 return serverNames; 238 } 239 240 public void splitLog(final ServerName serverName) throws IOException { 241 splitLog(Collections.<ServerName>singleton(serverName)); 242 } 243 244 /** 245 * Specialized method to handle the splitting for meta WAL 246 * @param serverName logs belonging to this server will be split 247 */ 248 public void splitMetaLog(final ServerName serverName) throws IOException { 249 splitMetaLog(Collections.<ServerName>singleton(serverName)); 250 } 251 252 /** 253 * Specialized method to handle the splitting for meta WAL 254 * @param serverNames logs belonging to these servers will be split 255 */ 256 public void splitMetaLog(final Set<ServerName> serverNames) throws IOException { 257 splitLog(serverNames, META_FILTER); 258 } 259 260 @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="UL_UNRELEASED_LOCK", justification= 261 "We only release this lock when we set it. Updates to code that uses it should verify use " + 262 "of the guard boolean.") 263 private List<Path> getLogDirs(final Set<ServerName> serverNames) throws IOException { 264 List<Path> logDirs = new ArrayList<>(); 265 boolean needReleaseLock = false; 266 if (!this.services.isInitialized()) { 267 // during master initialization, we could have multiple places splitting a same wal 268 // XXX: Does this still exist after we move to proc-v2? 269 this.splitLogLock.lock(); 270 needReleaseLock = true; 271 } 272 try { 273 for (ServerName serverName : serverNames) { 274 Path logDir = new Path(this.rootDir, 275 AbstractFSWALProvider.getWALDirectoryName(serverName.toString())); 276 Path splitDir = logDir.suffix(AbstractFSWALProvider.SPLITTING_EXT); 277 // Rename the directory so a rogue RS doesn't create more WALs 278 if (fs.exists(logDir)) { 279 if (!this.fs.rename(logDir, splitDir)) { 280 throw new IOException("Failed fs.rename for log split: " + logDir); 281 } 282 logDir = splitDir; 283 LOG.debug("Renamed region directory: " + splitDir); 284 } else if (!fs.exists(splitDir)) { 285 LOG.info("Log dir for server " + serverName + " does not exist"); 286 continue; 287 } 288 logDirs.add(splitDir); 289 } 290 } catch (IOException ioe) { 291 if (!checkFileSystem()) { 292 this.services.abort("Aborting due to filesystem unavailable", ioe); 293 throw ioe; 294 } 295 } finally { 296 if (needReleaseLock) { 297 this.splitLogLock.unlock(); 298 } 299 } 300 return logDirs; 301 } 302 303 public void splitLog(final Set<ServerName> serverNames) throws IOException { 304 splitLog(serverNames, NON_META_FILTER); 305 } 306 307 /** 308 * This method is the base split method that splits WAL files matching a filter. Callers should 309 * pass the appropriate filter for meta and non-meta WALs. 310 * @param serverNames logs belonging to these servers will be split; this will rename the log 311 * directory out from under a soft-failed server 312 */ 313 public void splitLog(final Set<ServerName> serverNames, PathFilter filter) throws IOException { 314 long splitTime = 0, splitLogSize = 0; 315 List<Path> logDirs = getLogDirs(serverNames); 316 317 splitLogManager.handleDeadWorkers(serverNames); 318 splitTime = EnvironmentEdgeManager.currentTime(); 319 splitLogSize = splitLogManager.splitLogDistributed(serverNames, logDirs, filter); 320 splitTime = EnvironmentEdgeManager.currentTime() - splitTime; 321 322 if (this.metricsMasterFilesystem != null) { 323 if (filter == META_FILTER) { 324 this.metricsMasterFilesystem.addMetaWALSplit(splitTime, splitLogSize); 325 } else { 326 this.metricsMasterFilesystem.addSplit(splitTime, splitLogSize); 327 } 328 } 329 } 330}