View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package org.apache.hadoop.hbase;
20  
21  import java.io.BufferedReader;
22  import java.io.BufferedWriter;
23  import java.io.File;
24  import java.io.FileNotFoundException;
25  import java.io.FileReader;
26  import java.io.FileWriter;
27  import java.io.IOException;
28  
29  import org.apache.commons.logging.Log;
30  import org.apache.commons.logging.LogFactory;
31  import org.apache.hadoop.conf.Configuration;
32  import org.apache.hadoop.hbase.master.balancer.BaseLoadBalancer;
33  import org.apache.hadoop.hbase.zookeeper.MasterAddressTracker;
34  import org.apache.hadoop.hbase.zookeeper.ZKUtil;
35  import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
36  import org.apache.zookeeper.KeeperException;
37  
38  /**
39   * <p>Contains a set of methods for the collaboration between the start/stop scripts and the
40   * servers. It allows to delete immediately the znode when the master or the regions server crashes.
41   * The region server / master writes a specific file when it starts / becomes main master. When they
42   * end properly, they delete the file.</p>
43   * <p>In the script, we check for the existence of these files when the program ends. If they still
44   * exist we conclude that the server crashed, likely without deleting their znode. To have a faster
45   * recovery we delete immediately the znode.</p>
46   * <p>The strategy depends on the server type. For a region server we store the znode path in the
47   * file, and use it to delete it. for a master, as the znode path constant whatever the server, we
48   * check its content to make sure that the backup server is not now in charge.</p>
49   */
50  public class ZNodeClearer {
51    private static final Log LOG = LogFactory.getLog(ZNodeClearer.class);
52  
53    private ZNodeClearer() {}
54  
55    /**
56     * Logs the errors without failing on exception.
57     */
58    public static void writeMyEphemeralNodeOnDisk(String fileContent) {
59      String fileName = ZNodeClearer.getMyEphemeralNodeFileName();
60      if (fileName == null) {
61        LOG.warn("Environment variable HBASE_ZNODE_FILE not set; znodes will not be cleared " +
62            "on crash by start scripts (Longer MTTR!)");
63        return;
64      }
65  
66      FileWriter fstream;
67      try {
68        fstream = new FileWriter(fileName);
69      } catch (IOException e) {
70        LOG.warn("Can't write znode file "+fileName, e);
71        return;
72      }
73  
74      BufferedWriter out = new BufferedWriter(fstream);
75  
76      try {
77        try {
78          out.write(fileContent + "\n");
79        } finally {
80          try {
81            out.close();
82          } finally {
83            fstream.close();
84          }
85        }
86      } catch (IOException e) {
87        LOG.warn("Can't write znode file "+fileName, e);
88      }
89    }
90  
91    /**
92     * read the content of znode file, expects a single line.
93     */
94    public static String readMyEphemeralNodeOnDisk() throws IOException {
95      String fileName = getMyEphemeralNodeFileName();
96      if (fileName == null){
97        throw new FileNotFoundException("No filename; set environment variable HBASE_ZNODE_FILE");
98      }
99      FileReader znodeFile = new FileReader(fileName);
100     BufferedReader br = null;
101     try {
102       br = new BufferedReader(znodeFile);
103       String file_content = br.readLine();
104       return file_content;
105     } finally {
106       if (br != null) br.close();
107     }
108   }
109 
110   /**
111    * Get the name of the file used to store the znode contents
112    */
113   public static String getMyEphemeralNodeFileName() {
114     return System.getenv().get("HBASE_ZNODE_FILE");
115   }
116 
117   /**
118    *  delete the znode file
119    */
120   public static void deleteMyEphemeralNodeOnDisk() {
121     String fileName = getMyEphemeralNodeFileName();
122 
123     if (fileName != null) {
124       new File(fileName).delete();
125     }
126   }
127   
128   /**
129    * See HBASE-14861. We are extracting master ServerName from rsZnodePath
130    * example: "/hbase/rs/server.example.com,16020,1448266496481"
131    * @param rsZnodePath from HBASE_ZNODE_FILE
132    * @return String representation of ServerName or null if fails
133    */
134   
135   public static String parseMasterServerName(String rsZnodePath) {
136     String masterServerName = null;
137     try {
138       String[] rsZnodeParts = rsZnodePath.split("/");
139       masterServerName = rsZnodeParts[rsZnodeParts.length -1];
140     } catch (IndexOutOfBoundsException e) {
141       LOG.warn("String " + rsZnodePath + " has wrong fromat", e);
142     }
143     return masterServerName; 
144   }
145   
146   /**
147    * 
148    * @return true if cluster is configured with master-rs collocation 
149    */
150   private static boolean tablesOnMaster(Configuration conf) {
151     boolean tablesOnMaster = true;
152     String confValue = conf.get(BaseLoadBalancer.TABLES_ON_MASTER);
153     if (confValue != null && confValue.equalsIgnoreCase("none")) {
154       tablesOnMaster = false;
155     }
156     return tablesOnMaster;
157   }
158 
159   /**
160    * Delete the master znode if its content (ServerName string) is the same
161    *  as the one in the znode file. (env: HBASE_ZNODE_FILE). I case of master-rs
162    *  colloaction we extract ServerName string from rsZnode path.(HBASE-14861)
163    * @return true on successful deletion, false otherwise.
164    */
165   public static boolean clear(Configuration conf) {
166     Configuration tempConf = new Configuration(conf);
167     tempConf.setInt("zookeeper.recovery.retry", 0);
168 
169     ZooKeeperWatcher zkw;
170     try {
171       zkw = new ZooKeeperWatcher(tempConf, "clean znode for master",
172           new Abortable() {
173             @Override public void abort(String why, Throwable e) {}
174             @Override public boolean isAborted() { return false; }
175           });
176     } catch (IOException e) {
177       LOG.warn("Can't connect to zookeeper to read the master znode", e);
178       return false;
179     }
180 
181     String znodeFileContent;
182     try {
183       znodeFileContent = ZNodeClearer.readMyEphemeralNodeOnDisk();
184       if(ZNodeClearer.tablesOnMaster(conf)) {
185       //In case of master crash also remove rsZnode since master is also regionserver 
186         ZKUtil.deleteNodeFailSilent(zkw, znodeFileContent);  
187         return MasterAddressTracker.deleteIfEquals(zkw, 
188                                     ZNodeClearer.parseMasterServerName(znodeFileContent));
189       } else {
190         return MasterAddressTracker.deleteIfEquals(zkw, znodeFileContent);
191       }
192     } catch (FileNotFoundException fnfe) {
193       // If no file, just keep going -- return success.
194       LOG.warn("Can't find the znode file; presume non-fatal", fnfe);
195       return true;
196     } catch (IOException e) {
197       LOG.warn("Can't read the content of the znode file", e);
198       return false;
199     } catch (KeeperException e) {
200       LOG.warn("ZooKeeper exception deleting znode", e);
201       return false;
202     } finally {
203       zkw.close();
204     }
205   }
206 }