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}