HBASE-17854 Use StealJobQueue in HFileCleaner after HBASE-17215

This commit is contained in:
Yu Li 2017-04-05 17:53:21 +08:00
parent a66d491892
commit cbcbcf4dcd
4 changed files with 102 additions and 48 deletions

View File

@ -22,7 +22,6 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
@ -35,6 +34,7 @@ import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hbase.Stoppable; import org.apache.hadoop.hbase.Stoppable;
import org.apache.hadoop.hbase.io.HFileLink; import org.apache.hadoop.hbase.io.HFileLink;
import org.apache.hadoop.hbase.regionserver.StoreFileInfo; import org.apache.hadoop.hbase.regionserver.StoreFileInfo;
import org.apache.hadoop.hbase.util.StealJobQueue;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
/** /**
@ -57,23 +57,23 @@ public class HFileCleaner extends CleanerChore<BaseHFileCleanerDelegate> impleme
"hbase.regionserver.thread.hfilecleaner.throttle"; "hbase.regionserver.thread.hfilecleaner.throttle";
public final static int DEFAULT_HFILE_DELETE_THROTTLE_THRESHOLD = 64 * 1024 * 1024;// 64M public final static int DEFAULT_HFILE_DELETE_THROTTLE_THRESHOLD = 64 * 1024 * 1024;// 64M
// Configuration key for large queue size // Configuration key for large queue initial size
public final static String LARGE_HFILE_DELETE_QUEUE_SIZE = public final static String LARGE_HFILE_QUEUE_INIT_SIZE =
"hbase.regionserver.hfilecleaner.large.queue.size"; "hbase.regionserver.hfilecleaner.large.queue.size";
public final static int DEFAULT_LARGE_HFILE_DELETE_QUEUE_SIZE = 1048576; public final static int DEFAULT_LARGE_HFILE_QUEUE_INIT_SIZE = 10240;
// Configuration key for small queue size // Configuration key for small queue initial size
public final static String SMALL_HFILE_DELETE_QUEUE_SIZE = public final static String SMALL_HFILE_QUEUE_INIT_SIZE =
"hbase.regionserver.hfilecleaner.small.queue.size"; "hbase.regionserver.hfilecleaner.small.queue.size";
public final static int DEFAULT_SMALL_HFILE_DELETE_QUEUE_SIZE = 1048576; public final static int DEFAULT_SMALL_HFILE_QUEUE_INIT_SIZE = 10240;
private static final Log LOG = LogFactory.getLog(HFileCleaner.class); private static final Log LOG = LogFactory.getLog(HFileCleaner.class);
BlockingQueue<HFileDeleteTask> largeFileQueue; StealJobQueue<HFileDeleteTask> largeFileQueue;
BlockingQueue<HFileDeleteTask> smallFileQueue; BlockingQueue<HFileDeleteTask> smallFileQueue;
private int throttlePoint; private int throttlePoint;
private int largeQueueSize; private int largeQueueInitSize;
private int smallQueueSize; private int smallQueueInitSize;
private List<Thread> threads = new ArrayList<Thread>(); private List<Thread> threads = new ArrayList<Thread>();
private boolean running; private boolean running;
@ -94,12 +94,12 @@ public class HFileCleaner extends CleanerChore<BaseHFileCleanerDelegate> impleme
directory, MASTER_HFILE_CLEANER_PLUGINS, params); directory, MASTER_HFILE_CLEANER_PLUGINS, params);
throttlePoint = throttlePoint =
conf.getInt(HFILE_DELETE_THROTTLE_THRESHOLD, DEFAULT_HFILE_DELETE_THROTTLE_THRESHOLD); conf.getInt(HFILE_DELETE_THROTTLE_THRESHOLD, DEFAULT_HFILE_DELETE_THROTTLE_THRESHOLD);
largeQueueSize = largeQueueInitSize =
conf.getInt(LARGE_HFILE_DELETE_QUEUE_SIZE, DEFAULT_LARGE_HFILE_DELETE_QUEUE_SIZE); conf.getInt(LARGE_HFILE_QUEUE_INIT_SIZE, DEFAULT_LARGE_HFILE_QUEUE_INIT_SIZE);
smallQueueSize = smallQueueInitSize =
conf.getInt(SMALL_HFILE_DELETE_QUEUE_SIZE, DEFAULT_SMALL_HFILE_DELETE_QUEUE_SIZE); conf.getInt(SMALL_HFILE_QUEUE_INIT_SIZE, DEFAULT_SMALL_HFILE_QUEUE_INIT_SIZE);
largeFileQueue = new LinkedBlockingQueue<HFileCleaner.HFileDeleteTask>(largeQueueSize); largeFileQueue = new StealJobQueue<>(largeQueueInitSize, smallQueueInitSize);
smallFileQueue = new LinkedBlockingQueue<HFileCleaner.HFileDeleteTask>(smallQueueSize); smallFileQueue = largeFileQueue.getStealFromQueue();
startHFileDeleteThreads(); startHFileDeleteThreads();
} }
@ -152,6 +152,7 @@ public class HFileCleaner extends CleanerChore<BaseHFileCleanerDelegate> impleme
private boolean dispatch(HFileDeleteTask task) { private boolean dispatch(HFileDeleteTask task) {
if (task.fileLength >= this.throttlePoint) { if (task.fileLength >= this.throttlePoint) {
if (!this.largeFileQueue.offer(task)) { if (!this.largeFileQueue.offer(task)) {
// should never arrive here as long as we use PriorityQueue
if (LOG.isTraceEnabled()) { if (LOG.isTraceEnabled()) {
LOG.trace("Large file deletion queue is full"); LOG.trace("Large file deletion queue is full");
} }
@ -159,6 +160,7 @@ public class HFileCleaner extends CleanerChore<BaseHFileCleanerDelegate> impleme
} }
} else { } else {
if (!this.smallFileQueue.offer(task)) { if (!this.smallFileQueue.offer(task)) {
// should never arrive here as long as we use PriorityQueue
if (LOG.isTraceEnabled()) { if (LOG.isTraceEnabled()) {
LOG.trace("Small file deletion queue is full"); LOG.trace("Small file deletion queue is full");
} }
@ -232,7 +234,7 @@ public class HFileCleaner extends CleanerChore<BaseHFileCleanerDelegate> impleme
} }
task.setResult(succeed); task.setResult(succeed);
if (succeed) { if (succeed) {
countDeletedFiles(queue == largeFileQueue); countDeletedFiles(task.fileLength >= throttlePoint, queue == largeFileQueue);
} }
} }
} }
@ -244,8 +246,8 @@ public class HFileCleaner extends CleanerChore<BaseHFileCleanerDelegate> impleme
} }
// Currently only for testing purpose // Currently only for testing purpose
private void countDeletedFiles(boolean isLarge) { private void countDeletedFiles(boolean isLargeFile, boolean fromLargeQueue) {
if (isLarge) { if (isLargeFile) {
if (deletedLargeFiles == Long.MAX_VALUE) { if (deletedLargeFiles == Long.MAX_VALUE) {
LOG.info("Deleted more than Long.MAX_VALUE large files, reset counter to 0"); LOG.info("Deleted more than Long.MAX_VALUE large files, reset counter to 0");
deletedLargeFiles = 0L; deletedLargeFiles = 0L;
@ -256,6 +258,9 @@ public class HFileCleaner extends CleanerChore<BaseHFileCleanerDelegate> impleme
LOG.info("Deleted more than Long.MAX_VALUE small files, reset counter to 0"); LOG.info("Deleted more than Long.MAX_VALUE small files, reset counter to 0");
deletedSmallFiles = 0L; deletedSmallFiles = 0L;
} }
if (fromLargeQueue && LOG.isTraceEnabled()) {
LOG.trace("Stolen a small file deletion task in large file thread");
}
deletedSmallFiles++; deletedSmallFiles++;
} }
} }
@ -273,7 +278,7 @@ public class HFileCleaner extends CleanerChore<BaseHFileCleanerDelegate> impleme
} }
} }
static class HFileDeleteTask { static class HFileDeleteTask implements Comparable<HFileDeleteTask> {
private static final long MAX_WAIT = 60 * 1000L; private static final long MAX_WAIT = 60 * 1000L;
private static final long WAIT_UNIT = 1000L; private static final long WAIT_UNIT = 1000L;
@ -315,6 +320,31 @@ public class HFileCleaner extends CleanerChore<BaseHFileCleanerDelegate> impleme
} }
return this.result; return this.result;
} }
@Override
public int compareTo(HFileDeleteTask o) {
long sub = this.fileLength - o.fileLength;
// smaller value with higher priority in PriorityQueue, and we intent to delete the larger
// file first.
return (sub > 0) ? -1 : (sub < 0 ? 1 : 0);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || !(o instanceof HFileDeleteTask)) {
return false;
}
HFileDeleteTask otherTask = (HFileDeleteTask) o;
return this.filePath.equals(otherTask.filePath) && (this.fileLength == otherTask.fileLength);
}
@Override
public int hashCode() {
return filePath.hashCode();
}
} }
@VisibleForTesting @VisibleForTesting
@ -333,13 +363,13 @@ public class HFileCleaner extends CleanerChore<BaseHFileCleanerDelegate> impleme
} }
@VisibleForTesting @VisibleForTesting
public long getLargeQueueSize() { public long getLargeQueueInitSize() {
return largeQueueSize; return largeQueueInitSize;
} }
@VisibleForTesting @VisibleForTesting
public long getSmallQueueSize() { public long getSmallQueueInitSize() {
return smallQueueSize; return smallQueueInitSize;
} }
@VisibleForTesting @VisibleForTesting
@ -351,15 +381,15 @@ public class HFileCleaner extends CleanerChore<BaseHFileCleanerDelegate> impleme
public void onConfigurationChange(Configuration conf) { public void onConfigurationChange(Configuration conf) {
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
builder.append("Updating configuration for HFileCleaner, previous throttle point: ") builder.append("Updating configuration for HFileCleaner, previous throttle point: ")
.append(throttlePoint).append(", largeQueueSize: ").append(largeQueueSize) .append(throttlePoint).append(", largeQueueInitSize: ").append(largeQueueInitSize)
.append(", smallQueueSize: ").append(smallQueueSize); .append(", smallQueueInitSize: ").append(smallQueueInitSize);
stopHFileDeleteThreads(); stopHFileDeleteThreads();
this.throttlePoint = this.throttlePoint =
conf.getInt(HFILE_DELETE_THROTTLE_THRESHOLD, DEFAULT_HFILE_DELETE_THROTTLE_THRESHOLD); conf.getInt(HFILE_DELETE_THROTTLE_THRESHOLD, DEFAULT_HFILE_DELETE_THROTTLE_THRESHOLD);
this.largeQueueSize = this.largeQueueInitSize =
conf.getInt(LARGE_HFILE_DELETE_QUEUE_SIZE, DEFAULT_LARGE_HFILE_DELETE_QUEUE_SIZE); conf.getInt(LARGE_HFILE_QUEUE_INIT_SIZE, DEFAULT_LARGE_HFILE_QUEUE_INIT_SIZE);
this.smallQueueSize = this.smallQueueInitSize =
conf.getInt(SMALL_HFILE_DELETE_QUEUE_SIZE, DEFAULT_SMALL_HFILE_DELETE_QUEUE_SIZE); conf.getInt(SMALL_HFILE_QUEUE_INIT_SIZE, DEFAULT_SMALL_HFILE_QUEUE_INIT_SIZE);
// record the left over tasks // record the left over tasks
List<HFileDeleteTask> leftOverTasks = new ArrayList<>(); List<HFileDeleteTask> leftOverTasks = new ArrayList<>();
for (HFileDeleteTask task : largeFileQueue) { for (HFileDeleteTask task : largeFileQueue) {
@ -368,11 +398,11 @@ public class HFileCleaner extends CleanerChore<BaseHFileCleanerDelegate> impleme
for (HFileDeleteTask task : smallFileQueue) { for (HFileDeleteTask task : smallFileQueue) {
leftOverTasks.add(task); leftOverTasks.add(task);
} }
largeFileQueue = new LinkedBlockingQueue<HFileCleaner.HFileDeleteTask>(largeQueueSize); largeFileQueue = new StealJobQueue<>(largeQueueInitSize, smallQueueInitSize);
smallFileQueue = new LinkedBlockingQueue<HFileCleaner.HFileDeleteTask>(smallQueueSize); smallFileQueue = largeFileQueue.getStealFromQueue();
threads.clear(); threads.clear();
builder.append("; new throttle point: ").append(throttlePoint).append(", largeQueueSize: ") builder.append("; new throttle point: ").append(throttlePoint).append(", largeQueueInitSize: ")
.append(largeQueueSize).append(", smallQueueSize: ").append(smallQueueSize); .append(largeQueueInitSize).append(", smallQueueInitSize: ").append(smallQueueInitSize);
LOG.debug(builder.toString()); LOG.debug(builder.toString());
startHFileDeleteThreads(); startHFileDeleteThreads();
// re-dispatch the left over tasks // re-dispatch the left over tasks

View File

@ -48,6 +48,7 @@ public class StealJobQueue<T> extends PriorityBlockingQueue<T> {
public StealJobQueue() { public StealJobQueue() {
this.stealFromQueue = new PriorityBlockingQueue<T>() { this.stealFromQueue = new PriorityBlockingQueue<T>() {
@Override @Override
public boolean offer(T t) { public boolean offer(T t) {
lock.lock(); lock.lock();
@ -61,6 +62,27 @@ public class StealJobQueue<T> extends PriorityBlockingQueue<T> {
}; };
} }
public StealJobQueue(int initCapacity, int stealFromQueueInitCapacity) {
super(initCapacity);
this.stealFromQueue = new PriorityBlockingQueue<T>(stealFromQueueInitCapacity) {
@Override
public boolean offer(T t) {
lock.lock();
try {
notEmpty.signal();
return super.offer(t);
} finally {
lock.unlock();
}
}
};
}
/**
* Get a queue whose job might be stolen by the consumer of this original queue
* @return the queue whose job could be stolen
*/
public BlockingQueue<T> getStealFromQueue() { public BlockingQueue<T> getStealFromQueue() {
return stealFromQueue; return stealFromQueue;
} }

