View Javadoc

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