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 = getCurrentVersion();
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 (Throwable t) {
129         // A throwable here indicates that we couldn't successfully flush the
130         // memstore before closing. So, we need to abort the server and allow
131         // the master to split our logs in order to recover the data.
132         server.abort("Unrecoverable exception while closing region " +
133           regionInfo.getRegionNameAsString() + ", still finishing close", t);
134         throw new RuntimeException(t);
135       }
136 
137       this.rsServices.removeFromOnlineRegions(regionInfo.getEncodedName());
138 
139       if (this.zk) {
140         if (setClosedState(expectedVersion, region)) {
141           LOG.debug("set region closed state in zk successfully for region " +
142             name + " sn name: " + this.server.getServerName());
143         } else {
144           LOG.debug("set region closed state in zk unsuccessfully for region " +
145             name + " sn name: " + this.server.getServerName());
146         }
147       }
148 
149       // Done!  Region is closed on this RS
150       LOG.debug("Closed region " + region.getRegionNameAsString());
151     } finally {
152       this.rsServices.getRegionsInTransitionInRS().
153           remove(this.regionInfo.getEncodedNameAsBytes());
154     }
155   }
156 
157   /**
158    * Transition ZK node to CLOSED
159    * @param expectedVersion
160    * @return If the state is set successfully
161    */
162   private boolean setClosedState(final int expectedVersion, final HRegion region) {
163     try {
164       if (ZKAssign.transitionNodeClosed(server.getZooKeeper(), regionInfo,
165           server.getServerName(), expectedVersion) == FAILED) {
166         LOG.warn("Completed the CLOSE of a region but when transitioning from " +
167             " CLOSING to CLOSED got a version mismatch, someone else clashed " +
168             "so now unassigning");
169         region.close();
170         return false;
171       }
172     } catch (NullPointerException e) {
173       // I've seen NPE when table was deleted while close was running in unit tests.
174       LOG.warn("NPE during close -- catching and continuing...", e);
175       return false;
176     } catch (KeeperException e) {
177       LOG.error("Failed transitioning node from CLOSING to CLOSED", e);
178       return false;
179     } catch (IOException e) {
180       LOG.error("Failed to close region after failing to transition", e);
181       return false;
182     }
183     return true;
184   }
185 
186   /**
187    * Get the node's current version
188    * @return The expectedVersion.  If -1, we failed getting the node
189    */
190   private int getCurrentVersion() {
191     int expectedVersion = FAILED;
192     try {
193       if ((expectedVersion = ZKAssign.getVersion(
194           server.getZooKeeper(), regionInfo)) == FAILED) {
195         LOG.warn("Error getting node's version in CLOSING state," +
196           " aborting close of " + regionInfo.getRegionNameAsString());
197       }
198     } catch (KeeperException e) {
199       LOG.warn("Error creating node in CLOSING state, aborting close of " +
200         regionInfo.getRegionNameAsString(), e);
201     }
202     return expectedVersion;
203   }
204 }