View File

@ -322,9 +322,9 @@ public class TestHFileCleaner {
public void testOnConfigurationChange() throws Exception { public void testOnConfigurationChange() throws Exception {
// constants // constants
final int ORIGINAL_THROTTLE_POINT = 512 * 1024; final int ORIGINAL_THROTTLE_POINT = 512 * 1024;
final int ORIGINAL_QUEUE_SIZE = 512; final int ORIGINAL_QUEUE_INIT_SIZE = 512;
final int UPDATE_THROTTLE_POINT = 1024;// small enough to change large/small check final int UPDATE_THROTTLE_POINT = 1024;// small enough to change large/small check
final int UPDATE_QUEUE_SIZE = 1024; final int UPDATE_QUEUE_INIT_SIZE = 1024;
final int LARGE_FILE_NUM = 5; final int LARGE_FILE_NUM = 5;
final int SMALL_FILE_NUM = 20; final int SMALL_FILE_NUM = 20;
@ -332,8 +332,8 @@ public class TestHFileCleaner {
// no cleaner policies = delete all files // no cleaner policies = delete all files
conf.setStrings(HFileCleaner.MASTER_HFILE_CLEANER_PLUGINS, ""); conf.setStrings(HFileCleaner.MASTER_HFILE_CLEANER_PLUGINS, "");
conf.setInt(HFileCleaner.HFILE_DELETE_THROTTLE_THRESHOLD, ORIGINAL_THROTTLE_POINT); conf.setInt(HFileCleaner.HFILE_DELETE_THROTTLE_THRESHOLD, ORIGINAL_THROTTLE_POINT);
conf.setInt(HFileCleaner.LARGE_HFILE_DELETE_QUEUE_SIZE, ORIGINAL_QUEUE_SIZE); conf.setInt(HFileCleaner.LARGE_HFILE_QUEUE_INIT_SIZE, ORIGINAL_QUEUE_INIT_SIZE);
conf.setInt(HFileCleaner.SMALL_HFILE_DELETE_QUEUE_SIZE, ORIGINAL_QUEUE_SIZE); conf.setInt(HFileCleaner.SMALL_HFILE_QUEUE_INIT_SIZE, ORIGINAL_QUEUE_INIT_SIZE);
Server server = new DummyServer(); Server server = new DummyServer();
Path archivedHfileDir = Path archivedHfileDir =
new Path(UTIL.getDataTestDirOnTestFS(), HConstants.HFILE_ARCHIVE_DIRECTORY); new Path(UTIL.getDataTestDirOnTestFS(), HConstants.HFILE_ARCHIVE_DIRECTORY);
@ -342,8 +342,8 @@ public class TestHFileCleaner {
FileSystem fs = UTIL.getDFSCluster().getFileSystem(); FileSystem fs = UTIL.getDFSCluster().getFileSystem();
final HFileCleaner cleaner = new HFileCleaner(1000, server, conf, fs, archivedHfileDir); final HFileCleaner cleaner = new HFileCleaner(1000, server, conf, fs, archivedHfileDir);
Assert.assertEquals(ORIGINAL_THROTTLE_POINT, cleaner.getThrottlePoint()); Assert.assertEquals(ORIGINAL_THROTTLE_POINT, cleaner.getThrottlePoint());
Assert.assertEquals(ORIGINAL_QUEUE_SIZE, cleaner.getLargeQueueSize()); Assert.assertEquals(ORIGINAL_QUEUE_INIT_SIZE, cleaner.getLargeQueueInitSize());
Assert.assertEquals(ORIGINAL_QUEUE_SIZE, cleaner.getSmallQueueSize()); Assert.assertEquals(ORIGINAL_QUEUE_INIT_SIZE, cleaner.getSmallQueueInitSize());
// clean up archive directory and create files for testing // clean up archive directory and create files for testing
fs.delete(archivedHfileDir, true); fs.delete(archivedHfileDir, true);
@ -359,22 +359,24 @@ public class TestHFileCleaner {
}; };
t.setDaemon(true); t.setDaemon(true);
t.start(); t.start();
// let the cleaner run for some while // wait until file clean started
Thread.sleep(20); while (cleaner.getNumOfDeletedSmallFiles() == 0) {
Thread.yield();
}
// trigger configuration change // trigger configuration change
Configuration newConf = new Configuration(conf); Configuration newConf = new Configuration(conf);
newConf.setInt(HFileCleaner.HFILE_DELETE_THROTTLE_THRESHOLD, UPDATE_THROTTLE_POINT); newConf.setInt(HFileCleaner.HFILE_DELETE_THROTTLE_THRESHOLD, UPDATE_THROTTLE_POINT);
newConf.setInt(HFileCleaner.LARGE_HFILE_DELETE_QUEUE_SIZE, UPDATE_QUEUE_SIZE); newConf.setInt(HFileCleaner.LARGE_HFILE_QUEUE_INIT_SIZE, UPDATE_QUEUE_INIT_SIZE);
newConf.setInt(HFileCleaner.SMALL_HFILE_DELETE_QUEUE_SIZE, UPDATE_QUEUE_SIZE); newConf.setInt(HFileCleaner.SMALL_HFILE_QUEUE_INIT_SIZE, UPDATE_QUEUE_INIT_SIZE);
cleaner.onConfigurationChange(newConf);
LOG.debug("File deleted from large queue: " + cleaner.getNumOfDeletedLargeFiles() LOG.debug("File deleted from large queue: " + cleaner.getNumOfDeletedLargeFiles()
+ "; from small queue: " + cleaner.getNumOfDeletedSmallFiles()); + "; from small queue: " + cleaner.getNumOfDeletedSmallFiles());
cleaner.onConfigurationChange(newConf);
// check values after change // check values after change
Assert.assertEquals(UPDATE_THROTTLE_POINT, cleaner.getThrottlePoint()); Assert.assertEquals(UPDATE_THROTTLE_POINT, cleaner.getThrottlePoint());
Assert.assertEquals(UPDATE_QUEUE_SIZE, cleaner.getLargeQueueSize()); Assert.assertEquals(UPDATE_QUEUE_INIT_SIZE, cleaner.getLargeQueueInitSize());
Assert.assertEquals(UPDATE_QUEUE_SIZE, cleaner.getSmallQueueSize()); Assert.assertEquals(UPDATE_QUEUE_INIT_SIZE, cleaner.getSmallQueueInitSize());
Assert.assertEquals(2, cleaner.getCleanerThreads().size()); Assert.assertEquals(2, cleaner.getCleanerThreads().size());
// wait until clean done and check // wait until clean done and check

View File

@ -38,7 +38,7 @@ import static org.junit.Assert.*;
public class TestStealJobQueue { public class TestStealJobQueue {
StealJobQueue<Integer> stealJobQueue; StealJobQueue<Integer> stealJobQueue;
BlockingQueue stealFromQueue; BlockingQueue<Integer> stealFromQueue;
@Before @Before
public void setup() { public void setup() {