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.policies;
018
019import java.io.IOException;
020import java.util.List;
021import java.util.Objects;
022
023import org.apache.hadoop.fs.FileStatus;
024import org.apache.hadoop.fs.FileSystem;
025import org.apache.hadoop.fs.Path;
026import org.apache.yetus.audience.InterfaceAudience;
027import org.apache.hadoop.hbase.client.Mutation;
028import org.apache.hadoop.hbase.quotas.SpaceLimitingException;
029import org.apache.hadoop.hbase.quotas.SpaceQuotaSnapshot;
030import org.apache.hadoop.hbase.quotas.SpaceViolationPolicyEnforcement;
031
032/**
033 * The default implementation for {@link SpaceViolationPolicyEnforcement}. This is done because all
034 * tables, whether or not they're in violation now, should be checking bulk loads to proactively
035 * catch a swell of files that would push the table into violation.
036 */
037@InterfaceAudience.Private
038public class DefaultViolationPolicyEnforcement extends AbstractViolationPolicyEnforcement {
039
040  @Override
041  public void enable() throws IOException {}
042
043  @Override
044  public void disable() throws IOException {}
045
046  @Override
047  public String getPolicyName() {
048    return "BulkLoadVerifying";
049  }
050
051  @Override
052  public void check(Mutation m) throws SpaceLimitingException {}
053
054  @Override
055  public boolean shouldCheckBulkLoads() {
056    // Reference check. The singleton is used when no quota exists to check against
057    return SpaceQuotaSnapshot.getNoSuchSnapshot() != quotaSnapshot;
058  }
059
060  @Override
061  public void checkBulkLoad(FileSystem fs, List<String> paths) throws SpaceLimitingException {
062    // Compute the amount of space that could be used to save some arithmetic in the for-loop
063    final long sizeAvailableForBulkLoads = quotaSnapshot.getLimit() - quotaSnapshot.getUsage();
064    long size = 0L;
065    for (String path : paths) {
066      size += addSingleFile(fs, path);
067      if (size > sizeAvailableForBulkLoads) {
068        break;
069      }
070    }
071    if (size > sizeAvailableForBulkLoads) {
072      throw new SpaceLimitingException(getPolicyName(), "Bulk load of " + paths
073          + " is disallowed because the file(s) exceed the limits of a space quota.");
074    }
075  }
076
077  private long addSingleFile(FileSystem fs, String path) throws SpaceLimitingException {
078    final FileStatus status;
079    try {
080      status = fs.getFileStatus(new Path(Objects.requireNonNull(path)));
081    } catch (IOException e) {
082      throw new SpaceLimitingException(
083          getPolicyName(), "Could not verify length of file to bulk load", e);
084    }
085    if (!status.isFile()) {
086      throw new IllegalArgumentException(path + " is not a file.");
087    }
088    return status.getLen();
089  }
090}