001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to you under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.hadoop.hbase.quotas;
018
019import java.util.Objects;
020
021import org.apache.commons.lang3.builder.HashCodeBuilder;
022import org.apache.yetus.audience.InterfaceAudience;
023import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
024import org.apache.hadoop.hbase.shaded.protobuf.generated.QuotaProtos;
025import org.apache.hadoop.util.StringUtils;
026
027/**
028 * A point-in-time view of a space quota on a table.
029 */
030@InterfaceAudience.Private
031public class SpaceQuotaSnapshot {
032  private static final SpaceQuotaSnapshot NO_SUCH_SNAPSHOT = new SpaceQuotaSnapshot(
033      SpaceQuotaStatus.notInViolation(), 0, Long.MAX_VALUE);
034  private final SpaceQuotaStatus quotaStatus;
035  private final long usage;
036  private final long limit;
037
038  /**
039   * Encapsulates the state of a quota on a table. The quota may or may not be in violation.
040   * If the quota is not in violation, the violation may be null. If the quota is in violation,
041   * there is guaranteed to be a non-null violation policy.
042   */
043  @InterfaceAudience.Private
044  public static class SpaceQuotaStatus {
045    private static final SpaceQuotaStatus NOT_IN_VIOLATION = new SpaceQuotaStatus(null, false);
046    final SpaceViolationPolicy policy;
047    final boolean inViolation;
048
049    /**
050     * Constructs a {@code SpaceQuotaSnapshot} which is in violation of the provided {@code policy}.
051     *
052     * Use {@link #notInViolation()} to obtain an instance of this class for the cases when the
053     * quota is not in violation.
054     *
055     * @param policy The non-null policy being violated.
056     */
057    public SpaceQuotaStatus(SpaceViolationPolicy policy) {
058      // If the caller is instantiating a status, the policy must be non-null
059      this (Objects.requireNonNull(policy), true);
060    }
061
062    private SpaceQuotaStatus(SpaceViolationPolicy policy, boolean inViolation) {
063      this.policy = policy;
064      this.inViolation = inViolation;
065    }
066
067    /**
068     * Returns the violation policy, which may be null. It is guaranteed to be non-null if
069     * {@link #isInViolation()} is {@code true}, but may be null otherwise.
070     */
071    public SpaceViolationPolicy getPolicy() {
072      return policy;
073    }
074
075    /**
076     * @return {@code true} if the quota is being violated, {@code false} otherwise.
077     */
078    public boolean isInViolation() {
079      return inViolation;
080    }
081
082    /**
083     * Returns a singleton referring to a quota which is not in violation.
084     */
085    public static SpaceQuotaStatus notInViolation() {
086      return NOT_IN_VIOLATION;
087    }
088
089    @Override
090    public int hashCode() {
091      return new HashCodeBuilder().append(policy == null ? 0 : policy.hashCode())
092          .append(inViolation).toHashCode();
093    }
094
095    @Override
096    public boolean equals(Object o) {
097      if (o instanceof SpaceQuotaStatus) {
098        SpaceQuotaStatus other = (SpaceQuotaStatus) o;
099        return Objects.equals(policy, other.policy) && inViolation == other.inViolation;
100      }
101      return false;
102    }
103
104    @Override
105    public String toString() {
106      StringBuilder sb = new StringBuilder(getClass().getSimpleName());
107      sb.append("[policy=").append(policy);
108      sb.append(", inViolation=").append(inViolation).append("]");
109      return sb.toString();
110    }
111
112    public static QuotaProtos.SpaceQuotaStatus toProto(SpaceQuotaStatus status) {
113      QuotaProtos.SpaceQuotaStatus.Builder builder = QuotaProtos.SpaceQuotaStatus.newBuilder();
114      builder.setInViolation(status.inViolation);
115      if (status.isInViolation()) {
116        builder.setViolationPolicy(ProtobufUtil.toProtoViolationPolicy(status.getPolicy()));
117      }
118      return builder.build();
119    }
120
121    public static SpaceQuotaStatus toStatus(QuotaProtos.SpaceQuotaStatus proto) {
122      if (proto.getInViolation()) {
123        return new SpaceQuotaStatus(ProtobufUtil.toViolationPolicy(proto.getViolationPolicy()));
124      } else {
125        return NOT_IN_VIOLATION;
126      }
127    }
128  }
129
130  public SpaceQuotaSnapshot(SpaceQuotaStatus quotaStatus, long usage, long limit) {
131    this.quotaStatus = Objects.requireNonNull(quotaStatus);
132    this.usage = usage;
133    this.limit = limit;
134  }
135
136  /**
137   * Returns the status of the quota.
138   */
139  public SpaceQuotaStatus getQuotaStatus() {
140    return quotaStatus;
141  }
142
143  /**
144   * Returns the current usage, in bytes, of the target (e.g. table, namespace).
145   */
146  public long getUsage() {
147    return usage;
148  }
149
150  /**
151   * Returns the limit, in bytes, of the target (e.g. table, namespace).
152   */
153  public long getLimit() {
154    return limit;
155  }
156
157  @Override
158  public int hashCode() {
159    return new HashCodeBuilder()
160        .append(quotaStatus.hashCode())
161        .append(usage)
162        .append(limit)
163        .toHashCode();
164  }
165
166  @Override
167  public boolean equals(Object o) {
168    if (o instanceof SpaceQuotaSnapshot) {
169      SpaceQuotaSnapshot other = (SpaceQuotaSnapshot) o;
170      return quotaStatus.equals(other.quotaStatus) && usage == other.usage && limit == other.limit;
171    }
172    return false;
173  }
174
175  @Override
176  public String toString() {
177    StringBuilder sb = new StringBuilder(32);
178    sb.append("SpaceQuotaSnapshot[policy=").append(quotaStatus).append(", use=");
179    sb.append(StringUtils.byteDesc(usage)).append("/");
180    sb.append(StringUtils.byteDesc(limit)).append("]");
181    return sb.toString();
182  }
183
184  // ProtobufUtil is in hbase-client, and this doesn't need to be public.
185  public static SpaceQuotaSnapshot toSpaceQuotaSnapshot(QuotaProtos.SpaceQuotaSnapshot proto) {
186    return new SpaceQuotaSnapshot(SpaceQuotaStatus.toStatus(proto.getQuotaStatus()),
187        proto.getQuotaUsage(), proto.getQuotaLimit());
188  }
189
190  public static QuotaProtos.SpaceQuotaSnapshot toProtoSnapshot(SpaceQuotaSnapshot snapshot) {
191    return QuotaProtos.SpaceQuotaSnapshot.newBuilder()
192        .setQuotaStatus(SpaceQuotaStatus.toProto(snapshot.getQuotaStatus()))
193        .setQuotaUsage(snapshot.getUsage()).setQuotaLimit(snapshot.getLimit()).build();
194  }
195
196  /**
197   * Returns a singleton that corresponds to no snapshot information.
198   */
199  public static SpaceQuotaSnapshot getNoSuchSnapshot() {
200    return NO_SUCH_SNAPSHOT;
201  }
202}