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.util.compaction;
019
020import java.io.IOException;
021import java.util.Collection;
022import java.util.List;
023import java.util.Optional;
024import java.util.Set;
025import org.apache.hadoop.fs.FileStatus;
026import org.apache.hadoop.fs.FileSystem;
027import org.apache.hadoop.fs.Path;
028import org.apache.hadoop.hbase.client.Admin;
029import org.apache.hadoop.hbase.client.Connection;
030import org.apache.hadoop.hbase.client.RegionInfo;
031import org.apache.hadoop.hbase.regionserver.HRegionFileSystem;
032import org.apache.hadoop.hbase.regionserver.StoreFileInfo;
033import org.apache.hadoop.hbase.util.CommonFSUtils;
034import org.apache.hadoop.hbase.util.FSUtils;
035import org.apache.yetus.audience.InterfaceAudience;
036import org.slf4j.Logger;
037import org.slf4j.LoggerFactory;
038
039import org.apache.hbase.thirdparty.com.google.common.collect.Sets;
040
041@InterfaceAudience.Private
042class MajorCompactionRequest {
043
044  private static final Logger LOG = LoggerFactory.getLogger(MajorCompactionRequest.class);
045
046  protected final Connection connection;
047  protected final RegionInfo region;
048  private Set<String> stores;
049
050  MajorCompactionRequest(Connection connection, RegionInfo region) {
051    this.connection = connection;
052    this.region = region;
053  }
054
055  MajorCompactionRequest(Connection connection, RegionInfo region, Set<String> stores) {
056    this(connection, region);
057    this.stores = stores;
058  }
059
060  static Optional<MajorCompactionRequest> newRequest(Connection connection, RegionInfo info,
061    Set<String> stores, long timestamp) throws IOException {
062    MajorCompactionRequest request = new MajorCompactionRequest(connection, info, stores);
063    return request.createRequest(connection, stores, timestamp);
064  }
065
066  RegionInfo getRegion() {
067    return region;
068  }
069
070  Set<String> getStores() {
071    return stores;
072  }
073
074  void setStores(Set<String> stores) {
075    this.stores = stores;
076  }
077
078  Optional<MajorCompactionRequest> createRequest(Connection connection, Set<String> stores,
079    long timestamp) throws IOException {
080    Set<String> familiesToCompact = getStoresRequiringCompaction(stores, timestamp);
081    MajorCompactionRequest request = null;
082    if (!familiesToCompact.isEmpty()) {
083      request = new MajorCompactionRequest(connection, region, familiesToCompact);
084    }
085    return Optional.ofNullable(request);
086  }
087
088  Set<String> getStoresRequiringCompaction(Set<String> requestedStores, long timestamp)
089    throws IOException {
090    HRegionFileSystem fileSystem = getFileSystem();
091    Set<String> familiesToCompact = Sets.newHashSet();
092    for (String family : requestedStores) {
093      if (shouldCFBeCompacted(fileSystem, family, timestamp)) {
094        familiesToCompact.add(family);
095      }
096    }
097    return familiesToCompact;
098  }
099
100  boolean shouldCFBeCompacted(HRegionFileSystem fileSystem, String family, long ts)
101    throws IOException {
102    // do we have any store files?
103    Collection<StoreFileInfo> storeFiles = fileSystem.getStoreFiles(family);
104    if (storeFiles == null) {
105      LOG.info("Excluding store: " + family + " for compaction for region:  "
106        + fileSystem.getRegionInfo().getEncodedName(), " has no store files");
107      return false;
108    }
109    // check for reference files
110    if (fileSystem.hasReferences(family) && familyHasReferenceFile(fileSystem, family, ts)) {
111      LOG.info("Including store: " + family + " with: " + storeFiles.size()
112        + " files for compaction for region: " + fileSystem.getRegionInfo().getEncodedName());
113      return true;
114    }
115    // check store file timestamps
116    boolean includeStore = this.shouldIncludeStore(fileSystem, family, storeFiles, ts);
117    if (!includeStore) {
118      LOG.info("Excluding store: " + family + " for compaction for region:  "
119        + fileSystem.getRegionInfo().getEncodedName() + " already compacted");
120    }
121    return includeStore;
122  }
123
124  protected boolean shouldIncludeStore(HRegionFileSystem fileSystem, String family,
125    Collection<StoreFileInfo> storeFiles, long ts) throws IOException {
126
127    for (StoreFileInfo storeFile : storeFiles) {
128      if (storeFile.getModificationTime() < ts) {
129        LOG.info("Including store: " + family + " with: " + storeFiles.size()
130          + " files for compaction for region: " + fileSystem.getRegionInfo().getEncodedName());
131        return true;
132      }
133    }
134    return false;
135  }
136
137  protected boolean familyHasReferenceFile(HRegionFileSystem fileSystem, String family, long ts)
138    throws IOException {
139    List<Path> referenceFiles =
140      getReferenceFilePaths(fileSystem.getFileSystem(), fileSystem.getStoreDir(family));
141    for (Path referenceFile : referenceFiles) {
142      FileStatus status = fileSystem.getFileSystem().getFileLinkStatus(referenceFile);
143      if (status.getModificationTime() < ts) {
144        LOG.info("Including store: " + family + " for compaction for region:  "
145          + fileSystem.getRegionInfo().getEncodedName() + " (reference store files)");
146        return true;
147      }
148    }
149    return false;
150
151  }
152
153  List<Path> getReferenceFilePaths(FileSystem fileSystem, Path familyDir) throws IOException {
154    return FSUtils.getReferenceFilePaths(fileSystem, familyDir);
155  }
156
157  HRegionFileSystem getFileSystem() throws IOException {
158    try (Admin admin = connection.getAdmin()) {
159      return HRegionFileSystem.openRegionFromFileSystem(admin.getConfiguration(),
160        CommonFSUtils.getCurrentFileSystem(admin.getConfiguration()), CommonFSUtils.getTableDir(
161          CommonFSUtils.getRootDir(admin.getConfiguration()), region.getTable()),
162        region, true);
163    }
164  }
165
166  @Override
167  public String toString() {
168    return "region: " + region.getEncodedName() + " store(s): " + stores;
169  }
170}