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}