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.hbase.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.protobuf.generated.RegionServerStatusProtos.RegionStateTransition.TransitionCode;
32  import org.apache.hadoop.hbase.regionserver.HRegion;
33  import org.apache.hadoop.hbase.regionserver.RegionServerServices;
34  import org.apache.hadoop.hbase.util.ConfigUtil;
35  import org.apache.hadoop.hbase.zookeeper.ZKAssign;
36  import org.apache.zookeeper.KeeperException;
37  
38  /**
39   * Handles closing of a region on a region server.
40   */
41  @InterfaceAudience.Private
42  public class CloseRegionHandler extends EventHandler {
43    // NOTE on priorities shutting down.  There are none for close. There are some
44    // for open.  I think that is right.  On shutdown, we want the meta to close
45    // before root and both to close after the user regions have closed.  What
46    // about the case where master tells us to shutdown a catalog region and we
47    // have a running queue of user regions to close?
48    private static final Log LOG = LogFactory.getLog(CloseRegionHandler.class);
49  
50    private final int FAILED = -1;
51    int expectedVersion = FAILED;
52  
53    private final RegionServerServices rsServices;
54  
55    private final HRegionInfo regionInfo;
56  
57    // If true, the hosting server is aborting.  Region close process is different
58    // when we are aborting.
59    private final boolean abort;
60  
61    // Update zk on closing transitions. Usually true.  Its false if cluster
62    // is going down.  In this case, its the rs that initiates the region
63    // close -- not the master process so state up in zk will unlikely be
64    // CLOSING.
65    private final boolean zk;
66    private ServerName destination;
67    private final boolean useZKForAssignment;
68  
69    // This is executed after receiving an CLOSE RPC from the master.
70    public CloseRegionHandler(final Server server,
71        final RegionServerServices rsServices, HRegionInfo regionInfo) {
72      this(server, rsServices, regionInfo, false, true, -1, EventType.M_RS_CLOSE_REGION, null);
73    }
74  
75    /**
76     * This method used internally by the RegionServer to close out regions.
77     * @param server
78     * @param rsServices
79     * @param regionInfo
80     * @param abort If the regionserver is aborting.
81     * @param zk If the close should be noted out in zookeeper.
82     */
83    public CloseRegionHandler(final Server server,
84        final RegionServerServices rsServices,
85        final HRegionInfo regionInfo, final boolean abort, final boolean zk,
86        final int versionOfClosingNode) {
87      this(server, rsServices,  regionInfo, abort, zk, versionOfClosingNode,
88        EventType.M_RS_CLOSE_REGION, null);
89    }
90  
91    public CloseRegionHandler(final Server server,
92        final RegionServerServices rsServices,
93        final HRegionInfo regionInfo, final boolean abort, final boolean zk,
94        final int versionOfClosingNode, ServerName destination) {
95      this(server, rsServices, regionInfo, abort, zk, versionOfClosingNode,
96        EventType.M_RS_CLOSE_REGION, destination);
97    }
98  
99    public CloseRegionHandler(final Server server,
100       final RegionServerServices rsServices, HRegionInfo regionInfo,
101       boolean abort, final boolean zk, final int versionOfClosingNode,
102       EventType eventType) {
103     this(server, rsServices, regionInfo, abort, zk, versionOfClosingNode, eventType, null);
104   }
105 
106   protected CloseRegionHandler(final Server server, final RegionServerServices rsServices,
107       HRegionInfo regionInfo, boolean abort, final boolean zk, final int versionOfClosingNode,
108       EventType eventType, ServerName destination) {
109     super(server, eventType);
110     this.server = server;
111     this.rsServices = rsServices;
112     this.regionInfo = regionInfo;
113     this.abort = abort;
114     this.zk = zk;
115     this.expectedVersion = versionOfClosingNode;
116     this.destination = destination;
117     useZKForAssignment = ConfigUtil.useZKForAssignment(server.getConfiguration());
118   }
119 
120   public HRegionInfo getRegionInfo() {
121     return regionInfo;
122   }
123 
124   @Override
125   public void process() {
126     try {
127       String name = regionInfo.getRegionNameAsString();
128       LOG.debug("Processing close of " + name);
129       String encodedRegionName = regionInfo.getEncodedName();
130       // Check that this region is being served here
131       HRegion region = this.rsServices.getFromOnlineRegions(encodedRegionName);
132       if (region == null) {
133         LOG.warn("Received CLOSE for region " + name + " but currently not serving - ignoring");
134         if (zk){
135           LOG.error("The znode is not modified as we are not serving " + name);
136         }
137         // TODO: do better than a simple warning
138         return;
139       }
140 
141       // Close the region
142       try {
143         if (zk && useZKForAssignment
144             && !ZKAssign.checkClosingState(server.getZooKeeper(), regionInfo, expectedVersion)) {
145           // bad znode state
146           return; // We're node deleting the znode, but it's not ours...
147         }
148 
149         // TODO: If we need to keep updating CLOSING stamp to prevent against
150         // a timeout if this is long-running, need to spin up a thread?
151         if (region.close(abort) == null) {
152           // This region got closed.  Most likely due to a split. So instead
153           // of doing the setClosedState() below, let's just ignore cont
154           // The split message will clean up the master state.
155           LOG.warn("Can't close region: was already closed during close(): " +
156             regionInfo.getRegionNameAsString());
157           return;
158         }
159       } catch (Throwable t) {
160         // A throwable here indicates that we couldn't successfully flush the
161         // memstore before closing. So, we need to abort the server and allow
162         // the master to split our logs in order to recover the data.
163         server.abort("Unrecoverable exception while closing region " +
164           regionInfo.getRegionNameAsString() + ", still finishing close", t);
165         throw new RuntimeException(t);
166       }
167 
168       this.rsServices.removeFromOnlineRegions(region, destination);
169       if (!useZKForAssignment) {
170         rsServices.reportRegionStateTransition(TransitionCode.CLOSED, regionInfo);
171       } else {
172         if (this.zk) {
173           if (setClosedState(this.expectedVersion, region)) {
174             LOG.debug("Set closed state in zk for " + name + " on " + this.server.getServerName());
175           } else {
176             LOG.debug("Set closed state in zk UNSUCCESSFUL for " + name + " on "
177                 + this.server.getServerName());
178           }
179         }
180       }
181       // Done!  Region is closed on this RS
182       LOG.debug("Closed " + region.getRegionNameAsString());
183     } finally {
184       this.rsServices.getRegionsInTransitionInRS().
185           remove(this.regionInfo.getEncodedNameAsBytes());
186     }
187   }
188 
189   /**
190    * Transition ZK node to CLOSED
191    * @param expectedVersion
192    * @return If the state is set successfully
193    */
194   private boolean setClosedState(final int expectedVersion, final HRegion region) {
195     try {
196       if (ZKAssign.transitionNodeClosed(server.getZooKeeper(), regionInfo,
197           server.getServerName(), expectedVersion) == FAILED) {
198         LOG.warn("Completed the CLOSE of a region but when transitioning from " +
199             " CLOSING to CLOSED got a version mismatch, someone else clashed " +
200             "so now unassigning");
201         region.close();
202         return false;
203       }
204     } catch (NullPointerException e) {
205       // I've seen NPE when table was deleted while close was running in unit tests.
206       LOG.warn("NPE during close -- catching and continuing...", e);
207       return false;
208     } catch (KeeperException e) {
209       LOG.error("Failed transitioning node from CLOSING to CLOSED", e);
210       return false;
211     } catch (IOException e) {
212       LOG.error("Failed to close region after failing to transition", e);
213       return false;
214     }
215     return true;
216   }
217 }