From e863e66130f7417ff249726b87eee9aa7cf05c0b Mon Sep 17 00:00:00 2001 From: Michael Musgrove Date: Fri, 8 May 2026 17:04:41 +0100 Subject: [PATCH] JBTM-4039 Set slot store sizes to match number of threads and clean the stores before each test. --- .../performance/DiskSlotsStoreBenchmark.java | 20 +++++- .../jta/xa/performance/HQStoreBenchmark.java | 12 +++- .../performance/InfinispanSlotsStoreBase.java | 3 +- .../InfinispanSlotsStoreBenchmark.java | 26 ++++--- .../ats/jta/xa/performance/JTAStoreBase.java | 67 +++++++++++++++++++ 5 files changed, 114 insertions(+), 14 deletions(-) diff --git a/narayana/ArjunaJTA/jta/tests/classes/com/arjuna/ats/jta/xa/performance/DiskSlotsStoreBenchmark.java b/narayana/ArjunaJTA/jta/tests/classes/com/arjuna/ats/jta/xa/performance/DiskSlotsStoreBenchmark.java index e6753f37..025ae40c 100644 --- a/narayana/ArjunaJTA/jta/tests/classes/com/arjuna/ats/jta/xa/performance/DiskSlotsStoreBenchmark.java +++ b/narayana/ArjunaJTA/jta/tests/classes/com/arjuna/ats/jta/xa/performance/DiskSlotsStoreBenchmark.java @@ -22,6 +22,7 @@ import org.openjdk.jmh.annotations.Scope; import org.openjdk.jmh.annotations.Setup; import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; import org.openjdk.jmh.infra.Blackhole; import org.openjdk.jmh.profile.JavaFlightRecorderProfiler; import org.openjdk.jmh.runner.Runner; @@ -30,11 +31,14 @@ import org.openjdk.jmh.runner.options.OptionsBuilder; import org.openjdk.jmh.runner.options.TimeValue; +import java.nio.file.Paths; import java.util.concurrent.TimeUnit; @State(Scope.Benchmark) public class DiskSlotsStoreBenchmark extends JTAStoreBase { static final int THREADS = 240;//10;//_000; //Threads.MAX; + static final String BM_CLASS_NAME = DiskSlotsStoreBenchmark.class.getSimpleName(); + static final int FORKS = 1; static final int ITERATIONS = 5; static final int TIME_PER_ITER = 2; @@ -42,8 +46,7 @@ public class DiskSlotsStoreBenchmark extends JTAStoreBase { public static void main(String[] args) throws RunnerException { // Sometimes it is useful to run the benchmark directly from an IDE: Options opt = new OptionsBuilder() - .include(InfinispanSlotsStoreBenchmark.class.getSimpleName() + ".testInfinispanStore") - + .include(BM_CLASS_NAME + ".testDiskSlotsStore") .timeUnit(TimeUnit.SECONDS) .threads(THREADS) .forks(FORKS) @@ -61,6 +64,7 @@ public static void main(String[] args) throws RunnerException { // bin directory .addProfiler(JavaFlightRecorderProfiler.class) .jvmArgs("-Djmh.executor=FJP") // ForkJoinPool + // to debug the forks use "-agentlib:jdwp=transport=dt_socket,address=5005,server=y,suspend=y" .build(); new Runner(opt).run(); @@ -71,8 +75,20 @@ public static void main(String[] args) throws RunnerException { public static void setup() throws CoreEnvironmentBeanException { JTAStoreBase.setup(SlotStoreAdaptor.class.getName()); SlotStoreEnvironmentBean configBean = BeanPopulator.getDefaultInstance(SlotStoreEnvironmentBean.class); + int threadCount = getThreadCountFromProperties(THREADS); + // set the slot size, making sure it's a sensible multiple + configBean.setNumberOfSlots(roundUp(256, threadCount)); configBean.setBackingSlotsClassName(DiskSlots.class.getName()); + + cleanStore(Paths.get(configBean.getStoreDir()).toFile()); + } + + @TearDown(Level.Trial) + public static void tearDown() { + SlotStoreEnvironmentBean configBean = BeanPopulator.getDefaultInstance(SlotStoreEnvironmentBean.class); + + cleanStore(Paths.get(configBean.getStoreDir()).toFile()); } @Benchmark diff --git a/narayana/ArjunaJTA/jta/tests/classes/com/arjuna/ats/jta/xa/performance/HQStoreBenchmark.java b/narayana/ArjunaJTA/jta/tests/classes/com/arjuna/ats/jta/xa/performance/HQStoreBenchmark.java index 2992e331..af80c5bf 100644 --- a/narayana/ArjunaJTA/jta/tests/classes/com/arjuna/ats/jta/xa/performance/HQStoreBenchmark.java +++ b/narayana/ArjunaJTA/jta/tests/classes/com/arjuna/ats/jta/xa/performance/HQStoreBenchmark.java @@ -15,14 +15,16 @@ import jakarta.transaction.RollbackException; import jakarta.transaction.SystemException; import org.junit.BeforeClass; -import org.junit.Test; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.Scope; import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.Setup; import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.TearDown; import org.openjdk.jmh.infra.Blackhole; +import java.nio.file.Paths; + @State(Scope.Benchmark) public class HQStoreBenchmark extends JTAStoreBase { @@ -30,6 +32,7 @@ public class HQStoreBenchmark extends JTAStoreBase { @BeforeClass public static void setup() throws CoreEnvironmentBeanException { HornetqJournalEnvironmentBean hornetqJournalEnvironmentBean = BeanPopulator.getDefaultInstance(HornetqJournalEnvironmentBean.class); + cleanStore(Paths.get(hornetqJournalEnvironmentBean.getStoreDir()).toFile()); // Please keep the journal config in line with the journal config in narayana/ArjunaJTA/jta/etc/jbossts-properties.xml hornetqJournalEnvironmentBean.setAsyncIO(true); hornetqJournalEnvironmentBean.setSyncDeletes(false); @@ -38,7 +41,12 @@ public static void setup() throws CoreEnvironmentBeanException { JTAStoreBase.setup(HornetqObjectStoreAdaptor.class.getName()); } - @Test + @TearDown + public static void tearDown() { + String storeDir = BeanPopulator.getDefaultInstance(HornetqJournalEnvironmentBean.class).getStoreDir(); + cleanStore(Paths.get(storeDir).toFile()); + } + @Benchmark public void testHQStore(Blackhole bh) throws HeuristicRollbackException, SystemException, HeuristicMixedException, NotSupportedException, RollbackException { bh.consume(super.jtaTest()); diff --git a/narayana/ArjunaJTA/jta/tests/classes/com/arjuna/ats/jta/xa/performance/InfinispanSlotsStoreBase.java b/narayana/ArjunaJTA/jta/tests/classes/com/arjuna/ats/jta/xa/performance/InfinispanSlotsStoreBase.java index 53cfe96c..ea0eff1a 100644 --- a/narayana/ArjunaJTA/jta/tests/classes/com/arjuna/ats/jta/xa/performance/InfinispanSlotsStoreBase.java +++ b/narayana/ArjunaJTA/jta/tests/classes/com/arjuna/ats/jta/xa/performance/InfinispanSlotsStoreBase.java @@ -35,8 +35,7 @@ public class InfinispanSlotsStoreBase extends JTAStoreBase { // the name of the cluster and the shared cache used for the object store static final String CLUSTER_NAME = "objectStoreCluster"; // location of the file system store (with surefire it will be the build directory) - static final String STORE_DIR = System.getProperty("narayana.storeLocation") + "/infinispan-caches"; - + static final String STORE_DIR = System.getProperty("user.dir") + "/infinispan-caches"; // record bringing together various data related to a slot store instance record Store(DefaultCacheManager manager, // the infinispan cache manager diff --git a/narayana/ArjunaJTA/jta/tests/classes/com/arjuna/ats/jta/xa/performance/InfinispanSlotsStoreBenchmark.java b/narayana/ArjunaJTA/jta/tests/classes/com/arjuna/ats/jta/xa/performance/InfinispanSlotsStoreBenchmark.java index 2265da29..48bcac05 100644 --- a/narayana/ArjunaJTA/jta/tests/classes/com/arjuna/ats/jta/xa/performance/InfinispanSlotsStoreBenchmark.java +++ b/narayana/ArjunaJTA/jta/tests/classes/com/arjuna/ats/jta/xa/performance/InfinispanSlotsStoreBenchmark.java @@ -7,6 +7,7 @@ import com.arjuna.ats.internal.arjuna.objectstore.slot.SlotStoreAdaptor; import com.arjuna.ats.internal.arjuna.objectstore.slot.infinispan.InfinispanSlots; +import com.arjuna.ats.internal.arjuna.objectstore.slot.infinispan.InfinispanStoreEnvironmentBean; import jakarta.transaction.HeuristicMixedException; import jakarta.transaction.HeuristicRollbackException; import jakarta.transaction.NotSupportedException; @@ -28,12 +29,12 @@ import org.openjdk.jmh.runner.options.OptionsBuilder; import org.openjdk.jmh.runner.options.TimeValue; +import java.nio.file.Paths; import java.util.concurrent.TimeUnit; @State(Scope.Benchmark) public class InfinispanSlotsStoreBenchmark extends InfinispanSlotsStoreBase { - // the name of the cluster and the shared cache used for the object store - static final String CLUSTER_NAME = "objectStoreCluster"; + static final String BM_CLASS_NAME = InfinispanSlotsStoreBenchmark.class.getSimpleName(); static Store store; static final int THREADS = 240;//10;//_000; //Threads.MAX; @@ -44,7 +45,7 @@ public class InfinispanSlotsStoreBenchmark extends InfinispanSlotsStoreBase { public static void main(String[] args) throws RunnerException { // Sometimes it is useful to run the benchmark directly from an IDE: Options opt = new OptionsBuilder() - .include(InfinispanSlotsStoreBenchmark.class.getSimpleName() + ".testWriteThroughCache") + .include(BM_CLASS_NAME + ".testInfinispanStore") .timeUnit(TimeUnit.SECONDS) .threads(THREADS) @@ -67,20 +68,29 @@ public static void main(String[] args) throws RunnerException { new Runner(opt).run(); } - @Setup(Level.Trial) public static void setup() throws Throwable { + JTAStoreBase.setup(SlotStoreAdaptor.class.getName()); + store = getStore("node1", CacheMode.REPL_SYNC, 3, null, true, false, null); - store.config().setBackingSlotsClassName(InfinispanSlots.class.getName()); - replaceEnvironmentBean(store.config()); - store.start(); + InfinispanStoreEnvironmentBean configBean = store.config(); - JTAStoreBase.setup(SlotStoreAdaptor.class.getName()); + // set the slot size, making sure it's a sensible multiple + int threadCount = getThreadCountFromProperties(THREADS); + configBean.setNumberOfSlots(roundUp(256, threadCount)); + configBean.setBackingSlotsClassName(InfinispanSlots.class.getName()); + + replaceEnvironmentBean(configBean); + + cleanStore(Paths.get(configBean.getStoreDir()).toFile()); + + store.start(); } @TearDown public static void tearDown() { store.stop(); + cleanStore(Paths.get(store.config().getStoreDir()).toFile()); } @Benchmark diff --git a/narayana/ArjunaJTA/jta/tests/classes/com/arjuna/ats/jta/xa/performance/JTAStoreBase.java b/narayana/ArjunaJTA/jta/tests/classes/com/arjuna/ats/jta/xa/performance/JTAStoreBase.java index bbd19a5d..d8cf6d92 100644 --- a/narayana/ArjunaJTA/jta/tests/classes/com/arjuna/ats/jta/xa/performance/JTAStoreBase.java +++ b/narayana/ArjunaJTA/jta/tests/classes/com/arjuna/ats/jta/xa/performance/JTAStoreBase.java @@ -18,12 +18,79 @@ import jakarta.transaction.TransactionManager; import org.jboss.logging.Logger; +import java.io.File; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + public class JTAStoreBase { + static final String JMHARGS_ENV = "JMHARGS"; // for overriding the number of THREADS + protected static final Logger log = Logger.getLogger(JTAStoreBase.class); private static XAResourceImpl resource1; private static XAResourceImpl resource2; private static TransactionManager tm; + protected static int getThreadCountFromProperties(int defaultThreadCount) { + int threadCount = defaultThreadCount; + String jmhArgs = System.getenv(JMHARGS_ENV); + + if (jmhArgs != null) { + /* + * The number of slots should equal the maximum number of unresolved transactions expected at any given time, + * including those in-flight and awaiting recovery so update the size based on the number of threads + * used to run the benchmark. + */ + // BeforeClass methods are not benchmarked so compile the pattern here + String pat = "-t\\s+(\\d+)"; + Pattern pattern = Pattern.compile(pat); + Matcher matcher = pattern.matcher(jmhArgs); + if (matcher.find()) { + try { + threadCount = Integer.parseInt(matcher.group(1)); + } catch (NumberFormatException e) { + System.err.printf("JMHARGS args -t option (%s) invalid%n", jmhArgs); + } + } + + System.out.printf("JMHARGS=%s%nUsing %d threads%n", jmhArgs, threadCount); + } + + return threadCount; + } + + protected static void cleanStore(File storeDir) { + try { + // clean up the storage otherwise it may interfere with the next run + if (!purgeFiles(storeDir)) { + System.err.printf("problem removing slot store file storage (%s)%n", storeDir); + } + } catch (Exception e) { + System.err.printf("Warn: problem cleaning the object store (%s): %s%n", storeDir, e.getMessage()); + } + } + + protected static boolean purgeFiles(File storeDir) { + File[] files = storeDir.listFiles(); + + if (files != null) { + for (File file : files) { + purgeFiles(file); + } + } + + return storeDir.delete(); + } + + protected static int roundUp(int increment, int value) { + int res = value % increment; + // round up to nearest multiple of increment (eg a h/w page size) + if (res == 0) { + return value; + } else { + return value + increment - res; + } + } + protected static void setup(String storeType) throws CoreEnvironmentBeanException { BeanPopulator.getDefaultInstance(CoreEnvironmentBean.class).setNodeIdentifier("1"); BeanPopulator.getDefaultInstance(ObjectStoreEnvironmentBean.class).setObjectStoreType(storeType);