Skip to content

JBTM-4038 JGroupsSlots and JGroupsRaftSlots benchmarks#239

Open
mmusgrov wants to merge 3 commits into
jbosstm:mainfrom
mmusgrov:JBTM-4038
Open

JBTM-4038 JGroupsSlots and JGroupsRaftSlots benchmarks#239
mmusgrov wants to merge 3 commits into
jbosstm:mainfrom
mmusgrov:JBTM-4038

Conversation

@mmusgrov

@mmusgrov mmusgrov commented Jul 20, 2026

Copy link
Copy Markdown
Member

https://redhat.atlassian.net/browse/JBTM-4038

NARAYANA_REPO=mmusgrov
NARAYANA_BRANCH=JBTM-4038
NY_BRANCH=JBTM-4038
THREAD_COUNTS=1 24 240
COMPARE_STORES=y
COMPARE_IMPLEMENTATIONS=n
COMPARE_TRANSPORTS=n
COMPARE_JOURNAL_PARAMETERS=n

JDK25

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds JGroups and JGroups Raft configurations, Maven dependency wiring, three JMH slot-store benchmarks, transaction rollback cleanup, and updates benchmark CSV row-count validation for the expanded benchmark set.

Changes

JGroups benchmark support

Layer / File(s) Summary
JGroups and Raft configuration
narayana/ArjunaJTA/jta/pom.xml, narayana/ArjunaJTA/jta/etc/jgroups.xml, narayana/ArjunaJTA/jta/etc/jgroups-raft.xml
Adds JGroups protocol configurations and an optional, versioned JGroups Raft dependency.
Benchmark setup and execution
narayana/ArjunaJTA/jta/tests/classes/com/arjuna/ats/jta/xa/performance/JGroupsSlotsStoreBenchmark.java, narayana/ArjunaJTA/jta/tests/classes/com/arjuna/ats/jta/xa/performance/JGroupsRaftSlotsStoreBenchmark.java
Adds JMH entry points, store configuration, WAL or Raft setup, lifecycle cleanup, and benchmark methods for both slot-store implementations.
Raft cluster benchmark
narayana/ArjunaJTA/jta/tests/classes/com/arjuna/ats/jta/xa/performance/JGroupsRaftClusterStoreBenchmark.java
Adds a three-node Raft benchmark covering leader election, replicated slot initialization, follower reads, and leader writes.
Transaction cleanup
narayana/ArjunaJTA/jta/tests/classes/com/arjuna/ats/jta/xa/performance/JTAStoreBase.java
Preserves transaction exceptions while attempting explicit and best-effort rollback during benchmark failures.
Benchmark result validation
.github/scripts/benchmark.sh
Changes the expected result count for the store benchmark pattern from five to eight.

Sequence Diagram(s)

sequenceDiagram
  participant JMHRunner
  participant RaftChannels
  participant JGroupsRaftSlots
  participant Leader
  participant Follower
  JMHRunner->>RaftChannels: create and connect three channels
  RaftChannels->>RaftChannels: elect leader
  JMHRunner->>JGroupsRaftSlots: initialize node slot stores
  Leader->>Follower: replicate pre-populated slots
  JMHRunner->>Follower: readFromFollower()
  JMHRunner->>Leader: writeToLeader()
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the JBTM-4038 benchmarks added for JGroupsSlots and JGroupsRaftSlots.
Description check ✅ Passed The description covers the Jira issue, key env vars, thread counts, comparison flags, and JDK selection.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/scripts/benchmark.sh:
- Line 113: Update BM5’s JMH selector pattern so it matches the newly added
JGroupsSlotsBenchmark and JGroupsRaftSlotsBenchmark benchmarks, including their
testJGroupsSlotsStore and testJGroupsRaftSlotsStore methods, while retaining the
existing StoreBenchmark matches and expected count of 7.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 6999bbdf-b1be-4c03-ae87-b1357153804c

📥 Commits

Reviewing files that changed from the base of the PR and between 2c4e9ac and 7615a43.

📒 Files selected for processing (6)
  • .github/scripts/benchmark.sh
  • narayana/ArjunaJTA/jta/etc/jgroups-raft.xml
  • narayana/ArjunaJTA/jta/etc/jgroups.xml
  • narayana/ArjunaJTA/jta/pom.xml
  • narayana/ArjunaJTA/jta/tests/classes/com/arjuna/ats/jta/xa/performance/JGroupsRaftSlotsBenchmark.java
  • narayana/ArjunaJTA/jta/tests/classes/com/arjuna/ats/jta/xa/performance/JGroupsSlotsBenchmark.java

