Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@

import static org.apache.geaflow.cluster.constants.ClusterConstants.EXIT_CODE;

import com.google.common.annotations.VisibleForTesting;
import java.util.Objects;
import java.util.function.IntConsumer;
import org.apache.geaflow.cluster.task.runner.AbstractTaskRunner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -30,6 +33,16 @@ public class ComponentExceptionSupervisor extends AbstractTaskRunner<ComponentEx
private static final Logger LOGGER = LoggerFactory.getLogger(ComponentExceptionSupervisor.class);

private static ComponentExceptionSupervisor INSTANCE;
private final IntConsumer processExit;

public ComponentExceptionSupervisor() {
this(System::exit);
}

@VisibleForTesting
protected ComponentExceptionSupervisor(IntConsumer processExit) {
this.processExit = Objects.requireNonNull(processExit);
}

@Override
protected void process(ExceptionElement exceptionElement) {
Expand All @@ -42,7 +55,7 @@ protected void process(ExceptionElement exceptionElement) {
if (running) {
LOGGER.error(String.format("%s occur fatal exception, exit process now",
exceptionElement.thread), exceptionElement.cause);
System.exit(EXIT_CODE);
processExit.accept(EXIT_CODE);
} else {
LOGGER.info("{} ignore exception because supervisor is shutdown", exceptionElement.thread);
}
Expand All @@ -55,6 +68,11 @@ public static synchronized ComponentExceptionSupervisor getInstance() {
return INSTANCE;
}

@VisibleForTesting
protected static synchronized void setInstance(ComponentExceptionSupervisor instance) {
INSTANCE = Objects.requireNonNull(instance);
}

@Override
public void shutdown() {
super.shutdown();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,47 +23,38 @@
import static org.apache.geaflow.common.config.keys.ExecutionConfigKeys.CONTAINER_DISPATCH_THREADS;
import static org.apache.geaflow.common.config.keys.ExecutionConfigKeys.REPORTER_LIST;
import static org.apache.geaflow.common.config.keys.ExecutionConfigKeys.RUN_LOCAL_MODE;
import static org.apache.geaflow.cluster.constants.ClusterConstants.EXIT_CODE;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.IntConsumer;
import org.apache.geaflow.cluster.exception.ComponentExceptionSupervisor;
import org.apache.geaflow.cluster.exception.ExceptionCollectService;
import org.apache.geaflow.cluster.protocol.EventType;
import org.apache.geaflow.cluster.protocol.ICommand;
import org.apache.geaflow.cluster.protocol.IExecutableCommand;
import org.apache.geaflow.cluster.protocol.OpenContainerEvent;
import org.apache.geaflow.cluster.task.ITaskContext;
import org.apache.geaflow.cluster.util.SystemExitSignalCatcher;
import org.apache.geaflow.common.config.Configuration;
import org.apache.geaflow.common.utils.ReflectionUtil;
import org.apache.geaflow.common.utils.SleepUtils;
import org.apache.geaflow.ha.service.HAServiceFactory;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

public class ContainerTest {

private static final int EXIT_NOT_CALLED = Integer.MIN_VALUE;
private static AtomicBoolean eventExecuted = new AtomicBoolean(false);
private static AtomicBoolean hasException = new AtomicBoolean(false);
private static SecurityManager securityManager;

@BeforeClass
public void before() {
securityManager = System.getSecurityManager();
System.setSecurityManager(new SystemExitSignalCatcher(hasException));
}

@AfterClass
public void after() {
System.setSecurityManager(securityManager);
}
private static AtomicInteger exitCode = new AtomicInteger(EXIT_NOT_CALLED);

@BeforeMethod
public void beforeMethod() {
hasException.set(false);
exitCode.set(EXIT_NOT_CALLED);
TestComponentExceptionSupervisor.install(exitCode::set);
}

@Test
Expand All @@ -83,12 +74,15 @@ public void testProcessEventHandleException() throws Exception {
ReflectionUtil.setField(container, "containerContext", new ContainerContext(0, configuration));
ReflectionUtil.setField(container, "exceptionCollectService", new ExceptionCollectService());
container.open(new OpenContainerEvent(1));
container.process(new TestCreateTaskEvent());
container.process(new ExceptionCommandEvent());

waitTestResult();
Assert.assertTrue(hasException.get());
container.close();
try {
container.process(new TestCreateTaskEvent());
container.process(new ExceptionCommandEvent());

waitTestResult();
Assert.assertEquals(exitCode.get(), EXIT_CODE);
} finally {
container.close();
}
}

@Test
Expand All @@ -108,12 +102,15 @@ public void testProcessMultiEventHandleException() throws Exception {
ReflectionUtil.setField(container, "containerContext", new ContainerContext(0, configuration));
ReflectionUtil.setField(container, "exceptionCollectService", new ExceptionCollectService());
container.open(new OpenContainerEvent(1));
container.process(new TestCreateTaskEvent());
container.process(new ExceptionCommandEvent());

waitTestResult();
Assert.assertTrue(hasException.get());
container.close();
try {
container.process(new TestCreateTaskEvent());
container.process(new ExceptionCommandEvent());

waitTestResult();
Assert.assertEquals(exitCode.get(), EXIT_CODE);
} finally {
container.close();
}
}

private void waitTestResult() {
Expand All @@ -123,7 +120,7 @@ private void waitTestResult() {
retry--;
}
retry = 10;
while (!hasException.get() && retry > 0) {
while (exitCode.get() == EXIT_NOT_CALLED && retry > 0) {
SleepUtils.sleepMilliSecond(100);
retry--;
}
Expand Down Expand Up @@ -165,4 +162,15 @@ public EventType getEventType() {
return EventType.CREATE_TASK;
}
}

private static class TestComponentExceptionSupervisor extends ComponentExceptionSupervisor {

private TestComponentExceptionSupervisor(IntConsumer processExit) {
super(processExit);
}

private static void install(IntConsumer processExit) {
setInstance(new TestComponentExceptionSupervisor(processExit));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,52 +19,44 @@

package org.apache.geaflow.cluster.exception;

import static org.apache.geaflow.cluster.constants.ClusterConstants.EXIT_CODE;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.geaflow.cluster.util.SystemExitSignalCatcher;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.geaflow.common.utils.ThreadUtil;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

public class ComponentUncaughtExceptionHandlerTest {

private static SecurityManager securityManager;
private static AtomicBoolean hasException = new AtomicBoolean(false);


@BeforeClass
public void before() {
securityManager = System.getSecurityManager();
System.setSecurityManager(new SystemExitSignalCatcher(hasException));
}

@AfterClass
public void after() {
System.setSecurityManager(securityManager);
}

@BeforeMethod
public void beforeMethod() {
hasException.set(false);
}

@Test
public void testHandleExceptionInThreadPool() throws InterruptedException {

ComponentExceptionSupervisor.getInstance();
AtomicInteger exitCode = new AtomicInteger();
CountDownLatch exitCalled = new CountDownLatch(1);
ComponentExceptionSupervisor supervisor = new ComponentExceptionSupervisor(code -> {
exitCode.set(code);
exitCalled.countDown();
});
ComponentExceptionSupervisor.setInstance(supervisor);
ExecutorService executorService = Executors.newFixedThreadPool(2,
ThreadUtil.namedThreadFactory(true, "test-handler", new ComponentUncaughtExceptionHandler()));

executorService.execute(() -> {
throw new RuntimeException("test exception");
});
executorService.execute(ComponentExceptionSupervisor.getInstance());
// wait async thread catch and handle exception
Thread.sleep(100);
Assert.assertTrue(hasException.get());
try {
executorService.execute(() -> {
throw new RuntimeException("test exception");
});
executorService.execute(supervisor);

Assert.assertTrue(exitCalled.await(1, TimeUnit.SECONDS));
Assert.assertEquals(exitCode.get(), EXIT_CODE);
} finally {
supervisor.shutdown();
executorService.shutdown();
executorService.awaitTermination(1, TimeUnit.SECONDS);
}
}
}

This file was deleted.

Loading