/*
* 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.accumulo.server.tabletserver;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.accumulo.core.Constants;
import org.apache.accumulo.core.conf.AccumuloConfiguration;
import org.apache.accumulo.core.conf.Property;
import org.apache.accumulo.core.data.KeyExtent;
import org.apache.accumulo.core.file.blockfile.cache.LruBlockCache;
import org.apache.accumulo.core.util.Daemon;
import org.apache.accumulo.core.util.LoggingRunnable;
import org.apache.accumulo.core.util.UtilWaitThread;
import org.apache.accumulo.server.tabletserver.FileManager.ScanFileManager;
import org.apache.accumulo.server.util.NamingThreadFactory;
import org.apache.accumulo.server.util.time.RelativeTime;
import org.apache.accumulo.start.classloader.AccumuloClassLoader;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.log4j.Logger;
import cloudtrace.instrument.TraceExecutorService;
/**
* ResourceManager is responsible for managing the resources of all tablets within a tablet server.
*
*
*
*/
public class TabletServerResourceManager {
private ExecutorService minorCompactionThreadPool;
private ExecutorService majorCompactionThreadPool;
private ExecutorService rootMajorCompactionThreadPool;
private ExecutorService defaultMajorCompactionThreadPool;
private ExecutorService splitThreadPool;
private ExecutorService defaultSplitThreadPool;
private ExecutorService defaultMigrationPool;
private ExecutorService migrationPool;
private ExecutorService assignmentPool;
private ExecutorService assignMetaDataPool;
private ExecutorService readAheadThreadPool;
private ExecutorService defaultReadAheadThreadPool;
private Map<String,ExecutorService> threadPools = new TreeMap<String,ExecutorService>();
private AccumuloConfiguration acuConf;
private HashSet<TabletResourceManager> tabletResources;
private FileManager fileManager;
private MemoryManager memoryManger;
private MemoryManagementFramework memMgmt;
private FileSystem fs;
private LruBlockCache _dCache = null;
private LruBlockCache _iCache = null;
private static final Logger log = Logger.getLogger(TabletServerResourceManager.class);
private ExecutorService addEs(String name, ExecutorService tp) {
if (threadPools.containsKey(name)) {
throw new IllegalArgumentException("Cannot create two executor services with same name " + name);
}
tp = new TraceExecutorService(tp);
threadPools.put(name, tp);
return tp;
}
private ExecutorService createEs(int max, String name) {
return addEs(name, Executors.newFixedThreadPool(max, new NamingThreadFactory(name)));
}
private ExecutorService createEs(int max, String name, BlockingQueue<Runnable> queue) {
ThreadPoolExecutor tp = new ThreadPoolExecutor(max, max, 0L, TimeUnit.MILLISECONDS, queue, new NamingThreadFactory(name));
return addEs(name, tp);
}
private ExecutorService createEs(int min, int max, int timeout, String name) {
return addEs(name, new ThreadPoolExecutor(min, max, timeout, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new NamingThreadFactory(name)));
}
public TabletServerResourceManager(Configuration conf, FileSystem fs) {
this.fs = fs;
this.acuConf = AccumuloConfiguration.getSystemConfiguration();
long maxMemory = acuConf.getMemoryInBytes(Property.TSERV_MAXMEM);
boolean usingNativeMap = AccumuloConfiguration.getSystemConfiguration().getBoolean(Property.TSERV_NATIVEMAP_ENABLED) && NativeMap.loadedNativeLibraries();
long blockSize = acuConf.getMemoryInBytes(Property.TSERV_DEFAULT_BLOCKSIZE);
long dCacheSize = acuConf.getMemoryInBytes(Property.TSERV_DATACACHE_SIZE);
long iCacheSize = acuConf.getMemoryInBytes(Property.TSERV_INDEXCACHE_SIZE);
_iCache = new LruBlockCache(iCacheSize, blockSize);
_dCache = new LruBlockCache(dCacheSize, blockSize);
Runtime runtime = Runtime.getRuntime();
if (!usingNativeMap && maxMemory > runtime.maxMemory()) {
throw new IllegalArgumentException(String.format("Maximum tablet server map memory %,d is too large for this JVM configuration %,d", maxMemory,
runtime.maxMemory()));
}
runtime.gc();
if (!usingNativeMap && maxMemory > runtime.freeMemory()) {
log.warn("In-memory map may not fit into local memory space.");
}
minorCompactionThreadPool = createEs(acuConf.getCount(Property.TSERV_MINC_MAXCONCURRENT), "minor compactor");
// make this thread pool have a priority queue... and execute tablets with the most
// files first!
majorCompactionThreadPool = createEs(acuConf.getCount(Property.TSERV_MAJC_MAXCONCURRENT), "major compactor", new CompactionQueue());
rootMajorCompactionThreadPool = createEs(0, 1, 300, "md root major compactor");
defaultMajorCompactionThreadPool = createEs(0, 1, 300, "md major compactor");
splitThreadPool = createEs(1, "splitter");
defaultSplitThreadPool = createEs(0, 1, 60, "md splitter");
defaultMigrationPool = createEs(0, 1, 60, "metadata tablet migration");
migrationPool = createEs(acuConf.getCount(Property.TSERV_MIGRATE_MAXCONCURRENT), "tablet migration");
// not sure if concurrent assignments can run safely... even if they could there is probably no benefit at startup because
// individual tablet servers are already running assignments concurrently... having each individual tablet server run
// concurrent assignments would put more load on the metadata table at startup
assignmentPool = createEs(1, "tablet assignment");
assignMetaDataPool = createEs(0, 1, 60, "metadata tablet assignment");
readAheadThreadPool = createEs(acuConf.getCount(Property.TSERV_READ_AHEAD_MAXCONCURRENT), "tablet read ahead");
defaultReadAheadThreadPool = createEs(acuConf.getCount(Property.TSERV_METADATA_READ_AHEAD_MAXCONCURRENT), "metadata tablets read ahead");
tabletResources = new HashSet<TabletResourceManager>();
int maxOpenFiles = acuConf.getCount(Property.TSERV_MAX_OPENFILES) - acuConf.getCount(Property.TSERV_MAJC_MAXOPEN);
fileManager = new FileManager(conf, fs, maxOpenFiles, _dCache, _iCache);
try {
Class<? extends MemoryManager> clazz = AccumuloClassLoader.loadClass(AccumuloConfiguration.getSystemConfiguration().get(Property.TSERV_MEM_MGMT),
MemoryManager.class);
memoryManger = clazz.newInstance();
log.debug("Loaded memory manager : " + memoryManger.getClass().getName());
} catch (Exception e) {
log.error("Failed to find memory manger in config, using default", e);
}
if (memoryManger == null) {
memoryManger = new LargestFirstMemoryManager();
}
memMgmt = new MemoryManagementFramework();
}
private static class TabletStateImpl implements TabletState, Cloneable {
private long lct;
private Tablet tablet;
private long mts;
private long mcmts;
public TabletStateImpl(Tablet t, long mts, long lct, long mcmts) {
this.tablet = t;
this.mts = mts;
this.lct = lct;
this.mcmts = mcmts;
}
public KeyExtent getExtent() {
return tablet.getExtent();
}
Tablet getTablet() {
return tablet;
}
public long getLastCommitTime() {
return lct;
}
public long getMemTableSize() {
return mts;
}
public long getMinorCompactingMemTableSize() {
return mcmts;
}
}
private class MemoryManagementFramework {
private Map<KeyExtent,TabletStateImpl> tabletReports;
private LinkedBlockingQueue<TabletStateImpl> memUsageReports;
private long lastMemCheckTime = System.currentTimeMillis();
private long maxMem;
MemoryManagementFramework() {
tabletReports = Collections.synchronizedMap(new HashMap<KeyExtent,TabletStateImpl>());
memUsageReports = new LinkedBlockingQueue<TabletStateImpl>();
maxMem = AccumuloConfiguration.getSystemConfiguration().getMemoryInBytes(Property.TSERV_MAXMEM);
Runnable r1 = new Runnable() {
public void run() {
processTabletMemStats();
}
};
Thread t1 = new Daemon(new LoggingRunnable(log, r1));
t1.setPriority(Thread.NORM_PRIORITY + 1);
t1.setName("Accumulo Memory Guard");
t1.start();
Runnable r2 = new Runnable() {
public void run() {
manageMemory();
}
};
Thread t2 = new Daemon(new LoggingRunnable(log, r2));
t2.setName("Accumulo Minor Compaction Initiator");
t2.start();
}
private long lastMemTotal = 0;
private void processTabletMemStats() {
while (true) {
try {
TabletStateImpl report = memUsageReports.take();
while (report != null) {
tabletReports.put(report.getExtent(), report);
report = memUsageReports.poll();
}
long delta = System.currentTimeMillis() - lastMemCheckTime;
if (holdCommits || delta > 50 || lastMemTotal > 0.90 * maxMem) {
lastMemCheckTime = System.currentTimeMillis();
long totalMemUsed = 0;
synchronized (tabletReports) {
for (TabletStateImpl tsi : tabletReports.values()) {
totalMemUsed += tsi.getMemTableSize();
totalMemUsed += tsi.getMinorCompactingMemTableSize();
}
}
if (totalMemUsed > 0.95 * maxMem) {
holdAllCommits(true);
} else {
holdAllCommits(false);
}
lastMemTotal = totalMemUsed;
}
} catch (InterruptedException e) {
log.warn(e, e);
}
}
}
private void manageMemory() {
while (true) {
MemoryManagementActions mma = null;
try {
ArrayList<TabletState> tablets;
synchronized (tabletReports) {
tablets = new ArrayList<TabletState>(tabletReports.values());
}
mma = memoryManger.getMemoryManagementActions(tablets);
} catch (Throwable t) {
log.error("Memory manager failed " + t.getMessage(), t);
}
try {
if (mma != null && mma.tabletsToMinorCompact != null && mma.tabletsToMinorCompact.size() > 0) {
for (KeyExtent keyExtent : mma.tabletsToMinorCompact) {
TabletStateImpl tabletReport = tabletReports.get(keyExtent);
if (tabletReport == null) {
log.warn("Memory manager asked to compact nonexistant tablet " + keyExtent);
continue;
}
if (!tabletReport.getTablet().initiateMinorCompaction()) {
if (tabletReport.getTablet().isClosed()) {
tabletReports.remove(tabletReport.getExtent());
log.debug("Ignoring memory manager recommendation: not minor compacting closed tablet " + keyExtent);
} else {
log.info("Ignoring memory manager recommendation: not minor compacting " + keyExtent);
}
}
}
// log.debug("mma.tabletsToMinorCompact = "+mma.tabletsToMinorCompact);
}
} catch (Throwable t) {
log.error("Minor compactions for memory managment failed", t);
}
UtilWaitThread.sleep(250);
}
}
public void updateMemoryUsageStats(Tablet tablet, long size, long lastCommitTime, long mincSize) {
memUsageReports.add(new TabletStateImpl(tablet, size, lastCommitTime, mincSize));
}
public void tabletClosed(KeyExtent extent) {
tabletReports.remove(extent);
}
}
private Object commitHold = new String("");
private volatile boolean holdCommits = false;
private long holdStartTime;
protected void holdAllCommits(boolean holdAllCommits) {
synchronized (commitHold) {
if (holdCommits != holdAllCommits) {
holdCommits = holdAllCommits;
if (holdCommits) {
holdStartTime = System.currentTimeMillis();
}
if (!holdCommits) {
log.debug(String.format("Commits held for %6.2f secs", (System.currentTimeMillis() - holdStartTime) / 1000.0));
commitHold.notifyAll();
}
}
}
}
void waitUntilCommitsAreEnabled() {
if (holdCommits) {
long timeout = System.currentTimeMillis() + AccumuloConfiguration.getSystemConfiguration().getTimeInMillis(Property.GENERAL_RPC_TIMEOUT);
synchronized (commitHold) {
while (holdCommits) {
try {
if (System.currentTimeMillis() > timeout)
throw new HoldTimeoutException("Commits are held");
commitHold.wait(1000);
} catch (InterruptedException e) {}
}
}
}
}
public long holdTime() {
if (!holdCommits)
return 0;
synchronized (commitHold) {
return System.currentTimeMillis() - holdStartTime;
}
}
public void close() {
for (ExecutorService executorService : threadPools.values()) {
executorService.shutdown();
}
for (Entry<String,ExecutorService> entry : threadPools.entrySet()) {
while (true) {
try {
if (entry.getValue().awaitTermination(60, TimeUnit.SECONDS))
break;
log.info("Waiting for thread pool " + entry.getKey() + " to shutdown");
} catch (InterruptedException e) {
log.warn(e);
}
}
}
}
public synchronized TabletResourceManager createTabletResourceManager() {
TabletResourceManager trm = new TabletResourceManager();
return trm;
}
synchronized private void addTabletResource(TabletResourceManager tr) {
tabletResources.add(tr);
}
synchronized private void removeTabletResource(TabletResourceManager tr) {
tabletResources.remove(tr);
}
private class MapFileInfo {
private long timestamp = 0;
private final String path;
private final long size;
MapFileInfo(String path, long size) {
this.path = path;
this.size = size;
}
long getModificationTime() {
if (timestamp == 0) {
try {
timestamp = fs.getFileStatus(new Path(path)).getModificationTime();
} catch (IOException e) {
log.warn("Failed to get modification time for " + path, e);
}
}
return timestamp;
}
}
public class TabletResourceManager {
private Map<String,MapFileInfo> mapFiles;
private final long creationTime = System.currentTimeMillis();
private volatile boolean openFilesReserved = false;
private volatile boolean closed = false;
private Tablet tablet;
private AccumuloConfiguration tableConf;
TabletResourceManager() {
mapFiles = Collections.synchronizedSortedMap(new TreeMap<String,MapFileInfo>());
}
void setTablet(Tablet tablet, AccumuloConfiguration tableConf) {
this.tablet = tablet;
this.tableConf = tableConf;
// TabletResourceManager is not really initialized until this
// function is called.... so do not make it publicly available
// until now
addTabletResource(this);
}
// BEGIN methods that Tablets call to manage their set of open map files
synchronized void importMapFile(String name, long size) throws IOException {
addMapFile(name, size);
// update this timestamp so the tablet is not idle compacted if
// files were recently imported
lastReportedCommitTime = System.currentTimeMillis();
}
synchronized void addMapFile(String name, long size) throws IOException {
if (closed)
throw new IOException("closed");
if (openFilesReserved)
throw new IOException("tried to add map file while open files reserved");
if (mapFiles.keySet().contains(name)) {
log.error("Adding map files that is already in set " + name);
}
mapFiles.put(name, new MapFileInfo(name, size));
}
synchronized void removeMapFiles(Set<String> filesCompacted) throws IOException {
if (closed)
throw new IOException("closed");
if (openFilesReserved)
throw new IOException("tried to remove map files while open files reserved");
for (String file : filesCompacted) {
if (!mapFiles.keySet().contains(file)) {
log.warn("Requested removal of file not in set " + file);
}
}
mapFiles.keySet().removeAll(filesCompacted);
}
synchronized SortedSet<String> getCopyOfMapFilePaths() throws IOException {
if (closed)
throw new IOException("closed");
return new TreeSet<String>(mapFiles.keySet());
}
synchronized boolean containsAllMapFiles(TreeSet<String> filesToCompact) throws IOException {
if (closed)
throw new IOException("closed");
return mapFiles.keySet().containsAll(filesToCompact);
}
synchronized ScanFileManager newScanFileManager() {
if (closed)
throw new IllegalStateException("closed");
return fileManager.newScanFileManager(tablet.getExtent());
}
// END methods that Tablets call to manage their set of open map files
// BEGIN methods that Tablets call to manage memory
private AtomicLong lastReportedSize = new AtomicLong();
private AtomicLong lastReportedMincSize = new AtomicLong();
private volatile long lastReportedCommitTime = 0;
public void updateMemoryUsageStats(long size, long mincSize) {
// do not want to update stats for every little change,
// so only do it under certain circumstances... the reason
// for this is that reporting stats acquires a lock, do
// not want all tablets locking on the same lock for every
// commit
long totalSize = size + mincSize;
long lrs = lastReportedSize.get();
long delta = totalSize - lrs;
long lrms = lastReportedMincSize.get();
boolean report = false;
// the atomic longs are considered independently, when one is set
// the other is not set intentionally because this method is not
// synchronized... therefore there are not transactional semantics
// for reading and writing two variables
if ((lrms > 0 && mincSize == 0 || lrms == 0 && mincSize > 0) && lastReportedMincSize.compareAndSet(lrms, mincSize)) {
report = true;
}
long currentTime = System.currentTimeMillis();
if ((delta > 32000 || delta < 0 || (currentTime - lastReportedCommitTime > 1000)) && lastReportedSize.compareAndSet(lrs, totalSize)) {
if (delta > 0)
lastReportedCommitTime = currentTime;
report = true;
}
if (report)
memMgmt.updateMemoryUsageStats(tablet, size, lastReportedCommitTime, mincSize);
}
// END methods that Tablets call to manage memory
// BEGIN methods that Tablets call to make decisions about major compaction
// when too many files are open, we may want tablets to compact down
// to one map file
private final SimpleDateFormat dateParser = new SimpleDateFormat("yyyyMMddHHmmssz");
private boolean needToCompactAll() {
String olderThan = tableConf.get(Property.TABLE_MAJC_COMPACTALL_AT);
long cutOff;
try {
synchronized (dateParser) {
cutOff = dateParser.parse(olderThan).getTime();
}
} catch (ParseException e) {
log.warn("Failed to parse date " + olderThan + " for " + tablet.getExtent().getTableId(), e);
return false;
}
if (cutOff == 0) {
// time is not set
return false;
}
if (cutOff > RelativeTime.currentTimeMillis()) {
// its in the future ignore it
return false;
}
synchronized (mapFiles) {
Set<Entry<String,MapFileInfo>> es = mapFiles.entrySet();
for (Entry<String,MapFileInfo> entry : es) {
// had considered using the time stamp in the metadata table
// associated with each map file entry, however this gets reset
// when splits occur... so went to HDFS to get a map files age
// and cached the result from HDFS
if (entry.getValue().getModificationTime() < cutOff) {
return true;
}
}
}
return false;
}
synchronized Map<String,Long> findMapFilesToCompact() {
if (needToCompactAll()) {
Map<String,Long> files = new HashMap<String,Long>();
Set<Entry<String,MapFileInfo>> es = mapFiles.entrySet();
for (Entry<String,MapFileInfo> entry : es) {
files.put(entry.getKey(), entry.getValue().size);
}
return files;
}
if (mapFiles.size() <= 1)
return null;
TreeSet<MapFileInfo> candidateFiles = new TreeSet<MapFileInfo>(new Comparator<MapFileInfo>() {
@Override
public int compare(MapFileInfo o1, MapFileInfo o2) {
if (o1 == o2)
return 0;
if (o1.size < o2.size)
return -1;
if (o1.size > o2.size)
return 1;
return o1.path.compareTo(o2.path);
}
});
double ratio = tableConf.getFraction(Property.TABLE_MAJC_RATIO);
int maxFilesToCompact = tableConf.getCount(Property.TSERV_MAJC_MAXOPEN) / tableConf.getCount(Property.TSERV_MAJC_MAXCONCURRENT);
synchronized (this) {
candidateFiles.addAll(mapFiles.values());
}
long totalSize = 0;
for (MapFileInfo mfi : candidateFiles) {
totalSize += mfi.size;
}
while (candidateFiles.size() > 1) {
MapFileInfo max = candidateFiles.last();
if (max.size * ratio <= totalSize) {
Map<String,Long> files = new HashMap<String,Long>();
for (MapFileInfo mfi : candidateFiles)
files.put(mfi.path, mfi.size);
if (files.size() > maxFilesToCompact) {
int numToCompact = maxFilesToCompact;
// take the smallest files
ArrayList<Entry<String,Long>> entries = new ArrayList<Map.Entry<String,Long>>();
entries.addAll(files.entrySet());
Collections.sort(entries, new Comparator<Entry<String,Long>>() {
@Override
public int compare(Entry<String,Long> e1, Entry<String,Long> e2) {
int cmp = e1.getValue().compareTo(e2.getValue());
if (cmp == 0)
cmp = e1.getKey().compareTo(e2.getKey());
return cmp;
}
});
files = new HashMap<String,Long>();
for (int i = 0; i < numToCompact; i++)
files.put(entries.get(i).getKey(), entries.get(i).getValue());
}
return files;
}
totalSize -= max.size;
candidateFiles.remove(max);
}
return null;
}
synchronized boolean needsMajorCompaction(boolean idleCompaction) {
if (closed)
return false;// throw new IOException("closed");
// int threshold;
if (idleCompaction) {
// threshold = 1;
long idleTime;
if (lastReportedCommitTime == 0) {
// no commits, so compute how long the tablet has been assigned to the
// tablet server
idleTime = System.currentTimeMillis() - creationTime;
} else {
idleTime = System.currentTimeMillis() - lastReportedCommitTime;
}
if (idleTime < tableConf.getTimeInMillis(Property.TABLE_MAJC_COMPACTALL_IDLETIME)) {
return false;
}
}/*
* else{ threshold = tableConf.getCount(Property.TABLE_MAJC_THRESHOLD); }
*/
return (findMapFilesToCompact() != null) || needToCompactAll();
}
// END methods that Tablets call to make decisions about major compaction
// tablets call this method to run minor compactions,
// this allows us to control how many minor compactions
// run concurrently in a tablet server
void executeMinorCompaction(final Runnable r) {
minorCompactionThreadPool.execute(new LoggingRunnable(log, r));
}
void close() throws IOException {
// always obtain locks in same order to avoid deadlock
synchronized (TabletServerResourceManager.this) {
synchronized (this) {
if (closed)
throw new IOException("closed");
if (openFilesReserved)
throw new IOException("tired to close files while open files reserved");
mapFiles.clear();
TabletServerResourceManager.this.removeTabletResource(this);
memMgmt.tabletClosed(tablet.getExtent());
memoryManger.tabletClosed(tablet.getExtent());
closed = true;
}
}
}
public TabletServerResourceManager getTabletServerResourceManager() {
return TabletServerResourceManager.this;
}
public void executeMajorCompaction(KeyExtent tablet, Runnable compactionTask) {
TabletServerResourceManager.this.executeMajorCompaction(tablet, compactionTask);
}
}
public void executeSplit(KeyExtent tablet, Runnable splitTask) {
if (tablet.getTableId().toString().equals(Constants.METADATA_TABLE_ID)) {
if (tablet.equals(Constants.ROOT_TABLET_EXTENT)) {
log.warn("Saw request to split root tablet, ignoring");
return;
}
defaultSplitThreadPool.execute(splitTask);
} else {
splitThreadPool.execute(splitTask);
}
}
public void executeMajorCompaction(KeyExtent tablet, Runnable compactionTask) {
if (tablet.equals(Constants.ROOT_TABLET_EXTENT)) {
rootMajorCompactionThreadPool.execute(compactionTask);
} else if (tablet.getTableId().toString().equals(Constants.METADATA_TABLE_ID)) {
defaultMajorCompactionThreadPool.execute(compactionTask);
} else {
majorCompactionThreadPool.execute(compactionTask);
}
}
public void executeReadAhead(KeyExtent tablet, Runnable task) {
if (tablet.equals(Constants.ROOT_TABLET_EXTENT)) {
task.run();
} else if (tablet.getTableId().toString().equals(Constants.METADATA_TABLE_ID)) {
defaultReadAheadThreadPool.execute(task);
} else {
readAheadThreadPool.execute(task);
}
}
public void addAssignment(Runnable assignmentHandler) {
assignmentPool.execute(assignmentHandler);
}
public void addMetaDataAssignment(Runnable assignmentHandler) {
assignMetaDataPool.execute(assignmentHandler);
}
public void addMigration(KeyExtent tablet, Runnable migrationHandler) {
if (tablet.equals(Constants.ROOT_TABLET_EXTENT)) {
migrationHandler.run();
} else if (tablet.getTableId().toString().equals(Constants.METADATA_TABLE_ID)) {
defaultMigrationPool.execute(migrationHandler);
} else {
migrationPool.execute(migrationHandler);
}
}
public void stopSplits() {
splitThreadPool.shutdown();
defaultSplitThreadPool.shutdown();
while (true) {
try {
while (!splitThreadPool.awaitTermination(1, TimeUnit.MINUTES)) {
log.info("Waiting for metadata split thread pool to stop");
}
while (!defaultSplitThreadPool.awaitTermination(1, TimeUnit.MINUTES)) {
log.info("Waiting for split thread pool to stop");
}
break;
} catch (InterruptedException ex) {
log.info(ex, ex);
}
}
}
public void stopNormalAssignments() {
assignmentPool.shutdown();
while (true) {
try {
while (!assignmentPool.awaitTermination(1, TimeUnit.MINUTES)) {
log.info("Waiting for assignment thread pool to stop");
}
break;
} catch (InterruptedException ex) {
log.info(ex, ex);
}
}
}
public void stopMetadataAssignments() {
assignMetaDataPool.shutdown();
while (true) {
try {
while (!assignMetaDataPool.awaitTermination(1, TimeUnit.MINUTES)) {
log.info("Waiting for metadata assignment thread pool to stop");
}
break;
} catch (InterruptedException ex) {
log.info(ex, ex);
}
}
}
}