001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.master;
019
020import java.util.Date;
021import org.apache.hadoop.hbase.ServerName;
022import org.apache.hadoop.hbase.client.RegionInfo;
023import org.apache.yetus.audience.InterfaceAudience;
024import org.apache.yetus.audience.InterfaceStability;
025
026import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
027import org.apache.hadoop.hbase.shaded.protobuf.generated.ClusterStatusProtos;
028
029/**
030 * State of a Region while undergoing transitions.
031 * This class is immutable.
032 */
033@InterfaceAudience.Private
034public class RegionState {
035
036  @InterfaceAudience.Private
037  @InterfaceStability.Evolving
038  public enum State {
039    OFFLINE,        // region is in an offline state
040    OPENING,        // server has begun to open but not yet done
041    OPEN,           // server opened region and updated meta
042    CLOSING,        // server has begun to close but not yet done
043    CLOSED,         // server closed region and updated meta
044    SPLITTING,      // server started split of a region
045    SPLIT,          // server completed split of a region
046    FAILED_OPEN,    // failed to open, and won't retry any more
047    FAILED_CLOSE,   // failed to close, and won't retry any more
048    MERGING,        // server started merge a region
049    MERGED,         // server completed merge a region
050    SPLITTING_NEW,  // new region to be created when RS splits a parent
051                    // region but hasn't be created yet, or master doesn't
052                    // know it's already created
053    MERGING_NEW,    // new region to be created when RS merges two
054                    // daughter regions but hasn't be created yet, or
055                    // master doesn't know it's already created
056    ABNORMALLY_CLOSED; // the region is CLOSED because of a RS crashes. Usually it is the same
057                       // with CLOSED, but for some operations such as merge/split, we can not
058                       // apply it to a region in this state, as it may lead to data loss as we
059                       // may have some data in recovered edits.
060
061    public boolean matches(State... expected) {
062      for (State state : expected) {
063        if (this == state) {
064          return true;
065        }
066      }
067      return false;
068    }
069
070    /**
071     * Convert to protobuf ClusterStatusProtos.RegionState.State
072     */
073    public ClusterStatusProtos.RegionState.State convert() {
074      ClusterStatusProtos.RegionState.State rs;
075      switch (this) {
076        case OFFLINE:
077          rs = ClusterStatusProtos.RegionState.State.OFFLINE;
078          break;
079        case OPENING:
080          rs = ClusterStatusProtos.RegionState.State.OPENING;
081          break;
082        case OPEN:
083          rs = ClusterStatusProtos.RegionState.State.OPEN;
084          break;
085        case CLOSING:
086          rs = ClusterStatusProtos.RegionState.State.CLOSING;
087          break;
088        case CLOSED:
089          rs = ClusterStatusProtos.RegionState.State.CLOSED;
090          break;
091        case SPLITTING:
092          rs = ClusterStatusProtos.RegionState.State.SPLITTING;
093          break;
094        case SPLIT:
095          rs = ClusterStatusProtos.RegionState.State.SPLIT;
096          break;
097        case FAILED_OPEN:
098          rs = ClusterStatusProtos.RegionState.State.FAILED_OPEN;
099          break;
100        case FAILED_CLOSE:
101          rs = ClusterStatusProtos.RegionState.State.FAILED_CLOSE;
102          break;
103        case MERGING:
104          rs = ClusterStatusProtos.RegionState.State.MERGING;
105          break;
106        case MERGED:
107          rs = ClusterStatusProtos.RegionState.State.MERGED;
108          break;
109        case SPLITTING_NEW:
110          rs = ClusterStatusProtos.RegionState.State.SPLITTING_NEW;
111          break;
112        case MERGING_NEW:
113          rs = ClusterStatusProtos.RegionState.State.MERGING_NEW;
114          break;
115        case ABNORMALLY_CLOSED:
116          rs = ClusterStatusProtos.RegionState.State.ABNORMALLY_CLOSED;
117          break;
118        default:
119          throw new IllegalStateException("");
120      }
121      return rs;
122    }
123
124    /**
125     * Convert a protobuf HBaseProtos.RegionState.State to a RegionState.State
126     *
127     * @return the RegionState.State
128     */
129    public static State convert(ClusterStatusProtos.RegionState.State protoState) {
130      State state;
131      switch (protoState) {
132        case OFFLINE:
133          state = OFFLINE;
134          break;
135        case PENDING_OPEN:
136        case OPENING:
137          state = OPENING;
138          break;
139        case OPEN:
140          state = OPEN;
141          break;
142        case PENDING_CLOSE:
143        case CLOSING:
144          state = CLOSING;
145          break;
146        case CLOSED:
147          state = CLOSED;
148          break;
149        case SPLITTING:
150          state = SPLITTING;
151          break;
152        case SPLIT:
153          state = SPLIT;
154          break;
155        case FAILED_OPEN:
156          state = FAILED_OPEN;
157          break;
158        case FAILED_CLOSE:
159          state = FAILED_CLOSE;
160          break;
161        case MERGING:
162          state = MERGING;
163          break;
164        case MERGED:
165          state = MERGED;
166          break;
167        case SPLITTING_NEW:
168          state = SPLITTING_NEW;
169          break;
170        case MERGING_NEW:
171          state = MERGING_NEW;
172          break;
173        case ABNORMALLY_CLOSED:
174          state = ABNORMALLY_CLOSED;
175          break;
176        default:
177          throw new IllegalStateException("Unhandled state " + protoState);
178      }
179      return state;
180    }
181  }
182
183  private final long stamp;
184  private final RegionInfo hri;
185  private final ServerName serverName;
186  private final State state;
187  // The duration of region in transition
188  private long ritDuration;
189
190  public static RegionState createForTesting(RegionInfo region, State state) {
191    return new RegionState(region, state, System.currentTimeMillis(), null);
192  }
193
194  public RegionState(RegionInfo region, State state, ServerName serverName) {
195    this(region, state, System.currentTimeMillis(), serverName);
196  }
197
198  public RegionState(RegionInfo region,
199      State state, long stamp, ServerName serverName) {
200    this(region, state, stamp, serverName, 0);
201  }
202
203  public RegionState(RegionInfo region, State state, long stamp, ServerName serverName,
204      long ritDuration) {
205    this.hri = region;
206    this.state = state;
207    this.stamp = stamp;
208    this.serverName = serverName;
209    this.ritDuration = ritDuration;
210  }
211
212  public State getState() {
213    return state;
214  }
215
216  public long getStamp() {
217    return stamp;
218  }
219
220  public RegionInfo getRegion() {
221    return hri;
222  }
223
224  public ServerName getServerName() {
225    return serverName;
226  }
227
228  public long getRitDuration() {
229    return ritDuration;
230  }
231
232  /**
233   * Update the duration of region in transition
234   * @param previousStamp previous RegionState's timestamp
235   */
236  @InterfaceAudience.Private
237  void updateRitDuration(long previousStamp) {
238    this.ritDuration += (this.stamp - previousStamp);
239  }
240
241  public boolean isClosing() {
242    return state == State.CLOSING;
243  }
244
245  public boolean isClosed() {
246    return state == State.CLOSED;
247  }
248
249  public boolean isClosedOrAbnormallyClosed() {
250    return isClosed() || this.state == State.ABNORMALLY_CLOSED;
251  }
252
253  public boolean isOpening() {
254    return state == State.OPENING;
255  }
256
257  public boolean isOpened() {
258    return state == State.OPEN;
259  }
260
261  public boolean isOffline() {
262    return state == State.OFFLINE;
263  }
264
265  public boolean isSplitting() {
266    return state == State.SPLITTING;
267  }
268
269  public boolean isSplit() {
270    return state == State.SPLIT;
271  }
272
273  public boolean isSplittingNew() {
274    return state == State.SPLITTING_NEW;
275  }
276
277  public boolean isFailedOpen() {
278    return state == State.FAILED_OPEN;
279  }
280
281  public boolean isFailedClose() {
282    return state == State.FAILED_CLOSE;
283  }
284
285  public boolean isMerging() {
286    return state == State.MERGING;
287  }
288
289  public boolean isMerged() {
290    return state == State.MERGED;
291  }
292
293  public boolean isMergingNew() {
294    return state == State.MERGING_NEW;
295  }
296
297  public boolean isOnServer(final ServerName sn) {
298    return serverName != null && serverName.equals(sn);
299  }
300
301  public boolean isMergingOnServer(final ServerName sn) {
302    return isOnServer(sn) && isMerging();
303  }
304
305  public boolean isMergingNewOnServer(final ServerName sn) {
306    return isOnServer(sn) && isMergingNew();
307  }
308
309  public boolean isMergingNewOrOpenedOnServer(final ServerName sn) {
310    return isOnServer(sn) && (isMergingNew() || isOpened());
311  }
312
313  public boolean isMergingNewOrOfflineOnServer(final ServerName sn) {
314    return isOnServer(sn) && (isMergingNew() || isOffline());
315  }
316
317  public boolean isSplittingOnServer(final ServerName sn) {
318    return isOnServer(sn) && isSplitting();
319  }
320
321  public boolean isSplittingNewOnServer(final ServerName sn) {
322    return isOnServer(sn) && isSplittingNew();
323  }
324
325  public boolean isSplittingOrOpenedOnServer(final ServerName sn) {
326    return isOnServer(sn) && (isSplitting() || isOpened());
327  }
328
329  public boolean isSplittingOrSplitOnServer(final ServerName sn) {
330    return isOnServer(sn) && (isSplitting() || isSplit());
331  }
332
333  public boolean isClosingOrClosedOnServer(final ServerName sn) {
334    return isOnServer(sn) && (isClosing() || isClosed());
335  }
336
337  public boolean isOpeningOrFailedOpenOnServer(final ServerName sn) {
338    return isOnServer(sn) && (isOpening() || isFailedOpen());
339  }
340
341  public boolean isOpeningOrOpenedOnServer(final ServerName sn) {
342    return isOnServer(sn) && (isOpening() || isOpened());
343  }
344
345  public boolean isOpenedOnServer(final ServerName sn) {
346    return isOnServer(sn) && isOpened();
347  }
348
349  /**
350   * Check if a region state can transition to offline
351   */
352  public boolean isReadyToOffline() {
353    return isMerged() || isSplit() || isOffline()
354      || isSplittingNew() || isMergingNew();
355  }
356
357  /**
358   * Check if a region state can transition to online
359   */
360  public boolean isReadyToOnline() {
361    return isOpened() || isSplittingNew() || isMergingNew();
362  }
363
364  /**
365   * Check if a region state is one of offline states that
366   * can't transition to pending_close/closing (unassign/offline)
367   */
368  public boolean isUnassignable() {
369    return isUnassignable(state);
370  }
371
372  /**
373   * Check if a region state is one of offline states that
374   * can't transition to pending_close/closing (unassign/offline)
375   */
376  public static boolean isUnassignable(State state) {
377    return state == State.MERGED || state == State.SPLIT || state == State.OFFLINE
378      || state == State.SPLITTING_NEW || state == State.MERGING_NEW;
379  }
380
381  @Override
382  public String toString() {
383    return "{" + hri.getShortNameToLog()
384      + " state=" + state
385      + ", ts=" + stamp
386      + ", server=" + serverName + "}";
387  }
388
389  /**
390   * A slower (but more easy-to-read) stringification
391   */
392  public String toDescriptiveString() {
393    long relTime = System.currentTimeMillis() - stamp;
394    return hri.getRegionNameAsString()
395      + " state=" + state
396      + ", ts=" + new Date(stamp) + " (" + (relTime/1000) + "s ago)"
397      + ", server=" + serverName;
398  }
399
400  /**
401   * Convert a RegionState to an HBaseProtos.RegionState
402   *
403   * @return the converted HBaseProtos.RegionState
404   */
405  public ClusterStatusProtos.RegionState convert() {
406    ClusterStatusProtos.RegionState.Builder regionState = ClusterStatusProtos.RegionState.newBuilder();
407    regionState.setRegionInfo(ProtobufUtil.toRegionInfo(hri));
408    regionState.setState(state.convert());
409    regionState.setStamp(getStamp());
410    return regionState.build();
411  }
412
413  /**
414   * Convert a protobuf HBaseProtos.RegionState to a RegionState
415   *
416   * @return the RegionState
417   */
418  public static RegionState convert(ClusterStatusProtos.RegionState proto) {
419    return new RegionState(ProtobufUtil.toRegionInfo(proto.getRegionInfo()),
420      State.convert(proto.getState()), proto.getStamp(), null);
421  }
422
423  /**
424   * Check if two states are the same, except timestamp
425   */
426  @Override
427  public boolean equals(Object obj) {
428    if (this == obj) return true;
429    if (obj == null || getClass() != obj.getClass()) {
430      return false;
431    }
432    RegionState tmp = (RegionState)obj;
433
434    return RegionInfo.COMPARATOR.compare(tmp.hri, hri) == 0 && tmp.state == state
435      && ((serverName != null && serverName.equals(tmp.serverName))
436        || (tmp.serverName == null && serverName == null));
437  }
438
439  /**
440   * Don't count timestamp in hash code calculation
441   */
442  @Override
443  public int hashCode() {
444    return (serverName != null ? serverName.hashCode() * 11 : 0)
445      + hri.hashCode() + 5 * state.ordinal();
446  }
447}