001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase; 019 020import java.io.BufferedReader; 021import java.io.BufferedWriter; 022import java.io.File; 023import java.io.FileNotFoundException; 024import java.io.FileReader; 025import java.io.FileWriter; 026import java.io.IOException; 027import org.apache.hadoop.conf.Configuration; 028import org.apache.hadoop.hbase.zookeeper.MasterAddressTracker; 029import org.apache.hadoop.hbase.zookeeper.ZKWatcher; 030import org.apache.yetus.audience.InterfaceAudience; 031import org.slf4j.Logger; 032import org.slf4j.LoggerFactory; 033 034/** 035 * <p> 036 * Contains a set of methods for the collaboration between the start/stop scripts and the servers. 037 * It allows to delete immediately the znode when the master or the regions server crashes. The 038 * region server / master writes a specific file when it starts / becomes main master. When they end 039 * properly, they delete the file. 040 * </p> 041 * <p> 042 * In the script, we check for the existence of these files when the program ends. If they still 043 * exist we conclude that the server crashed, likely without deleting their znode. To have a faster 044 * recovery we delete immediately the znode. 045 * </p> 046 * <p> 047 * The strategy depends on the server type. For a region server we store the znode path in the file, 048 * and use it to delete it. for a master, as the znode path constant whatever the server, we check 049 * its content to make sure that the backup server is not now in charge. 050 * </p> 051 */ 052@InterfaceAudience.Private 053public final class ZNodeClearer { 054 private static final Logger LOG = LoggerFactory.getLogger(ZNodeClearer.class); 055 056 private ZNodeClearer() { 057 } 058 059 /** 060 * Logs the errors without failing on exception. 061 */ 062 public static void writeMyEphemeralNodeOnDisk(String fileContent) { 063 String fileName = ZNodeClearer.getMyEphemeralNodeFileName(); 064 if (fileName == null) { 065 LOG.warn("Environment variable HBASE_ZNODE_FILE not set; znodes will not be cleared " 066 + "on crash by start scripts (Longer MTTR!)"); 067 return; 068 } 069 070 FileWriter fstream; 071 try { 072 fstream = new FileWriter(fileName); 073 } catch (IOException e) { 074 LOG.warn("Can't write znode file " + fileName, e); 075 return; 076 } 077 078 BufferedWriter out = new BufferedWriter(fstream); 079 080 try { 081 try { 082 out.write(fileContent + "\n"); 083 } finally { 084 try { 085 out.close(); 086 } finally { 087 fstream.close(); 088 } 089 } 090 } catch (IOException e) { 091 LOG.warn("Can't write znode file " + fileName, e); 092 } 093 } 094 095 /** 096 * read the content of znode file, expects a single line. 097 */ 098 public static String readMyEphemeralNodeOnDisk() throws IOException { 099 String fileName = getMyEphemeralNodeFileName(); 100 if (fileName == null) { 101 throw new FileNotFoundException("No filename; set environment variable HBASE_ZNODE_FILE"); 102 } 103 FileReader znodeFile = new FileReader(fileName); 104 BufferedReader br = null; 105 try { 106 br = new BufferedReader(znodeFile); 107 String file_content = br.readLine(); 108 return file_content; 109 } finally { 110 if (br != null) br.close(); 111 } 112 } 113 114 /** 115 * Get the name of the file used to store the znode contents 116 */ 117 public static String getMyEphemeralNodeFileName() { 118 return System.getenv().get("HBASE_ZNODE_FILE"); 119 } 120 121 /** 122 * delete the znode file 123 */ 124 public static void deleteMyEphemeralNodeOnDisk() { 125 String fileName = getMyEphemeralNodeFileName(); 126 127 if (fileName != null) { 128 new File(fileName).delete(); 129 } 130 } 131 132 /** 133 * See HBASE-14861. We are extracting master ServerName from rsZnodePath example: 134 * "/hbase/rs/server.example.com,16020,1448266496481" 135 * @param rsZnodePath from HBASE_ZNODE_FILE 136 * @return String representation of ServerName or null if fails 137 */ 138 139 public static String parseMasterServerName(String rsZnodePath) { 140 String masterServerName = null; 141 try { 142 String[] rsZnodeParts = rsZnodePath.split("/"); 143 masterServerName = rsZnodeParts[rsZnodeParts.length - 1]; 144 } catch (IndexOutOfBoundsException e) { 145 LOG.warn("String " + rsZnodePath + " has wrong format", e); 146 } 147 return masterServerName; 148 } 149 150 /** 151 * Delete the master znode if its content (ServerName string) is the same as the one in the znode 152 * file. (env: HBASE_ZNODE_FILE). I case of master-rs colloaction we extract ServerName string 153 * from rsZnode path.(HBASE-14861) 154 * @return true on successful deletion, false otherwise. 155 */ 156 public static boolean clear(Configuration conf) { 157 Configuration tempConf = new Configuration(conf); 158 tempConf.setInt("zookeeper.recovery.retry", 0); 159 160 ZKWatcher zkw; 161 try { 162 zkw = new ZKWatcher(tempConf, "clean znode for master", new Abortable() { 163 @Override 164 public void abort(String why, Throwable e) { 165 } 166 167 @Override 168 public boolean isAborted() { 169 return false; 170 } 171 }); 172 } catch (IOException e) { 173 LOG.warn("Can't connect to zookeeper to read the master znode", e); 174 return false; 175 } 176 177 String znodeFileContent; 178 try { 179 znodeFileContent = ZNodeClearer.readMyEphemeralNodeOnDisk(); 180 return MasterAddressTracker.deleteIfEquals(zkw, znodeFileContent); 181 } catch (FileNotFoundException fnfe) { 182 // If no file, just keep going -- return success. 183 LOG.warn("Can't find the znode file; presume non-fatal", fnfe); 184 return true; 185 } catch (IOException e) { 186 LOG.warn("Can't read the content of the znode file", e); 187 return false; 188 } finally { 189 zkw.close(); 190 } 191 } 192}