| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.hadoop.hbase; |
| |
| import java.io.BufferedReader; |
| import java.io.BufferedWriter; |
| import java.io.File; |
| import java.io.FileNotFoundException; |
| import java.io.FileReader; |
| import java.io.FileWriter; |
| import java.io.IOException; |
| import org.apache.hadoop.conf.Configuration; |
| import org.apache.hadoop.hbase.zookeeper.MasterAddressTracker; |
| import org.apache.hadoop.hbase.zookeeper.ZKWatcher; |
| import org.apache.yetus.audience.InterfaceAudience; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * <p> |
| * Contains a set of methods for the collaboration between the start/stop scripts and the servers. |
| * It allows to delete immediately the znode when the master or the regions server crashes. The |
| * region server / master writes a specific file when it starts / becomes main master. When they end |
| * properly, they delete the file. |
| * </p> |
| * <p> |
| * In the script, we check for the existence of these files when the program ends. If they still |
| * exist we conclude that the server crashed, likely without deleting their znode. To have a faster |
| * recovery we delete immediately the znode. |
| * </p> |
| * <p> |
| * The strategy depends on the server type. For a region server we store the znode path in the file, |
| * and use it to delete it. for a master, as the znode path constant whatever the server, we check |
| * its content to make sure that the backup server is not now in charge. |
| * </p> |
| */ |
| @InterfaceAudience.Private |
| public final class ZNodeClearer { |
| private static final Logger LOG = LoggerFactory.getLogger(ZNodeClearer.class); |
| |
| private ZNodeClearer() { |
| } |
| |
| /** |
| * Logs the errors without failing on exception. |
| */ |
| public static void writeMyEphemeralNodeOnDisk(String fileContent) { |
| String fileName = ZNodeClearer.getMyEphemeralNodeFileName(); |
| if (fileName == null) { |
| LOG.warn("Environment variable HBASE_ZNODE_FILE not set; znodes will not be cleared " |
| + "on crash by start scripts (Longer MTTR!)"); |
| return; |
| } |
| |
| FileWriter fstream; |
| try { |
| fstream = new FileWriter(fileName); |
| } catch (IOException e) { |
| LOG.warn("Can't write znode file " + fileName, e); |
| return; |
| } |
| |
| BufferedWriter out = new BufferedWriter(fstream); |
| |
| try { |
| try { |
| out.write(fileContent + "\n"); |
| } finally { |
| try { |
| out.close(); |
| } finally { |
| fstream.close(); |
| } |
| } |
| } catch (IOException e) { |
| LOG.warn("Can't write znode file " + fileName, e); |
| } |
| } |
| |
| /** |
| * read the content of znode file, expects a single line. |
| */ |
| public static String readMyEphemeralNodeOnDisk() throws IOException { |
| String fileName = getMyEphemeralNodeFileName(); |
| if (fileName == null) { |
| throw new FileNotFoundException("No filename; set environment variable HBASE_ZNODE_FILE"); |
| } |
| FileReader znodeFile = new FileReader(fileName); |
| BufferedReader br = null; |
| try { |
| br = new BufferedReader(znodeFile); |
| String file_content = br.readLine(); |
| return file_content; |
| } finally { |
| if (br != null) br.close(); |
| } |
| } |
| |
| /** |
| * Get the name of the file used to store the znode contents |
| */ |
| public static String getMyEphemeralNodeFileName() { |
| return System.getenv().get("HBASE_ZNODE_FILE"); |
| } |
| |
| /** |
| * delete the znode file |
| */ |
| public static void deleteMyEphemeralNodeOnDisk() { |
| String fileName = getMyEphemeralNodeFileName(); |
| |
| if (fileName != null) { |
| new File(fileName).delete(); |
| } |
| } |
| |
| /** |
| * See HBASE-14861. We are extracting master ServerName from rsZnodePath example: |
| * "/hbase/rs/server.example.com,16020,1448266496481" |
| * @param rsZnodePath from HBASE_ZNODE_FILE |
| * @return String representation of ServerName or null if fails |
| */ |
| |
| public static String parseMasterServerName(String rsZnodePath) { |
| String masterServerName = null; |
| try { |
| String[] rsZnodeParts = rsZnodePath.split("/"); |
| masterServerName = rsZnodeParts[rsZnodeParts.length - 1]; |
| } catch (IndexOutOfBoundsException e) { |
| LOG.warn("String " + rsZnodePath + " has wrong format", e); |
| } |
| return masterServerName; |
| } |
| |
| /** |
| * Delete the master znode if its content (ServerName string) is the same as the one in the znode |
| * file. (env: HBASE_ZNODE_FILE). I case of master-rs colloaction we extract ServerName string |
| * from rsZnode path.(HBASE-14861) |
| * @return true on successful deletion, false otherwise. |
| */ |
| public static boolean clear(Configuration conf) { |
| Configuration tempConf = new Configuration(conf); |
| tempConf.setInt("zookeeper.recovery.retry", 0); |
| |
| ZKWatcher zkw; |
| try { |
| zkw = new ZKWatcher(tempConf, "clean znode for master", new Abortable() { |
| @Override |
| public void abort(String why, Throwable e) { |
| } |
| |
| @Override |
| public boolean isAborted() { |
| return false; |
| } |
| }); |
| } catch (IOException e) { |
| LOG.warn("Can't connect to zookeeper to read the master znode", e); |
| return false; |
| } |
| |
| String znodeFileContent; |
| try { |
| znodeFileContent = ZNodeClearer.readMyEphemeralNodeOnDisk(); |
| return MasterAddressTracker.deleteIfEquals(zkw, znodeFileContent); |
| } catch (FileNotFoundException fnfe) { |
| // If no file, just keep going -- return success. |
| LOG.warn("Can't find the znode file; presume non-fatal", fnfe); |
| return true; |
| } catch (IOException e) { |
| LOG.warn("Can't read the content of the znode file", e); |
| return false; |
| } finally { |
| zkw.close(); |
| } |
| } |
| } |