View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase.master;
19  
20  import java.io.IOException;
21  import java.util.Arrays;
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.conf.Configuration;
27  import org.apache.hadoop.hbase.Cell;
28  import org.apache.hadoop.hbase.HConstants;
29  import org.apache.hadoop.hbase.HRegionInfo;
30  import org.apache.hadoop.hbase.Server;
31  import org.apache.hadoop.hbase.ServerName;
32  import org.apache.hadoop.hbase.TableName;
33  import org.apache.hadoop.hbase.catalog.CatalogTracker;
34  import org.apache.hadoop.hbase.catalog.MetaEditor;
35  import org.apache.hadoop.hbase.client.HTable;
36  import org.apache.hadoop.hbase.client.Put;
37  import org.apache.hadoop.hbase.client.Result;
38  import org.apache.hadoop.hbase.master.RegionState.State;
39  import org.apache.hadoop.hbase.regionserver.HRegion;
40  import org.apache.hadoop.hbase.regionserver.RegionServerServices;
41  import org.apache.hadoop.hbase.util.Bytes;
42  import org.apache.hadoop.hbase.util.ConfigUtil;
43  import org.apache.hadoop.hbase.util.MultiHConnection;
44  import org.apache.hadoop.hbase.zookeeper.MetaRegionTracker;
45  import org.apache.zookeeper.KeeperException;
46  
47  import com.google.common.base.Preconditions;
48  
49  /**
50   * A helper to persist region state in meta. We may change this class
51   * to StateStore later if we also use it to store other states in meta
52   */
53  @InterfaceAudience.Private
54  public class RegionStateStore {
55    private static final Log LOG = LogFactory.getLog(RegionStateStore.class);
56  
57    private volatile HRegion metaRegion;
58    private volatile HRegion rootRegion;
59    private volatile boolean initialized;
60  
61    private final boolean noPersistence;
62    private final CatalogTracker catalogTracker;
63    private final Server server;
64    private MultiHConnection multiHConnection;
65  
66    /**
67     * Returns the {@link ServerName} from catalog table {@link Result}
68     * where the region is transitioning. It should be the same as
69     * {@link HRegionInfo#getServerName(Result)} if the server is at OPEN state.
70     * @param r Result to pull from
71     * @return A ServerName instance or null if necessary fields not found or empty.
72     */
73    static ServerName getRegionServer(final Result r) {
74      Cell cell = r.getColumnLatestCell(HConstants.CATALOG_FAMILY, HConstants.SERVERNAME_QUALIFIER);
75      if (cell == null || cell.getValueLength() == 0) return HRegionInfo.getServerName(r);
76      return ServerName.parseServerName(Bytes.toString(cell.getValueArray(),
77        cell.getValueOffset(), cell.getValueLength()));
78    }
79  
80    /**
81     * Pull the region state from a catalog table {@link Result}.
82     * @param r Result to pull the region state from
83     * @return the region state, or OPEN if there's no value written.
84     */
85    static State getRegionState(final Result r) {
86      Cell cell = r.getColumnLatestCell(HConstants.CATALOG_FAMILY, HConstants.STATE_QUALIFIER);
87      if (cell == null || cell.getValueLength() == 0) return State.OPEN;
88      return State.valueOf(Bytes.toString(cell.getValueArray(),
89        cell.getValueOffset(), cell.getValueLength()));
90    }
91  
92    /**
93     * Check if we should persist a state change in meta. Generally it's
94     * better to persist all state changes. However, we should not do that
95     * if the region is not in meta at all. Based on the state and the
96     * previous state, we can identify if a user region has an entry
97     * in meta. For example, merged regions are deleted from meta;
98     * New merging parents, or splitting daughters are
99     * not created in meta yet.
100    */
101   private boolean shouldPersistStateChange(
102       HRegionInfo hri, RegionState state, RegionState oldState) {
103     return !hri.isMetaRegion() && !RegionStates.isOneOfStates(
104       state, State.MERGING_NEW, State.SPLITTING_NEW, State.MERGED)
105       && !(RegionStates.isOneOfStates(state, State.OFFLINE)
106         && RegionStates.isOneOfStates(oldState, State.MERGING_NEW,
107           State.SPLITTING_NEW, State.MERGED));
108   }
109 
110   RegionStateStore(final Server server) {
111     Configuration conf = server.getConfiguration();
112     // No need to persist if using ZK but not migrating
113     noPersistence = ConfigUtil.useZKForAssignment(conf)
114       && !conf.getBoolean("hbase.assignment.usezk.migrating", false);
115     catalogTracker = server.getCatalogTracker();
116     this.server = server;
117     initialized = false;
118   }
119 
120   void start() throws IOException {
121     if (!noPersistence) {
122       if (server instanceof RegionServerServices) {
123         metaRegion = ((RegionServerServices)server).getFromOnlineRegions(
124           HRegionInfo.FIRST_META_REGIONINFO.getEncodedName());
125       }
126       // When meta is not colocated on master
127       if (metaRegion == null) {
128         Configuration conf = server.getConfiguration();
129         // Config to determine the no of HConnections to META.
130         // A single HConnection should be sufficient in most cases. Only if
131         // you are doing lot of writes (>1M) to META,
132         // increasing this value might improve the write throughput.
133         multiHConnection =
134             new MultiHConnection(conf, conf.getInt("hbase.regionstatestore.meta.connection", 1));
135       }
136     }
137     initialized = true;
138   }
139 
140   void stop() {
141     initialized = false;
142     if (multiHConnection != null) {
143       multiHConnection.close();
144     }
145   }
146 
147   void updateRegionState(long openSeqNum, RegionState newState, RegionState oldState) {
148     if (noPersistence) {
149       return;
150     }
151     try {
152       HRegionInfo hri = newState.getRegion();
153       // update meta before checking for initialization.
154       // meta state stored in zk.
155       if (hri.isMetaRegion()) {
156         // persist meta state in MetaRegionTracker
157         try {
158           MetaRegionTracker.setMetaLocation(server.getZooKeeper(), newState.getServerName(),
159             newState.getState());
160           return; // Done
161         } catch (KeeperException e) {
162           throw new IOException("Failed to update meta ZNode", e);
163         }
164       }
165 
166       if (!initialized || !shouldPersistStateChange(hri, newState, oldState)) {
167         return;
168       }
169 
170       ServerName oldServer = oldState != null ? oldState.getServerName() : null;
171       ServerName serverName = newState.getServerName();
172       State state = newState.getState();
173       Put put = new Put(hri.getRegionName());
174       StringBuilder info = new StringBuilder("Updating row ");
175       info.append(hri.getRegionNameAsString()).append(" with state=").append(state);
176       if (serverName != null && !serverName.equals(oldServer)) {
177         put.addImmutable(HConstants.CATALOG_FAMILY, HConstants.SERVERNAME_QUALIFIER,
178           Bytes.toBytes(serverName.getServerName()));
179         info.append("&sn=").append(serverName);
180       }
181       if (openSeqNum >= 0) {
182         Preconditions.checkArgument(state == State.OPEN
183           && serverName != null, "Open region should be on a server");
184         put.addImmutable(HConstants.CATALOG_FAMILY, HConstants.SERVER_QUALIFIER,
185           Bytes.toBytes(serverName.getHostAndPort()));
186         put.addImmutable(HConstants.CATALOG_FAMILY, HConstants.STARTCODE_QUALIFIER,
187           Bytes.toBytes(serverName.getStartcode()));
188         put.addImmutable(HConstants.CATALOG_FAMILY, HConstants.SEQNUM_QUALIFIER,
189           Bytes.toBytes(openSeqNum));
190         info.append("&openSeqNum=").append(openSeqNum);
191         info.append("&server=").append(serverName);
192       }
193       put.addImmutable(HConstants.CATALOG_FAMILY, HConstants.STATE_QUALIFIER,
194         Bytes.toBytes(state.name()));
195       LOG.info(info);
196       // Persist the state change to meta
197       if (metaRegion != null) {
198         try {
199           // Assume meta is pinned to master.
200           // At least, that's what we want.
201           metaRegion.put(put);
202           return; // Done here
203         } catch (Throwable t) {
204           // In unit tests, meta could be moved away by intention
205           // So, the shortcut is gone. We won't try to establish the
206           // shortcut any more because we prefer meta to be pinned
207           // to the master
208           synchronized (this) {
209             if (metaRegion != null) {
210               LOG.info("Meta region shortcut failed", t);
211               if (multiHConnection == null) {
212                 multiHConnection = new MultiHConnection(server.getConfiguration(), 1);
213               }
214               metaRegion = null;
215             }
216           }
217         }
218       }
219       // Called when meta is not on master
220       multiHConnection.processBatchCallback(Arrays.asList(put), TableName.META_TABLE_NAME, null,
221         null);
222     } catch (IOException ioe) {
223       LOG.error("Failed to persist region state " + newState, ioe);
224       server.abort("Failed to update region location", ioe);
225     }
226   }
227 
228   void splitRegion(HRegionInfo p,
229       HRegionInfo a, HRegionInfo b, ServerName sn) throws IOException {
230     MetaEditor.splitRegion(catalogTracker, p, a, b, sn);
231   }
232 
233   void mergeRegions(HRegionInfo p,
234       HRegionInfo a, HRegionInfo b, ServerName sn) throws IOException {
235     MetaEditor.mergeRegions(catalogTracker, p, a, b, sn);
236   }
237   
238 }