Comment thread .github/scripts/benchmark.sh Outdated
@mmusgrov
mmusgrov force-pushed the JBTM-4038 branch 2 times, most recently from d21b12d to c0a91a7 Compare July 21, 2026 16:05

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/scripts/benchmark.sh:
- Line 113: Update the BM5 benchmark selector configuration to use a lower bound
of 9 instead of 8, ensuring all four added StoreBenchmark methods are required
while preserving the existing selector.

In
`@narayana/ArjunaJTA/jta/tests/classes/com/arjuna/ats/jta/xa/performance/JGroupsRaftSlotsStoreBenchmark.java`:
- Around line 90-115: Remove the stale “configure 3 node cluster” TODO from the
setup method in JGroupsRaftSlotsStoreBenchmark, since
JGroupsRaftClusterStoreBenchmark now covers that configuration. Leave the
existing single-node benchmark setup unchanged.

In
`@narayana/ArjunaJTA/jta/tests/classes/com/arjuna/ats/jta/xa/performance/JTAStoreBase.java`:
- Around line 113-127: Update the transaction setup and cleanup around
tm.begin() in JTAStoreBase so rollback occurs only when this method successfully
started the transaction. Track that state with a local transactionStarted flag,
set it after begin succeeds, and guard both the catch-path rollback and
finally-block rollback with it, preserving suppressed-exception handling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 5676c249-55bc-4a74-9f89-97b5e1e0fa1b

📥 Commits

Reviewing files that changed from the base of the PR and between 3a97f23 and 5af708c.

📒 Files selected for processing (8)
  • .github/scripts/benchmark.sh
  • narayana/ArjunaJTA/jta/etc/jgroups-raft.xml
  • narayana/ArjunaJTA/jta/etc/jgroups.xml
  • narayana/ArjunaJTA/jta/pom.xml
  • narayana/ArjunaJTA/jta/tests/classes/com/arjuna/ats/jta/xa/performance/JGroupsRaftClusterStoreBenchmark.java
  • narayana/ArjunaJTA/jta/tests/classes/com/arjuna/ats/jta/xa/performance/JGroupsRaftSlotsStoreBenchmark.java
  • narayana/ArjunaJTA/jta/tests/classes/com/arjuna/ats/jta/xa/performance/JGroupsSlotsStoreBenchmark.java
  • narayana/ArjunaJTA/jta/tests/classes/com/arjuna/ats/jta/xa/performance/JTAStoreBase.java

BM4d="ArjunaJTA/jta io.narayana.perf.product.AtomikosComparison.* 1"
#ArjunaJTA/jta/tests/classes/io/narayana/perf/product/ProductComparison.java
BM5="ArjunaJTA/jta com.arjuna.ats.jta.xa.performance.*StoreBenchmark.* 5"
BM5="ArjunaJTA/jta com.arjuna.ats.jta.xa.performance.*StoreBenchmark.* 8"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Set the lower bound to 9.

This selector now matches four added benchmark methods, so the prior lower bound of 5 should become 9. At 8, CI will still pass if any one new benchmark result is missing.

-BM5="ArjunaJTA/jta com.arjuna.ats.jta.xa.performance.*StoreBenchmark.* 8"
+BM5="ArjunaJTA/jta com.arjuna.ats.jta.xa.performance.*StoreBenchmark.* 9"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
BM5="ArjunaJTA/jta com.arjuna.ats.jta.xa.performance.*StoreBenchmark.* 8"
BM5="ArjunaJTA/jta com.arjuna.ats.jta.xa.performance.*StoreBenchmark.* 9"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/scripts/benchmark.sh at line 113, Update the BM5 benchmark selector
configuration to use a lower bound of 9 instead of 8, ensuring all four added
StoreBenchmark methods are required while preserving the existing selector.

