View Javadoc

1   /**
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  package org.apache.hadoop.hbase.regionserver.handler;
20  
21  import java.io.IOException;
22  
23  import org.apache.commons.logging.Log;
24  import org.apache.commons.logging.LogFactory;
25  import org.apache.hadoop.classification.InterfaceAudience;
26  import org.apache.hadoop.hbase.HRegionInfo;
27  import org.apache.hadoop.hbase.Server;
28  import org.apache.hadoop.hbase.ServerName;
29  import org.apache.hadoop.hbase.executor.EventHandler;
30  import org.apache.hadoop.hbase.executor.EventType;
31  import org.apache.hadoop.hbase.regionserver.HRegion;
32  import org.apache.hadoop.hbase.regionserver.RegionServerServices;
33  import org.apache.hadoop.hbase.zookeeper.ZKAssign;
34  import org.apache.zookeeper.KeeperException;
35  
36  /**
37   * Handles closing of a region on a region server.
38   */
39  @InterfaceAudience.Private
40  public class CloseRegionHandler extends EventHandler {
41    // NOTE on priorities shutting down.  There are none for close. There are some
42    // for open.  I think that is right.  On shutdown, we want the meta to close
43    // before root and both to close after the user regions have closed.  What
44    // about the case where master tells us to shutdown a catalog region and we
45    // have a running queue of user regions to close?
46    private static final Log LOG = LogFactory.getLog(CloseRegionHandler.class);
47  
48    private final int FAILED = -1;
49    int expectedVersion = FAILED;
50  
51    private final RegionServerServices rsServices;
52  
53    private final HRegionInfo regionInfo;
54  
55    // If true, the hosting server is aborting.  Region close process is different
56    // when we are aborting.
57    private final boolean abort;
58  
59    // Update zk on closing transitions. Usually true.  Its false if cluster
60    // is going down.  In this case, its the rs that initiates the region
61    // close -- not the master process so state up in zk will unlikely be
62    // CLOSING.
63    private final boolean zk;
64    private ServerName destination;
65  
66    // This is executed after receiving an CLOSE RPC from the master.
67    public CloseRegionHandler(final Server server,
68        final RegionServerServices rsServices, HRegionInfo regionInfo) {
69      this(server, rsServices, regionInfo, false, true, -1, EventType.M_RS_CLOSE_REGION, null);
70    }
71  
72    /**
73     * This method used internally by the RegionServer to close out regions.
74     * @param server
75     * @param rsServices
76     * @param regionInfo
77     * @param abort If the regionserver is aborting.
78     * @param zk If the close should be noted out in zookeeper.
79     */
80    public CloseRegionHandler(final Server server,
81        final RegionServerServices rsServices,
82        final HRegionInfo regionInfo, final boolean abort, final boolean zk,
83        final int versionOfClosingNode) {
84      this(server, rsServices,  regionInfo, abort, zk, versionOfClosingNode,
85        EventType.M_RS_CLOSE_REGION, null);
86    }
87  
88    public CloseRegionHandler(final Server server,
89        final RegionServerServices rsServices,
90        final HRegionInfo regionInfo, final boolean abort, final boolean zk,
91        final int versionOfClosingNode, ServerName destination) {
92      this(server, rsServices, regionInfo, abort, zk, versionOfClosingNode,
93        EventType.M_RS_CLOSE_REGION, destination);
94    }
95  
96    public CloseRegionHandler(final Server server,
97        final RegionServerServices rsServices, HRegionInfo regionInfo,
98        boolean abort, final boolean zk, final int versionOfClosingNode,
99        EventType eventType) {
100     this(server, rsServices, regionInfo, abort, zk, versionOfClosingNode, eventType, null);
101   }
102 
103     protected CloseRegionHandler(final Server server,
104       final RegionServerServices rsServices, HRegionInfo regionInfo,
105       boolean abort, final boolean zk, final int versionOfClosingNode,
106       EventType eventType, ServerName destination) {
107     super(server, eventType);
108     this.server = server;
109     this.rsServices = rsServices;
110     this.regionInfo = regionInfo;
111     this.abort = abort;
112     this.zk = zk;
113     this.expectedVersion = versionOfClosingNode;
114     this.destination = destination;
115   }
116 
117   public HRegionInfo getRegionInfo() {
118     return regionInfo;
119   }
120 
121   @Override
122   public void process() {
123     try {
124       String name = regionInfo.getRegionNameAsString();
125       LOG.debug("Processing close of " + name);
126       String encodedRegionName = regionInfo.getEncodedName();
127       // Check that this region is being served here
128       HRegion region = this.rsServices.getFromOnlineRegions(encodedRegionName);
129       if (region == null) {
130         LOG.warn("Received CLOSE for region " + name + " but currently not serving - ignoring");
131         if (zk){
132           LOG.error("The znode is not modified as we are not serving " + name);
133         }
134         // TODO: do better than a simple warning
135         return;
136       }
137 
138       // Close the region
139       try {
140         if (zk && !ZKAssign.checkClosingState(server.getZooKeeper(), regionInfo, expectedVersion)){
141           // bad znode state
142           return; // We're node deleting the znode, but it's not ours...
143         }
144 
145         // TODO: If we need to keep updating CLOSING stamp to prevent against
146         // a timeout if this is long-running, need to spin up a thread?
147         if (region.close(abort) == null) {
148           // This region got closed.  Most likely due to a split. So instead
149           // of doing the setClosedState() below, let's just ignore cont
150           // The split message will clean up the master state.
151           LOG.warn("Can't close region: was already closed during close(): " +
152             regionInfo.getRegionNameAsString());
153           return;
154         }
155       } catch (Throwable t) {
156         // A throwable here indicates that we couldn't successfully flush the
157         // memstore before closing. So, we need to abort the server and allow
158         // the master to split our logs in order to recover the data.
159         server.abort("Unrecoverable exception while closing region " +
160           regionInfo.getRegionNameAsString() + ", still finishing close", t);
161         throw new RuntimeException(t);
162       }
163 
164       this.rsServices.removeFromOnlineRegions(region, destination);
165 
166       if (this.zk) {
167         if (setClosedState(this.expectedVersion, region)) {
168           LOG.debug("Set closed state in zk for " + name + " on " + this.server.getServerName());
169         } else {
170           LOG.debug("Set closed state in zk UNSUCCESSFUL for " + name + " on " +
171             this.server.getServerName());
172         }
173       }
174 
175       // Done!  Region is closed on this RS
176       LOG.debug("Closed " + region.getRegionNameAsString());
177     } finally {
178       this.rsServices.getRegionsInTransitionInRS().
179           remove(this.regionInfo.getEncodedNameAsBytes());
180     }
181   }
182 
183   /**
184    * Transition ZK node to CLOSED
185    * @param expectedVersion
186    * @return If the state is set successfully
187    */
188   private boolean setClosedState(final int expectedVersion, final HRegion region) {
189     try {
190       if (ZKAssign.transitionNodeClosed(server.getZooKeeper(), regionInfo,
191           server.getServerName(), expectedVersion) == FAILED) {
192         LOG.warn("Completed the CLOSE of a region but when transitioning from " +
193             " CLOSING to CLOSED got a version mismatch, someone else clashed " +
194             "so now unassigning");
195         region.close();
196         return false;
197       }
198     } catch (NullPointerException e) {
199       // I've seen NPE when table was deleted while close was running in unit tests.
200       LOG.warn("NPE during close -- catching and continuing...", e);
201       return false;
202     } catch (KeeperException e) {
203       LOG.error("Failed transitioning node from CLOSING to CLOSED", e);
204       return false;
205     } catch (IOException e) {
206       LOG.error("Failed to close region after failing to transition", e);
207       return false;
208     }
209     return true;
210   }
211 }