From b4230c62bf178497d94d7e81471470c9c51871a4 Mon Sep 17 00:00:00 2001 From: Domenico Francesco Bruscino Date: Sat, 24 Jun 2023 09:41:16 +0200 Subject: [PATCH] ARTEMIS-4332 Add store operation trackers --- .../impl/journal/OperationContextImpl.java | 36 +++++++++++++++- .../journal/OperationContextUnitTest.java | 41 +++++++++++++++++++ 2 files changed, 75 insertions(+), 2 deletions(-) diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/OperationContextImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/OperationContextImpl.java index ceb402809c..0a17c8d2d8 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/OperationContextImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/OperationContextImpl.java @@ -28,6 +28,9 @@ import org.apache.activemq.artemis.core.journal.impl.SimpleWaitIOCallback; import org.apache.activemq.artemis.core.persistence.OperationContext; import org.apache.activemq.artemis.core.server.ActiveMQServerLogger; import org.apache.activemq.artemis.utils.ExecutorFactory; +import org.apache.commons.collections.Buffer; +import org.apache.commons.collections.BufferUtils; +import org.apache.commons.collections.buffer.CircularFifoBuffer; /** * Each instance of OperationContextImpl is associated with an executor (usually an ordered Executor). @@ -38,6 +41,9 @@ import org.apache.activemq.artemis.utils.ExecutorFactory; * If there are no pending IO operations, the tasks are just executed at the callers thread without any context switch. * * So, if you are doing operations that are not dependent on IO (e.g NonPersistentMessages) you wouldn't have any context switch. + * + * If you need to track store operations you can set the system property "ARTEMIS_OPCONTEXT_MAX_DEBUG_TRACKERS" + * with the max number of trackers that you want to keep in memory. */ public class OperationContextImpl implements OperationContext { @@ -104,6 +110,24 @@ public class OperationContextImpl implements OperationContext { private final Executor executor; + private static int maxDebugTrackers = Integer.parseInt( + System.getProperty("ARTEMIS_OPCONTEXT_MAX_DEBUG_TRACKERS", "0")); + + private Buffer debugTrackers = OperationContextImpl.maxDebugTrackers > 0 ? + BufferUtils.synchronizedBuffer(new CircularFifoBuffer(OperationContextImpl.maxDebugTrackers)) : null; + + protected static int getMaxDebugTrackers() { + return OperationContextImpl.maxDebugTrackers; + } + + protected static void setMaxDebugTrackers(int maxDebugTrackers) { + OperationContextImpl.maxDebugTrackers = maxDebugTrackers; + } + + protected Buffer getDebugTrackers() { + return debugTrackers; + } + public OperationContextImpl(final Executor executor) { super(); this.executor = executor; @@ -122,7 +146,10 @@ public class OperationContextImpl implements OperationContext { @Override public void storeLineUp() { - STORE_LINEUP_UPDATER.incrementAndGet(this); + long storeLineUpValue = STORE_LINEUP_UPDATER.incrementAndGet(this); + if (debugTrackers != null) { + debugTrackers.add(new Exception(">" + storeLineUpValue)); + } } @Override @@ -216,7 +243,12 @@ public class OperationContextImpl implements OperationContext { @Override public synchronized void done() { - stored++; + this.stored++; + + if (debugTrackers != null) { + debugTrackers.add(new Exception("<" + stored)); + } + checkTasks(); } diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/persistence/impl/journal/OperationContextUnitTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/persistence/impl/journal/OperationContextUnitTest.java index 997d8808be..6e95d6a3b3 100644 --- a/artemis-server/src/test/java/org/apache/activemq/artemis/core/persistence/impl/journal/OperationContextUnitTest.java +++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/persistence/impl/journal/OperationContextUnitTest.java @@ -422,4 +422,45 @@ public class OperationContextUnitTest extends ActiveMQTestBase { Assert.assertEquals(0, operations.get()); } + @Test + public void testContextWithoutDebugTrackers() { + ExecutorService executor = Executors.newSingleThreadExecutor(ActiveMQThreadFactory.defaultThreadFactory(getClass().getName())); + runAfter(executor::shutdownNow); + + Assert.assertEquals(0, OperationContextImpl.getMaxDebugTrackers()); + OperationContextImpl context = new OperationContextImpl(executor); + Assert.assertNull(context.getDebugTrackers()); + + context.storeLineUp(); + Assert.assertNull(context.getDebugTrackers()); + + context.done(); + Assert.assertNull(context.getDebugTrackers()); + } + + @Test + public void testContextWithDebugTrackers() { + int maxStoreOperationTrackers = OperationContextImpl.getMaxDebugTrackers(); + ExecutorService executor = Executors.newSingleThreadExecutor(ActiveMQThreadFactory.defaultThreadFactory(getClass().getName())); + runAfter(executor::shutdownNow); + + OperationContextImpl.setMaxDebugTrackers(1); + try { + Assert.assertEquals(1, OperationContextImpl.getMaxDebugTrackers()); + OperationContextImpl context = new OperationContextImpl(executor); + Assert.assertNotNull(context.getDebugTrackers()); + + context.storeLineUp(); + Assert.assertEquals(1, context.getDebugTrackers().size()); + Assert.assertEquals("storeLineUp", ((Exception)context.getDebugTrackers().get()).getStackTrace()[0].getMethodName()); + Assert.assertEquals("testContextWithDebugTrackers", ((Exception)context.getDebugTrackers().get()).getStackTrace()[1].getMethodName()); + + context.done(); + Assert.assertEquals(1, context.getDebugTrackers().size()); + Assert.assertEquals("done", ((Exception)context.getDebugTrackers().get()).getStackTrace()[0].getMethodName()); + Assert.assertEquals("testContextWithDebugTrackers", ((Exception)context.getDebugTrackers().get()).getStackTrace()[1].getMethodName()); + } finally { + OperationContextImpl.setMaxDebugTrackers(maxStoreOperationTrackers); + } + } }