Comment on lines +90 to +115
@Setup(Level.Trial)
@BeforeClass
public static void setup() throws CoreEnvironmentBeanException {
JTAStoreBase.setup(SlotStoreAdaptor.class.getName());
JGroupsRaftStoreEnvironmentBean configBean = BeanPopulator.getDefaultInstance(JGroupsRaftStoreEnvironmentBean.class);
BeanPopulator.getDefaultInstance(SlotStoreEnvironmentBean.class).setBackingSlotsClassName(JGroupsRaftSlots.class.getName());
int threadCount = getThreadCountFromProperties(THREADS);

// JGroups configuration
configBean.setJGroupsConfigFileName("jgroups-raft.xml");
configBean.setNodeAddress("raft-benchmark-node"); // TODO configure 3 node cluster
configBean.setClusterName("jgroups-raft-benchmark-" + System.currentTimeMillis());

// Note: Using default key generator (Uid-based) for single-node benchmark

// Raft configuration (single-node cluster for benchmark)
configBean.setRaftMembers("raft-benchmark-node"); // Single node
configBean.setRaftLogFsync(true); // durable writes for fault tolerance
configBean.setAllowDirtyReads(false);

// Set the slot size, making sure it's a sensible multiple
configBean.setNumberOfSlots(roundUp(256, threadCount));
configBean.setBackingSlotsClassName(JGroupsRaftSlots.class.getName());

cleanStore(Paths.get(configBean.getStoreDir()).toFile());
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Stale TODO — 3-node cluster is now covered by JGroupsRaftClusterStoreBenchmark.

The // TODO configure 3 node cluster comment predates (or overlaps with) JGroupsRaftClusterStoreBenchmark, which already stands up a 3-node Raft cluster in this same PR. Update or remove the TODO so readers aren't pointed at unfinished work that has, in fact, been done elsewhere.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@narayana/ArjunaJTA/jta/tests/classes/com/arjuna/ats/jta/xa/performance/JGroupsRaftSlotsStoreBenchmark.java`
around lines 90 - 115, Remove the stale “configure 3 node cluster” TODO from the
setup method in JGroupsRaftSlotsStoreBenchmark, since
JGroupsRaftClusterStoreBenchmark now covers that configuration. Leave the
existing single-node benchmark setup unchanged.

Comment on lines +113 to +127
log.warn("JTAStoreTests#jtaTest%n", e);
try {
tm.getTransaction().rollback();
} catch (Exception suppressed) {
e.addSuppressed(suppressed);
}
throw e;
} finally {
Transaction txn = tm.getTransaction();
if (txn != null) {
try {
txn.rollback();
} catch (Exception ignored) {
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

sed -n '1,220p' narayana/ArjunaJTA/jta/tests/classes/com/arjuna/ats/jta/xa/performance/JTAStoreBase.java

Repository: jbosstm/performance

Length of output: 4869


🏁 Script executed:

grep -Rni "class JTAStoreBase\|void jtaTest\|tm.begin\|getTransaction().rollback" narayana/ArjunaJTA/jta/tests/classes/com/arjuna/ats/jta/xa/performance/JTAStoreBase.java

Repository: jbosstm/performance

Length of output: 268


🏁 Script executed:

python3 - <<'PY'
from pathlib import Path
p = Path('narayana/ArjunaJTA/jta/tests/classes/com/arjuna/ats/jta/xa/performance/JTAStoreBase.java')
for i, line in enumerate(p.read_text().splitlines(), 1):
    if 90 <= i <= 150:
        print(f"{i:4}: {line}")
PY

Repository: jbosstm/performance

Length of output: 1821


🌐 Web query:

Jakarta Transaction TransactionManager.begin throws NotSupportedException existing transaction getTransaction rollback semantics caller-owned transaction

💡 Result:

In the Jakarta Transactions API, the TransactionManager.begin method throws a NotSupportedException when the calling thread is already associated with an active transaction and the underlying transaction manager implementation does not support nested transactions [1][2]. By design, the standard Jakarta EE transaction manager does not support nested transactions [3]. If a thread is already associated with a transaction, the manager requires that the existing transaction be completed (committed or rolled back) before a new one can be started [3]. Regarding caller-owned transaction and rollback semantics: 1. Ownership and Association: The Jakarta Transactions specification associates transaction context with the calling thread [2][4]. When a thread is associated with a transaction, that transaction is considered the active context for that thread [2]. 2. Rollback Semantics: The TransactionManager.rollback method is specifically designed to roll back the transaction currently associated with the calling thread [2][5]. Once the rollback completes, the thread is no longer associated with that transaction [2][5]. 3. External Control: Because Jakarta Transactions operates based on thread association, if a "caller-owned" transaction (e.g., one started by a parent component) is active on the current thread, any subsequent code—including nested calls—operates within that same transaction context unless it is explicitly suspended [2][4]. A caller cannot typically "own" a transaction in a way that prevents the current thread from interacting with it if that transaction is currently associated with that thread [2]. If the calling code needs to prevent interference or handle transaction boundaries differently, it must manage suspension or ensure that the existing transaction is properly concluded before initiating a new one [2][3].

Citations:


🏁 Script executed:

rg -n "jtaTest\(" narayana/ArjunaJTA/jta/tests/classes -g '!**/target/**'

Repository: jbosstm/performance

Length of output: 1912


🏁 Script executed:

python3 - <<'PY'
from pathlib import Path
for p in Path('narayana/ArjunaJTA/jta/tests/classes').rglob('*.java'):
    txt = p.read_text(errors='ignore')
    if 'jtaTest(' in txt and p.name != 'JTAStoreBase.java':
        for i, line in enumerate(txt.splitlines(), 1):
            if 'jtaTest(' in line:
                print(f"{p}:{i}:{line.strip()}")
PY

Repository: jbosstm/performance

Length of output: 1594


🏁 Script executed:

sed -n '1,220p' narayana/ArjunaJTA/jta/tests/classes/com/arjuna/ats/jta/xa/performance/JTAStoreBase.java

Repository: jbosstm/performance

Length of output: 4869


🏁 Script executed:

python3 - <<'PY'
from pathlib import Path
for p in Path('narayana/ArjunaJTA/jta/tests/classes').rglob('*.java'):
    txt = p.read_text(errors='ignore').splitlines()
    for i, line in enumerate(txt, 1):
        if 'jtaTest(' in line and 'JTAStoreBase' not in str(p):
            print(f"{p}:{i}:{line.strip()}")
PY

Repository: jbosstm/performance

Length of output: 1594


🌐 Web query:

Jakarta Transactions TransactionManager.getTransaction after begin throws NotSupportedException existing associated transaction remains active rollback current thread

💡 Result:

In the Jakarta Transactions (JTA) specification, the NotSupportedException is thrown by the TransactionManager.begin method when a thread is already associated with a transaction and the underlying transaction manager implementation does not support nested transactions [1][2][3]. When this exception is thrown, the existing transaction associated with the thread remains active and is not automatically rolled back by the TransactionManager.begin call [2][3][4]. The exception indicates that the request to start a new transaction cannot be fulfilled [5]; it does not imply that the current transactional context has been invalidated or ended [2][3]. To handle this situation, the developer is responsible for managing the state of the existing transaction. Since the original transaction remains active, you may choose to: 1. Continue using the existing transaction (if the logic allows). 2. Explicitly roll back the existing transaction using the TransactionManager.rollback method before attempting further operations, if the failure to start a new nested transaction renders the current unit of work invalid [6][7]. The TransactionManager.getTransaction method will continue to return the Transaction object representing the pre-existing, still-active transaction context [1][8][9]. If you require a new, independent transaction context, you must ensure that the current transaction is completed (committed or rolled back) and dissociated from the thread (typically via suspension or completion) before calling begin again [2][3][6].

Citations:


🏁 Script executed:

python3 - <<'PY'
from pathlib import Path
base = Path('narayana/ArjunaJTA/jta/tests/classes/com/arjuna/ats/jta/xa/performance')
for p in base.rglob('*.java'):
    txt = p.read_text(errors='ignore')
    if p.name != 'JTAStoreBase.java' and ('extends JTAStoreBase' in txt or 'jtaTest()' in txt):
        print(f"\n{p}")
        for i, line in enumerate(txt.splitlines(), 1):
            if 'extends JTAStoreBase' in line or 'jtaTest()' in line:
                print(f"{i}: {line}")
PY

Repository: jbosstm/performance

Length of output: 2110


Only roll back the transaction started here
If tm.begin() throws NotSupportedException because the thread is already associated with a transaction, tm.getTransaction() still returns that pre-existing transaction. Both cleanup paths can then abort work owned by the caller; gate rollback on a local transactionStarted flag.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@narayana/ArjunaJTA/jta/tests/classes/com/arjuna/ats/jta/xa/performance/JTAStoreBase.java`
around lines 113 - 127, Update the transaction setup and cleanup around
tm.begin() in JTAStoreBase so rollback occurs only when this method successfully
started the transaction. Track that state with a local transactionStarted flag,
set it after begin succeeds, and guard both the catch-path rollback and
finally-block rollback with it, preserving suppressed-exception handling.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant