blob: 851006759cec7ebcd18bb0be93b8bb2e1755c927 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hadoop.hbase.regionserver;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.HColumnDescriptor;
import org.apache.hadoop.hbase.HRegionInfo;
import org.apache.hadoop.hbase.regionserver.compactions.CompactionRequest;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;
import org.mockito.Mockito;
/**
* Compact passed set of files.
* Create an instance and then call {@ink #compact(Store, Collection, boolean, long)}.
* Call this classes {@link #main(String[])} to see how to run compaction code
* 'standalone'.
*/
public class CompactionTool implements Tool {
// Instantiates a Store instance and a mocked up HRegion. The compaction code
// requires a StoreScanner and a StoreScanner has to have a Store; its too
// tangled to do without (Store needs an HRegion which is another tangle).
// TODO: Undo the tangles some day.
private Configuration conf;
CompactionTool() {
super();
}
@Override
public Configuration getConf() {
return this.conf;
}
@Override
public void setConf(Configuration c) {
this.conf = c;
}
private int usage(final int errCode) {
return usage(errCode, null);
}
private int usage(final int errCode, final String errMsg) {
if (errMsg != null) System.err.println("ERROR: " + errMsg);
System.err.println("Usage: CompactionTool [options] <inputdir>");
System.err.println(" To preserve input files, pass -Dhbase.hstore.compaction.complete=false");
System.err.println(" To set tmp dir, pass -Dhbase.tmp.dir=ALTERNATE_DIR");
System.err.println(" To stop delete of compacted file, pass -Dhbase.compactiontool.delete=false");
return errCode;
}
int checkdir(final FileSystem fs, final Path p) throws IOException {
if (!fs.exists(p)) {
return usage(-2, p.toString() + " does not exist.");
}
if (!fs.getFileStatus(p).isDir()) {
return usage(-3, p.toString() + " must be a directory");
}
return 0;
}
/**
* Mock up an HRegion instance. Need to return an HRegionInfo when asked.
* Also need an executor to run storefile open/closes. Need to repeat
* the thenReturn on getOpenAndCloseThreadPool because otherwise it returns
* cache of first which is closed during the opening. Also, need to return
* tmpdir, etc.
* @param hri
* @param tmpdir
* @return
*/
private HRegion createHRegion(final HRegionInfo hri, final Path tmpdir) {
HRegion mockedHRegion = Mockito.mock(HRegion.class);
Mockito.when(mockedHRegion.getRegionInfo()).thenReturn(hri);
Mockito.when(mockedHRegion.getStoreFileOpenAndCloseThreadPool(Mockito.anyString())).
thenReturn(HRegion.getOpenAndCloseThreadPool(1, "mockedRegion.opener")).
thenReturn(HRegion.getOpenAndCloseThreadPool(1, "mockedRegion.closer"));
Mockito.when(mockedHRegion.areWritesEnabled()).thenReturn(true);
Mockito.when(mockedHRegion.getTmpDir()).thenReturn(tmpdir);
return mockedHRegion;
}
/**
* Fake up a Store around the passed <code>storedir</code>.
* @param fs
* @param storedir
* @param tmpdir
* @return
* @throws IOException
*/
private HStore getStore(final FileSystem fs, final Path storedir, final Path tmpdir)
throws IOException {
// TODO: Let config on table and column family be configurable from
// command-line setting versions, etc. For now do defaults
HColumnDescriptor hcd = new HColumnDescriptor("f");
HRegionInfo hri = new HRegionInfo(Bytes.toBytes("t"));
// Get a shell of an HRegion w/ enough functionality to make Store happy.
HRegion region = createHRegion(hri, tmpdir);
// Create a Store w/ check of hbase.rootdir blanked out and return our
// list of files instead of have Store search its home dir.
return new HStore(tmpdir, region, hcd, fs, getConf()) {
@Override
public FileStatus[] getStoreFiles() throws IOException {
return this.fs.listStatus(getHomedir());
}
@Override
Path createStoreHomeDir(FileSystem fs, Path homedir) throws IOException {
return storedir;
}
};
}
@Override
public int run(final String[] args) throws Exception {
if (args.length == 0) return usage(-1);
FileSystem fs = FileSystem.get(getConf());
final Path inputdir = new Path(args[0]);
final Path tmpdir = new Path(getConf().get("hbase.tmp.dir"));
int errCode = checkdir(fs, inputdir);
if (errCode != 0) return errCode;
errCode = checkdir(fs, tmpdir);
if (errCode != 0) return errCode;
// Get a Store that wraps the inputdir of files to compact.
HStore store = getStore(fs, inputdir, tmpdir);
// Now we have a Store, run a compaction of passed files.
try {
CompactionRequest cr = store.requestCompaction();
StoreFile sf = store.compact(cr);
if (sf != null) {
sf.closeReader(true);
if (conf.getBoolean("hbase.compactiontool.delete", true)) {
if (!fs.delete(sf.getPath(), false)) {
throw new IOException("Failed delete of " + sf.getPath());
}
}
}
} finally {
store.close();
}
return 0;
}
public static void main(String[] args) throws Exception {
System.exit(ToolRunner.run(HBaseConfiguration.create(),
new CompactionTool(), args));
}
}