You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@bookkeeper.apache.org by GitBox <gi...@apache.org> on 2018/10/02 18:52:21 UTC
[GitHub] ivankelly closed pull request #1729: Decorate OrderedExecutor
threads rather than runnables
ivankelly closed pull request #1729: Decorate OrderedExecutor threads rather than runnables
URL: https://github.com/apache/bookkeeper/pull/1729
This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:
As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):
diff --git a/bookkeeper-common/src/main/java/org/apache/bookkeeper/common/util/OrderedExecutor.java b/bookkeeper-common/src/main/java/org/apache/bookkeeper/common/util/OrderedExecutor.java
index ab62a687e1..220e1af9c0 100644
--- a/bookkeeper-common/src/main/java/org/apache/bookkeeper/common/util/OrderedExecutor.java
+++ b/bookkeeper-common/src/main/java/org/apache/bookkeeper/common/util/OrderedExecutor.java
@@ -19,6 +19,7 @@
import static com.google.common.base.Preconditions.checkArgument;
+import com.google.common.util.concurrent.ForwardingExecutorService;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
@@ -283,6 +284,61 @@ protected ExecutorService getBoundedExecutor(ThreadPoolExecutor executor) {
return new BoundedExecutorService(executor, this.maxTasksInQueue);
}
+ protected ExecutorService addExecutorDecorators(ExecutorService executor) {
+ return new ForwardingExecutorService() {
+ @Override
+ protected ExecutorService delegate() {
+ return executor;
+ }
+
+ @Override
+ public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
+ throws InterruptedException {
+ return super.invokeAll(timedCallables(tasks));
+ }
+
+ @Override
+ public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
+ long timeout, TimeUnit unit)
+ throws InterruptedException {
+ return super.invokeAll(timedCallables(tasks), timeout, unit);
+ }
+
+ @Override
+ public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
+ throws InterruptedException, ExecutionException {
+ return super.invokeAny(timedCallables(tasks));
+ }
+
+ @Override
+ public <T> T invokeAny(Collection<? extends Callable<T>> tasks,
+ long timeout, TimeUnit unit)
+ throws InterruptedException, ExecutionException, TimeoutException {
+ return super.invokeAny(timedCallables(tasks), timeout, unit);
+ }
+
+ @Override
+ public void execute(Runnable command) {
+ super.execute(timedRunnable(command));
+ }
+
+ @Override
+ public <T> Future<T> submit(Callable<T> task) {
+ return super.submit(timedCallable(task));
+ }
+
+ @Override
+ public Future<?> submit(Runnable task) {
+ return super.submit(timedRunnable(task));
+ }
+
+ @Override
+ public <T> Future<T> submit(Runnable task, T result) {
+ return super.submit(timedRunnable(task), result);
+ }
+ };
+ }
+
/**
* Constructs Safe executor.
*
@@ -318,7 +374,7 @@ protected OrderedExecutor(String baseName, int numThreads, ThreadFactory threadF
ThreadPoolExecutor thread = createSingleThreadExecutor(
new ThreadFactoryBuilder().setNameFormat(name + "-" + getClass().getSimpleName() + "-" + i + "-%d")
.setThreadFactory(threadFactory).build());
- threads[i] = getBoundedExecutor(thread);
+ threads[i] = addExecutorDecorators(getBoundedExecutor(thread));
final int idx = i;
try {
@@ -391,7 +447,7 @@ public boolean preserveMdc() {
* @param r
*/
public void executeOrdered(Object orderingKey, SafeRunnable r) {
- chooseThread(orderingKey).execute(timedRunnable(r));
+ chooseThread(orderingKey).execute(r);
}
/**
@@ -400,7 +456,7 @@ public void executeOrdered(Object orderingKey, SafeRunnable r) {
* @param r
*/
public void executeOrdered(long orderingKey, SafeRunnable r) {
- chooseThread(orderingKey).execute(timedRunnable(r));
+ chooseThread(orderingKey).execute(r);
}
/**
@@ -409,7 +465,7 @@ public void executeOrdered(long orderingKey, SafeRunnable r) {
* @param r
*/
public void executeOrdered(int orderingKey, SafeRunnable r) {
- chooseThread(orderingKey).execute(timedRunnable(r));
+ chooseThread(orderingKey).execute(r);
}
public <T> ListenableFuture<T> submitOrdered(long orderingKey, Callable<T> task) {
@@ -504,7 +560,7 @@ protected Runnable timedRunnable(Runnable r) {
*/
@Override
public <T> Future<T> submit(Runnable task, T result) {
- return chooseThread().submit(timedRunnable(task), result);
+ return chooseThread().submit(task, result);
}
/**
@@ -512,7 +568,7 @@ protected Runnable timedRunnable(Runnable r) {
*/
@Override
public Future<?> submit(Runnable task) {
- return chooseThread().submit(timedRunnable(task));
+ return chooseThread().submit(task);
}
/**
diff --git a/bookkeeper-common/src/main/java/org/apache/bookkeeper/common/util/OrderedScheduler.java b/bookkeeper-common/src/main/java/org/apache/bookkeeper/common/util/OrderedScheduler.java
index 6f366fdbc8..3c1fef5f79 100644
--- a/bookkeeper-common/src/main/java/org/apache/bookkeeper/common/util/OrderedScheduler.java
+++ b/bookkeeper-common/src/main/java/org/apache/bookkeeper/common/util/OrderedScheduler.java
@@ -17,18 +17,27 @@
*/
package org.apache.bookkeeper.common.util;
+import com.google.common.util.concurrent.ForwardingListeningExecutorService;
import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListenableScheduledFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.ListeningScheduledExecutorService;
import io.netty.util.concurrent.DefaultThreadFactory;
+import java.util.Collection;
+import java.util.List;
import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
import org.apache.bookkeeper.stats.StatsLogger;
@@ -119,6 +128,11 @@ protected ListeningScheduledExecutorService getBoundedExecutor(ThreadPoolExecuto
return new BoundedScheduledExecutorService((ScheduledThreadPoolExecutor) executor, this.maxTasksInQueue);
}
+ @Override
+ protected ListeningScheduledExecutorService addExecutorDecorators(ExecutorService executor) {
+ return new OrderedSchedulerDecoratedThread((ListeningScheduledExecutorService) executor);
+ }
+
@Override
public ListeningScheduledExecutorService chooseThread() {
return (ListeningScheduledExecutorService) super.chooseThread();
@@ -286,4 +300,84 @@ public ListeningScheduledExecutorService chooseThread(long orderingKey) {
return chooseThread().scheduleWithFixedDelay(timedRunnable(command), initialDelay, delay, unit);
}
+ class OrderedSchedulerDecoratedThread extends ForwardingListeningExecutorService
+ implements ListeningScheduledExecutorService {
+ private final ListeningScheduledExecutorService delegate;
+
+ private OrderedSchedulerDecoratedThread(ListeningScheduledExecutorService delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ protected ListeningExecutorService delegate() {
+ return delegate;
+ }
+
+ @Override
+ public ListenableScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
+ return delegate.schedule(timedRunnable(command), delay, unit);
+ }
+
+ @Override
+ public <V> ListenableScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
+ return delegate.schedule(timedCallable(callable), delay, unit);
+ }
+
+ @Override
+ public ListenableScheduledFuture<?> scheduleAtFixedRate(Runnable command,
+ long initialDelay, long period, TimeUnit unit) {
+ return delegate.scheduleAtFixedRate(timedRunnable(command), initialDelay, period, unit);
+ }
+
+ @Override
+ public ListenableScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
+ long initialDelay, long delay, TimeUnit unit) {
+ return delegate.scheduleAtFixedRate(timedRunnable(command), initialDelay, delay, unit);
+ }
+
+ @Override
+ public <T> ListenableFuture<T> submit(Callable<T> task) {
+ return super.submit(timedCallable(task));
+ }
+
+ @Override
+ public ListenableFuture<?> submit(Runnable task) {
+ return super.submit(timedRunnable(task));
+ }
+
+ @Override
+ public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException {
+ return super.invokeAll(timedCallables(tasks));
+ }
+
+ @Override
+ public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
+ long timeout, TimeUnit unit) throws InterruptedException {
+ return super.invokeAll(timedCallables(tasks), timeout, unit);
+ }
+
+ @Override
+ public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
+ throws InterruptedException, ExecutionException {
+ return super.invokeAny(timedCallables(tasks));
+ }
+
+ @Override
+ public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout,
+ TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
+
+ return super.invokeAny(timedCallables(tasks), timeout, unit);
+ }
+
+ @Override
+ public <T> ListenableFuture<T> submit(Runnable task, T result) {
+ return super.submit(timedRunnable(task), result);
+ }
+
+ @Override
+ public void execute(Runnable command) {
+ super.execute(timedRunnable(command));
+ }
+ };
+
}
diff --git a/bookkeeper-common/src/test/java/org/apache/bookkeeper/common/util/TestOrderedExecutorDecorators.java b/bookkeeper-common/src/test/java/org/apache/bookkeeper/common/util/TestOrderedExecutorDecorators.java
new file mode 100644
index 0000000000..f03b24df95
--- /dev/null
+++ b/bookkeeper-common/src/test/java/org/apache/bookkeeper/common/util/TestOrderedExecutorDecorators.java
@@ -0,0 +1,157 @@
+/*
+ *
+ * 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.bookkeeper.common.util;
+
+import static org.apache.bookkeeper.common.util.SafeRunnable.safeRun;
+import static org.hamcrest.Matchers.hasItem;
+import static org.junit.Assert.assertThat;
+import static org.mockito.AdditionalAnswers.answerVoid;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.Level;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.MDC;
+import org.apache.log4j.spi.LoggingEvent;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Test that decorators applied by OrderedExecutor/Scheduler are correctly applied.
+ */
+public class TestOrderedExecutorDecorators {
+ private static final Logger log = LoggerFactory.getLogger(TestOrderedExecutorDecorators.class);
+ private static final String MDC_KEY = "mdc-key";
+
+ private Appender mockAppender;
+ private final Queue<String> capturedEvents = new ConcurrentLinkedQueue<>();
+
+ public static String mdcFormat(Object mdc, String message) {
+ return String.format("[%s:%s] %s", MDC_KEY, mdc, message);
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ MDC.clear();
+ mockAppender = mock(Appender.class);
+ when(mockAppender.getName()).thenReturn("MockAppender");
+
+ LogManager.getRootLogger().addAppender(mockAppender);
+ LogManager.getRootLogger().setLevel(Level.INFO);
+
+ doAnswer(answerVoid((LoggingEvent event) -> {
+ capturedEvents.add(mdcFormat(event.getMDC(MDC_KEY),
+ event.getRenderedMessage()));
+ })).when(mockAppender).doAppend(any());
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ LogManager.getRootLogger().removeAppender(mockAppender);
+ capturedEvents.clear();
+ MDC.clear();
+ }
+
+ @Test
+ public void testMDCInvokeOrdered() throws Exception {
+ OrderedExecutor executor = OrderedExecutor.newBuilder()
+ .name("test").numThreads(20).preserveMdcForTaskExecution(true).build();
+
+ try {
+ MDC.put(MDC_KEY, "testMDCInvokeOrdered");
+ executor.submitOrdered(10, () -> {
+ log.info("foobar");
+ return 10;
+ }).get();
+ assertThat(capturedEvents,
+ hasItem(mdcFormat("testMDCInvokeOrdered", "foobar")));
+ } finally {
+ executor.shutdown();
+ }
+ }
+
+ @Test
+ public void testMDCInvokeDirectOnChosen() throws Exception {
+ OrderedExecutor executor = OrderedExecutor.newBuilder()
+ .name("test").numThreads(20).preserveMdcForTaskExecution(true).build();
+
+ try {
+ MDC.put(MDC_KEY, "testMDCInvokeOrdered");
+ executor.chooseThread(10).submit(() -> {
+ log.info("foobar");
+ return 10;
+ }).get();
+ assertThat(capturedEvents,
+ hasItem(mdcFormat("testMDCInvokeOrdered", "foobar")));
+ } finally {
+ executor.shutdown();
+ }
+
+ }
+
+
+ @Test
+ public void testMDCScheduleOrdered() throws Exception {
+ OrderedScheduler scheduler = OrderedScheduler.newSchedulerBuilder()
+ .name("test").numThreads(20).preserveMdcForTaskExecution(true).build();
+
+ try {
+ MDC.put(MDC_KEY, "testMDCInvokeOrdered");
+ scheduler.scheduleOrdered(10, safeRun(() -> {
+ log.info("foobar");
+ }), 0, TimeUnit.DAYS).get();
+ assertThat(capturedEvents,
+ hasItem(mdcFormat("testMDCInvokeOrdered", "foobar")));
+ } finally {
+ scheduler.shutdown();
+ }
+ }
+
+ @Test
+ public void testMDCScheduleDirectOnChosen() throws Exception {
+ OrderedScheduler scheduler = OrderedScheduler.newSchedulerBuilder()
+ .name("test").numThreads(20).preserveMdcForTaskExecution(true).build();
+
+ try {
+ MDC.put(MDC_KEY, "testMDCInvokeOrdered");
+ scheduler.chooseThread(10).schedule(safeRun(() -> {
+ log.info("foobar");
+ }), 0, TimeUnit.DAYS).get();
+ assertThat(capturedEvents,
+ hasItem(mdcFormat("testMDCInvokeOrdered", "foobar")));
+ } finally {
+ scheduler.shutdown();
+ }
+ }
+
+}
----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
With regards,
Apache Git Services