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