HADOOP-16341. ShutDownHookManager: Regressed performance on Hook removals after HADOOP-15679

Contributed by Gopal V and Atilla Magyar.

Change-Id: I066d5eece332a1673594de0f9b484443f95530ec
This commit is contained in:
Gopal V 2019-07-17 13:49:20 +01:00 committed by Steve Loughran
parent 19a001826f
commit b4466a3b0a
No known key found for this signature in database
GPG Key ID: D22CF846DBB162A0
2 changed files with 32 additions and 18 deletions

View File

@ -92,7 +92,7 @@ public void run() {
return; return;
} }
long started = System.currentTimeMillis(); long started = System.currentTimeMillis();
int timeoutCount = executeShutdown(); int timeoutCount = MGR.executeShutdown();
long ended = System.currentTimeMillis(); long ended = System.currentTimeMillis();
LOG.debug(String.format( LOG.debug(String.format(
"Completed shutdown in %.3f seconds; Timeouts: %d", "Completed shutdown in %.3f seconds; Timeouts: %d",
@ -116,9 +116,9 @@ public void run() {
*/ */
@InterfaceAudience.Private @InterfaceAudience.Private
@VisibleForTesting @VisibleForTesting
static int executeShutdown() { int executeShutdown() {
int timeouts = 0; int timeouts = 0;
for (HookEntry entry: MGR.getShutdownHooksInOrder()) { for (HookEntry entry: getShutdownHooksInOrder()) {
Future<?> future = EXECUTOR.submit(entry.getHook()); Future<?> future = EXECUTOR.submit(entry.getHook());
try { try {
future.get(entry.getTimeout(), entry.getTimeUnit()); future.get(entry.getTimeout(), entry.getTimeUnit());
@ -254,7 +254,9 @@ TimeUnit getTimeUnit() {
private AtomicBoolean shutdownInProgress = new AtomicBoolean(false); private AtomicBoolean shutdownInProgress = new AtomicBoolean(false);
//private to constructor to ensure singularity //private to constructor to ensure singularity
private ShutdownHookManager() { @VisibleForTesting
@InterfaceAudience.Private
ShutdownHookManager() {
} }
/** /**
@ -267,8 +269,8 @@ private ShutdownHookManager() {
@VisibleForTesting @VisibleForTesting
List<HookEntry> getShutdownHooksInOrder() { List<HookEntry> getShutdownHooksInOrder() {
List<HookEntry> list; List<HookEntry> list;
synchronized (MGR.hooks) { synchronized (hooks) {
list = new ArrayList<>(MGR.hooks); list = new ArrayList<>(hooks);
} }
Collections.sort(list, new Comparator<HookEntry>() { Collections.sort(list, new Comparator<HookEntry>() {
@ -342,7 +344,9 @@ public boolean removeShutdownHook(Runnable shutdownHook) {
throw new IllegalStateException("Shutdown in progress, cannot remove a " + throw new IllegalStateException("Shutdown in progress, cannot remove a " +
"shutdownHook"); "shutdownHook");
} }
return hooks.remove(new HookEntry(shutdownHook, 0)); // hooks are only == by runnable
return hooks.remove(new HookEntry(shutdownHook, 0, TIMEOUT_MINIMUM,
TIME_UNIT_DEFAULT));
} }
/** /**
@ -354,7 +358,8 @@ public boolean removeShutdownHook(Runnable shutdownHook) {
@InterfaceAudience.Public @InterfaceAudience.Public
@InterfaceStability.Stable @InterfaceStability.Stable
public boolean hasShutdownHook(Runnable shutdownHook) { public boolean hasShutdownHook(Runnable shutdownHook) {
return hooks.contains(new HookEntry(shutdownHook, 0)); return hooks.contains(new HookEntry(shutdownHook, 0, TIMEOUT_MINIMUM,
TIME_UNIT_DEFAULT));
} }
/** /**

View File

@ -21,7 +21,6 @@
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import org.junit.After;
import org.junit.Test; import org.junit.Test;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -43,13 +42,10 @@ public class TestShutdownHookManager {
LoggerFactory.getLogger(TestShutdownHookManager.class.getName()); LoggerFactory.getLogger(TestShutdownHookManager.class.getName());
/** /**
* remove all the shutdown hooks so that they never get invoked later * A new instance of ShutdownHookManager to ensure parallel tests
* on in this test process. * don't have shared context.
*/ */
@After private final ShutdownHookManager mgr = new ShutdownHookManager();
public void clearShutdownHooks() {
ShutdownHookManager.get().clearShutdownHooks();
}
/** /**
* Verify hook registration, then execute the hook callback stage * Verify hook registration, then execute the hook callback stage
@ -58,7 +54,6 @@ public void clearShutdownHooks() {
*/ */
@Test @Test
public void shutdownHookManager() { public void shutdownHookManager() {
ShutdownHookManager mgr = ShutdownHookManager.get();
assertNotNull("No ShutdownHookManager", mgr); assertNotNull("No ShutdownHookManager", mgr);
assertEquals(0, mgr.getShutdownHooksInOrder().size()); assertEquals(0, mgr.getShutdownHooksInOrder().size());
Hook hook1 = new Hook("hook1", 0, false); Hook hook1 = new Hook("hook1", 0, false);
@ -119,7 +114,7 @@ public void shutdownHookManager() {
// now execute the hook shutdown sequence // now execute the hook shutdown sequence
INVOCATION_COUNT.set(0); INVOCATION_COUNT.set(0);
LOG.info("invoking executeShutdown()"); LOG.info("invoking executeShutdown()");
int timeouts = ShutdownHookManager.executeShutdown(); int timeouts = mgr.executeShutdown();
LOG.info("Shutdown completed"); LOG.info("Shutdown completed");
assertEquals("Number of timed out hooks", 1, timeouts); assertEquals("Number of timed out hooks", 1, timeouts);
@ -193,7 +188,6 @@ public void testShutdownTimeoutBadConfiguration() throws Throwable {
*/ */
@Test @Test
public void testDuplicateRegistration() throws Throwable { public void testDuplicateRegistration() throws Throwable {
ShutdownHookManager mgr = ShutdownHookManager.get();
Hook hook = new Hook("hook1", 0, false); Hook hook = new Hook("hook1", 0, false);
// add the hook // add the hook
@ -222,6 +216,21 @@ public void testDuplicateRegistration() throws Throwable {
} }
@Test
public void testShutdownRemove() throws Throwable {
assertNotNull("No ShutdownHookManager", mgr);
assertEquals(0, mgr.getShutdownHooksInOrder().size());
Hook hook1 = new Hook("hook1", 0, false);
Hook hook2 = new Hook("hook2", 0, false);
mgr.addShutdownHook(hook1, 9); // create Hook1 with priority 9
assertTrue("No hook1", mgr.hasShutdownHook(hook1)); // hook1 lookup works
assertEquals(1, mgr.getShutdownHooksInOrder().size()); // 1 hook
assertFalse("Delete hook2 should not be allowed",
mgr.removeShutdownHook(hook2));
assertTrue("Can't delete hook1", mgr.removeShutdownHook(hook1));
assertEquals(0, mgr.getShutdownHooksInOrder().size());
}
private static final AtomicInteger INVOCATION_COUNT = new AtomicInteger(); private static final AtomicInteger INVOCATION_COUNT = new AtomicInteger();
/** /**