diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/cleaner/CleanerChore.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/cleaner/CleanerChore.java index 944db106703..775d8f96034 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/cleaner/CleanerChore.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/cleaner/CleanerChore.java @@ -119,7 +119,7 @@ public abstract class CleanerChore extends Schedu * @param poolSize size from configuration * @return size of pool after calculation */ - private int calculatePoolSize(String poolSize) { + int calculatePoolSize(String poolSize) { if (poolSize.matches("[1-9][0-9]*")) { // If poolSize is an integer, return it directly, // but upmost to the number of available processors. @@ -130,7 +130,13 @@ public abstract class CleanerChore extends Schedu return size; } else if (poolSize.matches("0.[0-9]+|1.0")) { // if poolSize is a double, return poolSize * availableProcessors; - return (int) (AVAIL_PROCESSORS * Double.valueOf(poolSize)); + // Ensure that we always return at least one. + int computedThreads = (int) (AVAIL_PROCESSORS * Double.valueOf(poolSize)); + if (computedThreads < 1) { + LOG.debug("Computed {} threads for CleanerChore, using 1 instead", computedThreads); + return 1; + } + return computedThreads; } else { LOG.error("Unrecognized value: " + poolSize + " for " + CHORE_POOL_SIZE + ", use default config: " + DEFAULT_CHORE_POOL_SIZE + " instead."); diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/cleaner/TestCleanerChore.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/cleaner/TestCleanerChore.java index a9a856b9058..95178e2afc5 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/cleaner/TestCleanerChore.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/cleaner/TestCleanerChore.java @@ -388,6 +388,25 @@ public class TestCleanerChore { t.join(); } + @Test + public void testMinimumNumberOfThreads() throws Exception { + Stoppable stop = new StoppableImplementation(); + Configuration conf = UTIL.getConfiguration(); + Path testDir = UTIL.getDataTestDir(); + FileSystem fs = UTIL.getTestFileSystem(); + String confKey = "hbase.test.cleaner.delegates"; + conf.set(confKey, AlwaysDelete.class.getName()); + conf.set(CleanerChore.CHORE_POOL_SIZE, "2"); + AllValidPaths chore = new AllValidPaths("test-file-cleaner", stop, conf, fs, testDir, confKey); + int numProcs = Runtime.getRuntime().availableProcessors(); + // Sanity + assertEquals(numProcs, chore.calculatePoolSize(Integer.toString(numProcs))); + // The implementation does not allow us to set more threads than we have processors + assertEquals(numProcs, chore.calculatePoolSize(Integer.toString(numProcs + 2))); + // Force us into the branch that is multiplying 0.0 against the number of processors + assertEquals(1, chore.calculatePoolSize("0.0")); + } + private void createFiles(FileSystem fs, Path parentDir, int numOfFiles) throws IOException { Random random = new Random(); for (int i = 0; i < numOfFiles; i++) {