| /** |
| * 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)); |
| } |
| } |