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.hadoop.hbase.util.EnvironmentEdgeManager;
024import org.apache.yetus.audience.InterfaceAudience;
025import org.apache.yetus.audience.InterfaceStability;
026
027import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
028import org.apache.hadoop.hbase.shaded.protobuf.generated.ClusterStatusProtos;
029
030/**
031 * State of a Region while undergoing transitions. 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     * @return the RegionState.State
127     */
128    public static State convert(ClusterStatusProtos.RegionState.State protoState) {
129      State state;
130      switch (protoState) {
131        case OFFLINE:
132          state = OFFLINE;
133          break;
134        case PENDING_OPEN:
135        case OPENING:
136          state = OPENING;
137          break;
138        case OPEN:
139          state = OPEN;
140          break;
141        case PENDING_CLOSE:
142        case CLOSING:
143          state = CLOSING;
144          break;
145        case CLOSED:
146          state = CLOSED;
147          break;
148        case SPLITTING:
149          state = SPLITTING;
150          break;
151        case SPLIT:
152          state = SPLIT;
153          break;
154        case FAILED_OPEN:
155          state = FAILED_OPEN;
156          break;
157        case FAILED_CLOSE:
158          state = FAILED_CLOSE;
159          break;
160        case MERGING:
161          state = MERGING;
162          break;
163        case MERGED:
164          state = MERGED;
165          break;
166        case SPLITTING_NEW:
167          state = SPLITTING_NEW;
168          break;
169        case MERGING_NEW:
170          state = MERGING_NEW;
171          break;
172        case ABNORMALLY_CLOSED:
173          state = ABNORMALLY_CLOSED;
174          break;
175        default:
176          throw new IllegalStateException("Unhandled state " + protoState);
177      }
178      return state;
179    }
180  }
181
182  private final long stamp;
183  private final RegionInfo hri;
184  private final ServerName serverName;
185  private final State state;
186  // The duration of region in transition
187  private long ritDuration;
188
189  public static RegionState createForTesting(RegionInfo region, State state) {
190    return new RegionState(region, state, EnvironmentEdgeManager.currentTime(), null);
191  }
192
193  public RegionState(RegionInfo region, State state, ServerName serverName) {
194    this(region, state, EnvironmentEdgeManager.currentTime(), serverName);
195  }
196
197  public RegionState(RegionInfo region, State state, long stamp, ServerName serverName) {
198    this(region, state, stamp, serverName, 0);
199  }
200
201  public RegionState(RegionInfo region, State state, long stamp, ServerName serverName,
202    long ritDuration) {
203    this.hri = region;
204    this.state = state;
205    this.stamp = stamp;
206    this.serverName = serverName;
207    this.ritDuration = ritDuration;
208  }
209
210  public State getState() {
211    return state;
212  }
213
214  public long getStamp() {
215    return stamp;
216  }
217
218  public RegionInfo getRegion() {
219    return hri;
220  }
221
222  public ServerName getServerName() {
223    return serverName;
224  }
225
226  public long getRitDuration() {
227    return ritDuration;
228  }
229
230  /**
231   * Update the duration of region in transition
232   * @param previousStamp previous RegionState's timestamp
233   */
234  @InterfaceAudience.Private
235  void updateRitDuration(long previousStamp) {
236    this.ritDuration += (this.stamp - previousStamp);
237  }
238
239  public boolean isClosing() {
240    return state == State.CLOSING;
241  }
242
243  public boolean isClosed() {
244    return state == State.CLOSED;
245  }
246
247  public boolean isClosedOrAbnormallyClosed() {
248    return isClosed() || this.state == State.ABNORMALLY_CLOSED;
249  }
250
251  public boolean isOpening() {
252    return state == State.OPENING;
253  }
254
255  public boolean isOpened() {
256    return state == State.OPEN;
257  }
258
259  public boolean isOffline() {
260    return state == State.OFFLINE;
261  }
262
263  public boolean isSplitting() {
264    return state == State.SPLITTING;
265  }
266
267  public boolean isSplit() {
268    return state == State.SPLIT;
269  }
270
271  public boolean isSplittingNew() {
272    return state == State.SPLITTING_NEW;
273  }
274
275  public boolean isFailedOpen() {
276    return state == State.FAILED_OPEN;
277  }
278
279  public boolean isFailedClose() {
280    return state == State.FAILED_CLOSE;
281  }
282
283  public boolean isMerging() {
284    return state == State.MERGING;
285  }
286
287  public boolean isMerged() {
288    return state == State.MERGED;
289  }
290
291  public boolean isMergingNew() {
292    return state == State.MERGING_NEW;
293  }
294
295  public boolean isOnServer(final ServerName sn) {
296    return serverName != null && serverName.equals(sn);
297  }
298
299  public boolean isMergingOnServer(final ServerName sn) {
300    return isOnServer(sn) && isMerging();
301  }
302
303  public boolean isMergingNewOnServer(final ServerName sn) {
304    return isOnServer(sn) && isMergingNew();
305  }
306
307  public boolean isMergingNewOrOpenedOnServer(final ServerName sn) {
308    return isOnServer(sn) && (isMergingNew() || isOpened());
309  }
310
311  public boolean isMergingNewOrOfflineOnServer(final ServerName sn) {
312    return isOnServer(sn) && (isMergingNew() || isOffline());
313  }
314
315  public boolean isSplittingOnServer(final ServerName sn) {
316    return isOnServer(sn) && isSplitting();
317  }
318
319  public boolean isSplittingNewOnServer(final ServerName sn) {
320    return isOnServer(sn) && isSplittingNew();
321  }
322
323  public boolean isSplittingOrOpenedOnServer(final ServerName sn) {
324    return isOnServer(sn) && (isSplitting() || isOpened());
325  }
326
327  public boolean isSplittingOrSplitOnServer(final ServerName sn) {
328    return isOnServer(sn) && (isSplitting() || isSplit());
329  }
330
331  public boolean isClosingOrClosedOnServer(final ServerName sn) {
332    return isOnServer(sn) && (isClosing() || isClosed());
333  }
334
335  public boolean isOpeningOrFailedOpenOnServer(final ServerName sn) {
336    return isOnServer(sn) && (isOpening() || isFailedOpen());
337  }
338
339  public boolean isOpeningOrOpenedOnServer(final ServerName sn) {
340    return isOnServer(sn) && (isOpening() || isOpened());
341  }
342
343  public boolean isOpenedOnServer(final ServerName sn) {
344    return isOnServer(sn) && isOpened();
345  }
346
347  /**
348   * Check if a region state can transition to offline
349   */
350  public boolean isReadyToOffline() {
351    return isMerged() || isSplit() || isOffline() || isSplittingNew() || isMergingNew();
352  }
353
354  /**
355   * Check if a region state can transition to online
356   */
357  public boolean isReadyToOnline() {
358    return isOpened() || isSplittingNew() || isMergingNew();
359  }
360
361  /**
362   * Check if a region state is one of offline states that can't transition to pending_close/closing
363   * (unassign/offline)
364   */
365  public boolean isUnassignable() {
366    return isUnassignable(state);
367  }
368
369  /**
370   * Check if a region state is one of offline states that can't transition to pending_close/closing
371   * (unassign/offline)
372   */
373  public static boolean isUnassignable(State state) {
374    return state == State.MERGED || state == State.SPLIT || state == State.OFFLINE
375      || state == State.SPLITTING_NEW || state == State.MERGING_NEW;
376  }
377
378  @Override
379  public String toString() {
380    return "{" + hri.getShortNameToLog() + " state=" + state + ", ts=" + stamp + ", server="
381      + serverName + "}";
382  }
383
384  /**
385   * A slower (but more easy-to-read) stringification
386   */
387  public String toDescriptiveString() {
388    long relTime = EnvironmentEdgeManager.currentTime() - stamp;
389    return hri.getRegionNameAsString() + " state=" + state + ", ts=" + new Date(stamp) + " ("
390      + (relTime / 1000) + "s ago)" + ", server=" + serverName;
391  }
392
393  /**
394   * Convert a RegionState to an HBaseProtos.RegionState
395   * @return the converted HBaseProtos.RegionState
396   */
397  public ClusterStatusProtos.RegionState convert() {
398    ClusterStatusProtos.RegionState.Builder regionState =
399      ClusterStatusProtos.RegionState.newBuilder();
400    regionState.setRegionInfo(ProtobufUtil.toRegionInfo(hri));
401    regionState.setState(state.convert());
402    regionState.setStamp(getStamp());
403    return regionState.build();
404  }
405
406  /**
407   * Convert a protobuf HBaseProtos.RegionState to a RegionState
408   * @return the RegionState
409   */
410  public static RegionState convert(ClusterStatusProtos.RegionState proto) {
411    return new RegionState(ProtobufUtil.toRegionInfo(proto.getRegionInfo()),
412      State.convert(proto.getState()), proto.getStamp(), null);
413  }
414
415  /**
416   * Check if two states are the same, except timestamp
417   */
418  @Override
419  public boolean equals(Object obj) {
420    if (this == obj) {
421      return true;
422    }
423    if (!(obj instanceof RegionState)) {
424      return false;
425    }
426    RegionState tmp = (RegionState) obj;
427    return RegionInfo.COMPARATOR.compare(tmp.hri, hri) == 0 && tmp.state == state
428      && ((serverName != null && serverName.equals(tmp.serverName))
429        || (tmp.serverName == null && serverName == null));
430  }
431
432  /**
433   * Don't count timestamp in hash code calculation
434   */
435  @Override
436  public int hashCode() {
437    return (serverName != null ? serverName.hashCode() * 11 : 0) + hri.hashCode()
438      + 5 * state.ordinal();
439  }
440}