mirror of
https://github.com/ebean-orm/ebean.git
synced 2026-10-22 07:15:04 +00:00
Refactor BackgroundExecutor add submit() methods returning Future (#2121)
- Adds submit() methods that return Future - Refactor internals to use DaemonScheduleThreadPool - Delete the now unused DaemonExecutorService - Tidy internals using wrapMDC() methods
This commit is contained in:
@@ -1,31 +1,40 @@
|
||||
package io.ebean;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Background thread pool service for executing of tasks asynchronously.
|
||||
* Background executor service for executing of tasks asynchronously.
|
||||
* <p>
|
||||
* This service is used internally by Ebean for executing background tasks such
|
||||
* as the {@link Query#findFutureList()} and also for executing background tasks
|
||||
* periodically.
|
||||
* </p>
|
||||
* This service can be used to execute tasks in the background.
|
||||
* <p>
|
||||
* This service has been made available so you can use it for your application
|
||||
* code if you want. It can be useful for some server caching implementations
|
||||
* (background population and trimming of the cache etc).
|
||||
* </p>
|
||||
*
|
||||
* @author rbygrave
|
||||
* This service is managed by Ebean and will perform a clean shutdown
|
||||
* waiting for background tasks to complete with a default 30 second
|
||||
* timeout. Shutdown occurs prior to DataSource shutdown.
|
||||
* <p>
|
||||
* This also propagates MDC context from the current thread to the
|
||||
* background task if defined.
|
||||
*/
|
||||
public interface BackgroundExecutor {
|
||||
|
||||
/**
|
||||
* Execute a task in the background.
|
||||
* Execute a callable task in the background returning the Future.
|
||||
*/
|
||||
void execute(Runnable r);
|
||||
<T> Future<T> submit(Callable<T> task);
|
||||
|
||||
/**
|
||||
* Execute a runnable task in the background returning the Future.
|
||||
*/
|
||||
Future<?> submit(Runnable task);
|
||||
|
||||
/**
|
||||
* Execute a task in the background. Effectively the same as
|
||||
* {@link BackgroundExecutor#submit(Runnable)} but returns void.
|
||||
*/
|
||||
void execute(Runnable task);
|
||||
|
||||
/**
|
||||
* Execute a task periodically with a fixed delay between each execution.
|
||||
@@ -36,12 +45,12 @@ public interface BackgroundExecutor {
|
||||
* That is, this method has the same behaviour characteristics as
|
||||
* {@link ScheduledExecutorService#scheduleWithFixedDelay(Runnable, long, long, TimeUnit)}
|
||||
*/
|
||||
void executePeriodically(Runnable r, long delay, TimeUnit unit);
|
||||
void executePeriodically(Runnable task, long delay, TimeUnit unit);
|
||||
|
||||
/**
|
||||
* Execute a task periodically additionally with an initial delay different from delay.
|
||||
*/
|
||||
void executePeriodically(Runnable r, long initialDelay, long delay, TimeUnit unit);
|
||||
void executePeriodically(Runnable task, long initialDelay, long delay, TimeUnit unit);
|
||||
|
||||
/**
|
||||
* Schedules a Runnable for one-shot action that becomes enabled after the given delay.
|
||||
@@ -49,14 +58,13 @@ public interface BackgroundExecutor {
|
||||
* @return a ScheduledFuture representing pending completion of the task and
|
||||
* whose get() method will return null upon completion
|
||||
*/
|
||||
ScheduledFuture<?> schedule(Runnable r, long delay, TimeUnit unit);
|
||||
ScheduledFuture<?> schedule(Runnable task, long delay, TimeUnit unit);
|
||||
|
||||
/**
|
||||
* Schedules a Callable for one-shot action that becomes enabled after the given delay.
|
||||
*
|
||||
* @return a ScheduledFuture that can be used to extract result or cancel
|
||||
*/
|
||||
<V> ScheduledFuture<V> schedule(Callable<V> c, long delay, TimeUnit unit);
|
||||
|
||||
<V> ScheduledFuture<V> schedule(Callable<V> task, long delay, TimeUnit unit);
|
||||
|
||||
}
|
||||
|
||||
+63
-66
@@ -1,12 +1,13 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
import io.ebeaninternal.api.SpiBackgroundExecutor;
|
||||
import io.ebeaninternal.server.lib.DaemonExecutorService;
|
||||
import io.ebeaninternal.server.lib.DaemonScheduleThreadPool;
|
||||
import org.slf4j.MDC;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@@ -15,98 +16,94 @@ import java.util.concurrent.TimeUnit;
|
||||
*/
|
||||
public class DefaultBackgroundExecutor implements SpiBackgroundExecutor {
|
||||
|
||||
private final DaemonScheduleThreadPool schedulePool;
|
||||
|
||||
private final DaemonExecutorService pool;
|
||||
private final ScheduledExecutorService executor;
|
||||
|
||||
/**
|
||||
* Construct the default implementation of BackgroundExecutor.
|
||||
*/
|
||||
public DefaultBackgroundExecutor(int schedulePoolSize, int shutdownWaitSeconds, String namePrefix) {
|
||||
this.pool = new DaemonExecutorService(shutdownWaitSeconds, namePrefix);
|
||||
this.schedulePool = new DaemonScheduleThreadPool(schedulePoolSize, shutdownWaitSeconds, namePrefix + "-periodic-");
|
||||
this.executor = new DaemonScheduleThreadPool(schedulePoolSize, shutdownWaitSeconds, namePrefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap the task with MDC context if defined.
|
||||
*/
|
||||
<T> Callable<T> wrapMDC(Callable<T> task) {
|
||||
final Map<String, String> map = MDC.getCopyOfContextMap();
|
||||
if (map == null) {
|
||||
return task;
|
||||
} else {
|
||||
return () -> {
|
||||
MDC.setContextMap(map);
|
||||
try {
|
||||
return task.call();
|
||||
} finally {
|
||||
MDC.clear();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap the task with MDC context if defined.
|
||||
*/
|
||||
Runnable wrapMDC(Runnable task) {
|
||||
final Map<String, String> map = MDC.getCopyOfContextMap();
|
||||
if (map == null) {
|
||||
return task;
|
||||
} else {
|
||||
return () -> {
|
||||
MDC.setContextMap(map);
|
||||
try {
|
||||
task.run();
|
||||
} finally {
|
||||
MDC.clear();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Future<T> submit(Callable<T> task) {
|
||||
return executor.submit(wrapMDC(task));
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a Runnable using a background thread.
|
||||
*/
|
||||
@Override
|
||||
public void execute(Runnable r) {
|
||||
final Map<String, String> map = MDC.getCopyOfContextMap();
|
||||
if (map == null) {
|
||||
pool.execute(r);
|
||||
} else {
|
||||
pool.execute(() -> {
|
||||
MDC.setContextMap(map);
|
||||
try {
|
||||
r.run();
|
||||
} finally {
|
||||
MDC.clear();
|
||||
}
|
||||
});
|
||||
}
|
||||
public Future<?> submit(Runnable task) {
|
||||
return executor.submit(wrapMDC(task));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executePeriodically(Runnable r, long delay, TimeUnit unit) {
|
||||
executePeriodically(r, delay, delay, unit);
|
||||
public void execute(Runnable task) {
|
||||
submit(task);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executePeriodically(Runnable r, long initialDelay, long delay, TimeUnit unit) {
|
||||
final Map<String, String> map = MDC.getCopyOfContextMap();
|
||||
if (map == null) {
|
||||
schedulePool.scheduleWithFixedDelay(r, initialDelay, delay, unit);
|
||||
} else {
|
||||
schedulePool.scheduleWithFixedDelay(() -> {
|
||||
MDC.setContextMap(map);
|
||||
try {
|
||||
r.run();
|
||||
} finally {
|
||||
MDC.clear();
|
||||
}
|
||||
}, initialDelay, delay, unit);
|
||||
}
|
||||
public void executePeriodically(Runnable task, long delay, TimeUnit unit) {
|
||||
executePeriodically(task, delay, delay, unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScheduledFuture<?> schedule(Runnable r, long delay, TimeUnit unit) {
|
||||
final Map<String, String> map = MDC.getCopyOfContextMap();
|
||||
if (map == null) {
|
||||
return schedulePool.schedule(r, delay, unit);
|
||||
} else {
|
||||
return schedulePool.schedule(() -> {
|
||||
MDC.setContextMap(map);
|
||||
try {
|
||||
r.run();
|
||||
} finally {
|
||||
MDC.clear();
|
||||
}
|
||||
}, delay, unit);
|
||||
}
|
||||
public void executePeriodically(Runnable task, long initialDelay, long delay, TimeUnit unit) {
|
||||
executor.scheduleWithFixedDelay(wrapMDC(task), initialDelay, delay, unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> ScheduledFuture<V> schedule(Callable<V> c, long delay, TimeUnit unit) {
|
||||
final Map<String, String> map = MDC.getCopyOfContextMap();
|
||||
if (map == null) {
|
||||
return schedulePool.schedule(c, delay, unit);
|
||||
} else {
|
||||
return schedulePool.schedule(() -> {
|
||||
MDC.setContextMap(map);
|
||||
try {
|
||||
return c.call();
|
||||
} finally {
|
||||
MDC.clear();
|
||||
}
|
||||
}, delay, unit);
|
||||
}
|
||||
public ScheduledFuture<?> schedule(Runnable task, long delay, TimeUnit unit) {
|
||||
return executor.schedule(wrapMDC(task), delay, unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> ScheduledFuture<V> schedule(Callable<V> task, long delay, TimeUnit unit) {
|
||||
return executor.schedule(wrapMDC(task), delay, unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
pool.shutdown();
|
||||
schedulePool.shutdown();
|
||||
executor.shutdown();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -451,16 +451,20 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
backgroundExecutor.shutdown();
|
||||
// shutdown DataSource (if its an Ebean one)
|
||||
transactionManager.shutdown(shutdownDataSource, deregisterDriver);
|
||||
dumpMetrics();
|
||||
shutdown = true;
|
||||
if (shutdownDataSource) {
|
||||
config.setDataSource(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void shutdownPlugins() {
|
||||
private void dumpMetrics() {
|
||||
if (config.isDumpMetricsOnShutdown()) {
|
||||
new DumpMetrics(this, config.getDumpMetricsOptions()).dump();
|
||||
}
|
||||
}
|
||||
|
||||
private void shutdownPlugins() {
|
||||
for (Plugin plugin : serverPlugins) {
|
||||
try {
|
||||
plugin.shutdown();
|
||||
|
||||
@@ -223,12 +223,9 @@ public class BootupClasses implements ClassFilter {
|
||||
*/
|
||||
private <T> T create(Class<T> cls, boolean logOnException) {
|
||||
try {
|
||||
// instantiate via found class
|
||||
Constructor<T> constructor = cls.getConstructor();
|
||||
return constructor.newInstance();
|
||||
|
||||
return cls.getConstructor().newInstance();
|
||||
} catch (NoSuchMethodException e) {
|
||||
logger.debug("Ignore/expected - no default constructor", e);
|
||||
logger.debug("Ignore/expected - no default constructor: " +e.getMessage());
|
||||
return null;
|
||||
|
||||
} catch (Exception e) {
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
package io.ebeaninternal.server.lib;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
* A "CachedThreadPool" based on Daemon threads.
|
||||
* <p>
|
||||
* The Threads are created as needed and once idle live for 60 seconds.
|
||||
*/
|
||||
public final class DaemonExecutorService {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(DaemonExecutorService.class);
|
||||
|
||||
private final ReentrantLock lock = new ReentrantLock(false);
|
||||
|
||||
private final String namePrefix;
|
||||
|
||||
private final int shutdownWaitSeconds;
|
||||
|
||||
private final ExecutorService service;
|
||||
|
||||
/**
|
||||
* Construct the DaemonThreadPool.
|
||||
*
|
||||
* @param shutdownWaitSeconds the time in seconds allowed for the pool to shutdown nicely. After
|
||||
* this the pool is forced to shutdown.
|
||||
*/
|
||||
public DaemonExecutorService(int shutdownWaitSeconds, String namePrefix) {
|
||||
this.service = Executors.newCachedThreadPool(new DaemonThreadFactory(namePrefix));
|
||||
this.shutdownWaitSeconds = shutdownWaitSeconds;
|
||||
this.namePrefix = namePrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the Runnable.
|
||||
*/
|
||||
public void execute(Runnable runnable) {
|
||||
service.execute(runnable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown this thread pool nicely if possible.
|
||||
* <p>
|
||||
* This will wait a maximum of 20 seconds before terminating any threads still
|
||||
* working.
|
||||
* </p>
|
||||
*/
|
||||
public void shutdown() {
|
||||
lock.lock();
|
||||
try {
|
||||
if (service.isShutdown()) {
|
||||
logger.debug("DaemonExecutorService[{}] already shut down", namePrefix);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
logger.debug("DaemonExecutorService[{}] shutting down...", namePrefix);
|
||||
service.shutdown();
|
||||
if (!service.awaitTermination(shutdownWaitSeconds, TimeUnit.SECONDS)) {
|
||||
logger.info("DaemonExecutorService[{}] shut down timeout exceeded. Terminating running threads.", namePrefix);
|
||||
service.shutdownNow();
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("Error during shutdown of DaemonThreadPool[" + namePrefix + "]", e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,15 +9,12 @@ import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
* Daemon based ScheduleThreadPool.
|
||||
* <p>
|
||||
* Uses Daemon threads and hooks into shutdown event.
|
||||
* </p>
|
||||
*/
|
||||
public final class DaemonScheduleThreadPool extends ScheduledThreadPoolExecutor {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(DaemonScheduleThreadPool.class);
|
||||
|
||||
private final ReentrantLock lock = new ReentrantLock(false);
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
|
||||
private final String namePrefix;
|
||||
|
||||
@@ -35,26 +32,25 @@ public final class DaemonScheduleThreadPool extends ScheduledThreadPoolExecutor
|
||||
/**
|
||||
* Shutdown this thread pool nicely if possible.
|
||||
* <p>
|
||||
* This will wait a maximum of 20 seconds before terminating any threads still
|
||||
* working.
|
||||
* </p>
|
||||
* This will wait a maximum of shutdownWaitSeconds seconds before
|
||||
* terminating any threads still working.
|
||||
*/
|
||||
@Override
|
||||
public void shutdown() {
|
||||
lock.lock();
|
||||
try {
|
||||
if (super.isShutdown()) {
|
||||
logger.debug("DaemonScheduleThreadPool {} already shut down", namePrefix);
|
||||
logger.debug("Already shutdown {}", namePrefix);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
logger.debug("DaemonScheduleThreadPool {} shutting down...", namePrefix);
|
||||
logger.trace("Shutting down {} ...", namePrefix);
|
||||
super.shutdown();
|
||||
if (!super.awaitTermination(shutdownWaitSeconds, TimeUnit.SECONDS)) {
|
||||
logger.info("DaemonScheduleThreadPool shut down timeout exceeded. Terminating running threads.");
|
||||
logger.info("Shutdown wait timeout exceeded. Terminating running threads for {}", namePrefix);
|
||||
super.shutdownNow();
|
||||
}
|
||||
|
||||
logger.debug("Shutdown complete for {}", namePrefix);
|
||||
} catch (Exception e) {
|
||||
logger.error("Error during shutdown of " + namePrefix, e);
|
||||
e.printStackTrace();
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package io.ebeaninternal.server.lib;
|
||||
|
||||
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
@@ -9,42 +8,28 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
* <p>
|
||||
* Daemon threads do not stop a JVM stopping. If an application only has Daemon
|
||||
* threads left it will shutdown.
|
||||
* </p>
|
||||
* <p>
|
||||
* In using Daemon threads you need to either not care about being interrupted
|
||||
* on shutdown or register with the JVM shutdown hook to perform a nice shutdown
|
||||
* of the daemon threads etc.
|
||||
* </p>
|
||||
*
|
||||
* @author rbygrave
|
||||
*/
|
||||
public class DaemonThreadFactory implements ThreadFactory {
|
||||
|
||||
private static final AtomicInteger poolNumber = new AtomicInteger(1);
|
||||
|
||||
private final ThreadGroup group;
|
||||
|
||||
private final AtomicInteger threadNumber = new AtomicInteger(1);
|
||||
|
||||
private final String namePrefix;
|
||||
|
||||
public DaemonThreadFactory(String namePrefix) {
|
||||
SecurityManager s = System.getSecurityManager();
|
||||
this.group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
|
||||
this.namePrefix = namePrefix != null ? namePrefix : "pool-" + poolNumber.getAndIncrement() + "-thread-";
|
||||
this.namePrefix = namePrefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Thread newThread(Runnable r) {
|
||||
|
||||
Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
|
||||
|
||||
Thread t = new Thread(null, r, namePrefix + threadNumber.getAndIncrement(), 0);
|
||||
t.setDaemon(true);
|
||||
|
||||
if (t.getPriority() != Thread.NORM_PRIORITY) {
|
||||
t.setPriority(Thread.NORM_PRIORITY);
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
+91
-3
@@ -2,12 +2,64 @@ package io.ebeaninternal.server.core;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.slf4j.MDC;
|
||||
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class DefaultBackgroundExecutorTest {
|
||||
|
||||
@Test
|
||||
public void submit_callable() throws Exception {
|
||||
|
||||
DefaultBackgroundExecutor es = new DefaultBackgroundExecutor(1, 2, "test");
|
||||
|
||||
final Future<String> future0 = es.submit(() -> "Hello");
|
||||
final Future<String> future1 = es.submit(() -> "There");
|
||||
final Future<String> future2 = es.submit(() -> {
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
return "Slow";
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
return "Interrupted";
|
||||
}
|
||||
});
|
||||
|
||||
es.shutdown();
|
||||
|
||||
assertThat(future0.get()).isEqualTo("Hello");
|
||||
assertThat(future1.get(1, TimeUnit.SECONDS)).isEqualTo("There");
|
||||
assertThat(future2.get()).isEqualTo("Slow");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shutdown_slowCallable_expect_interrupted() throws Exception {
|
||||
|
||||
int shutdownWaitSecs = 1;
|
||||
DefaultBackgroundExecutor es = new DefaultBackgroundExecutor(1, shutdownWaitSecs, "test");
|
||||
|
||||
final Future<String> future2 = es.submit(() -> {
|
||||
try {
|
||||
Thread.sleep(1500); // longer than shutdown wait
|
||||
return "Slow";
|
||||
} catch (InterruptedException e) {
|
||||
// expected for this test
|
||||
Thread.currentThread().interrupt();
|
||||
return "Interrupted";
|
||||
}
|
||||
});
|
||||
|
||||
// shutdown waits max shutdownWaitSecs seconds for active tasks
|
||||
es.shutdown();
|
||||
assertThat(future2.get()).isEqualTo("Interrupted");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("test takes long time")
|
||||
public void shutdown_when_running_expect_waitAndNiceShutdown() throws Exception {
|
||||
public void shutdown_when_running_expect_waitAndNiceShutdown() {
|
||||
|
||||
DefaultBackgroundExecutor es = new DefaultBackgroundExecutor(1, 20, "test");
|
||||
|
||||
@@ -20,7 +72,7 @@ public class DefaultBackgroundExecutorTest {
|
||||
|
||||
@Test
|
||||
@Ignore("test takes long time")
|
||||
public void shutdown_when_rougeRunnable_expect_InterruptedException() throws Exception {
|
||||
public void shutdown_when_rougeRunnable_expect_InterruptedException() {
|
||||
|
||||
DefaultBackgroundExecutor es = new DefaultBackgroundExecutor(1, 10, "test");
|
||||
|
||||
@@ -31,8 +83,44 @@ public class DefaultBackgroundExecutorTest {
|
||||
es.shutdown();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void wrapWithNoMDC() {
|
||||
DefaultBackgroundExecutor es = new DefaultBackgroundExecutor(1, 10, "test");
|
||||
assertThat(MDC.getCopyOfContextMap()).isNull();
|
||||
es.wrapMDC(() -> {
|
||||
assertThat(MDC.getCopyOfContextMap()).isNull();
|
||||
});
|
||||
es.wrapMDC(() -> {
|
||||
assertThat(MDC.getCopyOfContextMap()).isNull();
|
||||
return "Callable";
|
||||
});
|
||||
es.shutdown();
|
||||
}
|
||||
|
||||
class RunFor implements Runnable {
|
||||
@Test
|
||||
public void wrapWithMDC_expect_() {
|
||||
DefaultBackgroundExecutor es = new DefaultBackgroundExecutor(1, 10, "test");
|
||||
MDC.clear();
|
||||
MDC.put("hello", "there");
|
||||
es.wrapMDC(() -> {
|
||||
assertThat(MDC.get("hello")).isEqualTo("there");
|
||||
});
|
||||
es.wrapMDC(() -> {
|
||||
assertThat(MDC.get("hello")).isEqualTo("there");
|
||||
return "Callable";
|
||||
});
|
||||
es.execute(() -> {
|
||||
assertThat(MDC.get("hello")).isEqualTo("there");
|
||||
});
|
||||
es.submit(() -> {
|
||||
assertThat(MDC.get("hello")).isEqualTo("there");
|
||||
return "Callable";
|
||||
});
|
||||
MDC.clear();
|
||||
es.shutdown();
|
||||
}
|
||||
|
||||
private static class RunFor implements Runnable {
|
||||
|
||||
final long wait;
|
||||
final String id;
|
||||
|
||||
-67
@@ -1,67 +0,0 @@
|
||||
package io.ebeaninternal.server.lib.sql;
|
||||
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.Ebean;
|
||||
import io.ebean.EbeanServer;
|
||||
import io.ebeaninternal.server.core.DefaultBackgroundExecutor;
|
||||
import org.tests.model.basic.Customer;
|
||||
import org.junit.Test;
|
||||
|
||||
public class TestDataSourceMaxWithEntity extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
boolean skipThisTest = true;
|
||||
|
||||
if (skipThisTest) {
|
||||
return;
|
||||
}
|
||||
|
||||
EbeanServer server = Ebean.getServer(null);
|
||||
|
||||
|
||||
DefaultBackgroundExecutor bg = new DefaultBackgroundExecutor(1, 30, "testDs");
|
||||
|
||||
try {
|
||||
for (int i = 0; i < 12; i++) {
|
||||
// Thread.sleep(10*i);
|
||||
bg.execute(new ConnRunner(server, 4000, i));
|
||||
}
|
||||
|
||||
Thread.sleep(30000);
|
||||
|
||||
server.shutdown(true, false);
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class ConnRunner implements Runnable {
|
||||
|
||||
final EbeanServer server;
|
||||
final long sleepMillis;
|
||||
final int position;
|
||||
|
||||
ConnRunner(EbeanServer server, long sleepMillis, int position) {
|
||||
this.server = server;
|
||||
this.sleepMillis = sleepMillis;
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
server.find(Customer.class).findCount();
|
||||
try {
|
||||
System.out.println(position + " sleep " + sleepMillis);
|
||||
Thread.sleep(sleepMillis);
|
||||
System.out.println(position + " sleep done");
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
package io.ebeaninternal.server.lib.sql;
|
||||
|
||||
import io.ebean.BackgroundExecutor;
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.DB;
|
||||
import io.ebean.Database;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.tests.model.basic.Customer;
|
||||
|
||||
public class TestShutdownWithBackgroundTasks extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void test() {
|
||||
|
||||
Database server = DB.getDefault();
|
||||
final BackgroundExecutor bg = server.getBackgroundExecutor();
|
||||
try {
|
||||
for (int i = 0; i < 12; i++) {
|
||||
bg.execute(new Job(server, 500, i));
|
||||
}
|
||||
|
||||
Thread.sleep(1000);
|
||||
server.shutdown();
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static class Job implements Runnable {
|
||||
|
||||
final Database server;
|
||||
final long sleepMillis;
|
||||
final int position;
|
||||
|
||||
Job(Database server, long sleepMillis, int position) {
|
||||
this.server = server;
|
||||
this.sleepMillis = sleepMillis;
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
System.out.println(position + " sleep " + sleepMillis);
|
||||
Thread.sleep(sleepMillis);
|
||||
server.find(Customer.class).findCount();
|
||||
System.out.println(position + " sleep done");
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -92,7 +92,7 @@
|
||||
<!-- <logger name="io.ebean.cache.NATKEY" level="TRACE"/>-->
|
||||
<!-- <logger name="io.ebean.cache.COLL" level="TRACE"/>-->
|
||||
|
||||
<!--<logger name="io.ebeaninternal.server.deploy" level="TRACE"/>-->
|
||||
<logger name="io.ebeaninternal.server.lib" level="TRACE"/>
|
||||
|
||||
<!--<logger name="io.ebeaninternal.server.autotune" level="TRACE"/>-->
|
||||
|
||||
|
||||
Reference in New Issue
Block a user