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