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      try {
99        String name = regionInfo.getRegionNameAsString();
100       LOG.debug("Processing close of " + name);
101       String encodedRegionName = regionInfo.getEncodedName();
102       // Check that this region is being served here
103       HRegion region = this.rsServices.getFromOnlineRegions(encodedRegionName);
104       if (region == null) {
105         LOG.warn("Received CLOSE for region " + name +
106             " but currently not serving");
107         return;
108       }
109 
110       int expectedVersion = FAILED;
111       if (this.zk) {
112         expectedVersion = setClosingState();
113         if (expectedVersion == FAILED) return;
114       }
115 
116       // Close the region
117       try {
118         // TODO: If we need to keep updating CLOSING stamp to prevent against
119         // a timeout if this is long-running, need to spin up a thread?
120         if (region.close(abort) == null) {
121           // This region got closed.  Most likely due to a split. So instead
122           // of doing the setClosedState() below, let's just ignore cont
123           // The split message will clean up the master state.
124           LOG.warn("Can't close region: was already closed during close(): " +
125             regionInfo.getRegionNameAsString());
126           return;
127         }
128       } catch (IOException e) {
129         LOG.error("Unrecoverable exception while closing region " +
130           regionInfo.getRegionNameAsString() + ", still finishing close", e);
131       }
132 
133       this.rsServices.removeFromOnlineRegions(regionInfo.getEncodedName());
134 
135       if (this.zk) setClosedState(expectedVersion, region);
136 
137       // Done!  Region is closed on this RS
138       LOG.debug("Closed region " + region.getRegionNameAsString());
139     } finally {
140       this.rsServices.getRegionsInTransitionInRS().
141           remove(this.regionInfo.getEncodedNameAsBytes());
142     }
143   }
144 
145   /**
146    * Transition ZK node to CLOSED
147    * @param expectedVersion
148    */
149   private void setClosedState(final int expectedVersion, final HRegion region) {
150     try {
151       if (ZKAssign.transitionNodeClosed(server.getZooKeeper(), regionInfo,
152           server.getServerName(), expectedVersion) == FAILED) {
153         LOG.warn("Completed the CLOSE of a region but when transitioning from " +
154             " CLOSING to CLOSED got a version mismatch, someone else clashed " +
155             "so now unassigning");
156         region.close();
157         return;
158       }
159     } catch (NullPointerException e) {
160       // I've seen NPE when table was deleted while close was running in unit tests.
161       LOG.warn("NPE during close -- catching and continuing...", e);
162     } catch (KeeperException e) {
163       LOG.error("Failed transitioning node from CLOSING to CLOSED", e);
164       return;
165     } catch (IOException e) {
166       LOG.error("Failed to close region after failing to transition", e);
167       return;
168     }
169   }
170 
171   /**
172    * Create ZK node in CLOSING state.
173    * @return The expectedVersion.  If -1, we failed setting CLOSING.
174    */
175   private int setClosingState() {
176     int expectedVersion = FAILED;
177     try {
178       if ((expectedVersion = ZKAssign.createNodeClosing(
179           server.getZooKeeper(), regionInfo, server.getServerName())) == FAILED) {
180         LOG.warn("Error creating node in CLOSING state, aborting close of " +
181           regionInfo.getRegionNameAsString());
182       }
183     } catch (KeeperException e) {
184       LOG.warn("Error creating node in CLOSING state, aborting close of " +
185         regionInfo.getRegionNameAsString());
186     }
187     return expectedVersion;
188   }
189 }