Skip to content

fix(net): fix RejectedExecutionException during shutdown trxHandlePool#8

Open
0xbigapple wants to merge 1 commit intodevelopfrom
fix/threadpool-submit-issue
Open

fix(net): fix RejectedExecutionException during shutdown trxHandlePool#8
0xbigapple wants to merge 1 commit intodevelopfrom
fix/threadpool-submit-issue

Conversation

@0xbigapple
Copy link
Copy Markdown
Owner

@0xbigapple 0xbigapple commented Mar 30, 2026

What does this PR do?
fix RejectedExecutionException during shutdown trxHandlePool

Why are these changes required?

This PR has been tested by:

  • Unit Tests
  • Manual Testing

Follow up

Extra details


Summary by cubic

Prevents RejectedExecutionException during shutdown by blocking new submissions, stopping the scheduler before the worker pool, and clearing pending queues for a clean close. New messages are ignored after close; mid-batch processing stops safely; only pending tasks are dropped.

  • Bug Fixes
    • Gate submissions with isClosed (early return in processMessage, re-check during iteration).
    • Shutdown sequence and cleanup: stop smartContractExecutor before trxHandlePool; clear smartContractQueue and internal queue.
    • Robust submit and tests: catch RejectedExecutionException and stop on first failure; add tests for post-close ignore, mid-batch close, rejected submit, valid tx broadcast, and BAD_TRX → mark peer bad + disconnect with BAD_TX.

Written for commit 2e3164c. Summary will update on new commits.

Summary by CodeRabbit

  • Refactor

    • Improved shutdown behavior to stop new message processing, drain and clear pending queues, and avoid submitting work once shutdown begins; added safeguards for handling task rejections to reduce runtime disruptions and ensure cleaner peer handling when invalid transactions are detected.
  • Tests

    • Added unit tests covering shutdown timing, rejected task submissions, early-stop during processing, transaction handling paths, and peer disconnect behavior to ensure reliability.

@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 30, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4d394067-01ae-4ce1-a74a-9255e54a2667

📥 Commits

Reviewing files that changed from the base of the PR and between 595f6a8 and 2e3164c.

📒 Files selected for processing (2)
  • framework/src/main/java/org/tron/core/net/messagehandler/TransactionsMsgHandler.java
  • framework/src/test/java/org/tron/core/net/messagehandler/TransactionsMsgHandlerTest.java
🚧 Files skipped from review as they are similar to previous changes (2)
  • framework/src/main/java/org/tron/core/net/messagehandler/TransactionsMsgHandler.java
  • framework/src/test/java/org/tron/core/net/messagehandler/TransactionsMsgHandlerTest.java

📝 Walkthrough

Walkthrough

Added a closed-state flag to TransactionsMsgHandler that blocks new message processing and task submissions during shutdown; adjusted shutdown order to stop the smart-contract scheduler before the transaction worker pool and added handling for RejectedExecutionException. New unit tests exercise lifecycle and transaction handling behaviors.

Changes

Cohort / File(s) Summary
Handler lifecycle & shutdown
framework/src/main/java/org/tron/core/net/messagehandler/TransactionsMsgHandler.java
Added a volatile isClosed flag; close() sets the flag, shuts down the smart-contract scheduler before the transaction worker pool, and clears internal queues. processMessage(...) immediately drops messages when closed and stops submitting tasks if closed; non-SC task submissions catch RejectedExecutionException, log a warning, and break the loop.
Unit tests for lifecycle & transaction handling
framework/src/test/java/org/tron/core/net/messagehandler/TransactionsMsgHandlerTest.java
Added tests: testProcessMessageAfterClose, testRejectedExecution, testCloseDuringProcessing, testHandleTransaction, plus helper builders/stubs. Tests inject mocks (including via reflection) to verify closed-state behavior, rejected submissions, early-stop during processing, and peer handling on bad transactions.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I thump a paw when shutdown's nigh,

queues clear, new work says bye-bye.
Tasks bow out, the schedulers cease,
Rejections logged, the system finds peace.
Hooray — one handler hops with ease! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main fix: addressing RejectedExecutionException that occurs during shutdown of the transaction handler pool.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/threadpool-submit-issue

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 and usage tips.

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

No issues found across 2 files

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes shutdown-time RejectedExecutionException in TransactionsMsgHandler by preventing new task submissions after close and changing executor shutdown ordering to stop the scheduler before the worker pool.

Changes:

  • Add an isClosed flag and guard processMessage/iteration to stop submitting work during shutdown.
  • Change shutdown order to terminate smartContractExecutor before trxHandlePool, and catch RejectedExecutionException on submit.
  • Add a unit test to ensure processMessage() does not throw after close().

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
framework/src/main/java/org/tron/core/net/messagehandler/TransactionsMsgHandler.java Adds close-gating and adjusts executor shutdown order to avoid rejected submissions during shutdown.
framework/src/test/java/org/tron/core/net/messagehandler/TransactionsMsgHandlerTest.java Adds a regression test ensuring processMessage is safe after handler close.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +97 to +101
// Re-check isClosed status during iteration to handle concurrent shutdown
if (isClosed) {
logger.warn("TransactionsMsgHandler is closed during processing, stop submit.");
break;
}
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

When isClosed flips to true (or trxHandlePool starts rejecting), this loop breaks early, but the earlier loop has already removed all tx items from peer.getAdvInvRequest(). That can leave some transactions neither queued nor requested anymore. To avoid silently dropping txs, consider removing items from advInvRequest only after a tx is successfully enqueued/submitted (or stop removing once shutdown is detected).

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@0xbigapple 0xbigapple force-pushed the fix/threadpool-submit-issue branch from 4d5d454 to 595f6a8 Compare April 16, 2026 11:32
@0xbigapple
Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 16, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
framework/src/test/java/org/tron/core/net/messagehandler/TransactionsMsgHandlerTest.java (1)

154-206: Add cleanup to avoid leaking executor threads.

The test creates a TransactionsMsgHandler whose field initializers instantiate real thread pools (trxHandlePool and smartContractExecutor). Without calling close(), these executors remain running after the test completes, potentially causing resource leaks or test pollution.

🧹 Proposed fix to add cleanup
 `@Test`
 public void testHandleTransaction() throws Exception {
   TransactionsMsgHandler handler = new TransactionsMsgHandler();
+  try {
     TronNetDelegate tronNetDelegate = Mockito.mock(TronNetDelegate.class);
     // ... existing test code ...
     Mockito.verify(peer).setBadPeer(true);
     Mockito.verify(peer).disconnect(Protocol.ReasonCode.BAD_TX);
+  } finally {
+    handler.close();
+  }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@framework/src/test/java/org/tron/core/net/messagehandler/TransactionsMsgHandlerTest.java`
around lines 154 - 206, The test leaks executor threads because
TransactionsMsgHandler's field initializers create real thread pools
(trxHandlePool and smartContractExecutor) that are not shut down; fix by
ensuring these are closed after the test—either call handler.close() (or if no
public close exists, reflectively get and shutdown the trxHandlePool and
smartContractExecutor fields) in a finally block at the end of
testHandleTransaction or move cleanup into an `@After` method to always shut down
those executors; reference TransactionsMsgHandler, trxHandlePool,
smartContractExecutor, and close()/shutdown() when applying the change.
framework/src/main/java/org/tron/core/net/messagehandler/TransactionsMsgHandler.java (1)

136-151: Consider adding try/catch for consistency (optional).

While the shutdown order guarantees trxHandlePool is still running when this method executes, adding a try/catch for RejectedExecutionException would make the code more defensive and consistent with processMessage().

🔧 Optional defensive improvement
 private void handleSmartContract() {
   ExecutorServiceManager.scheduleWithFixedDelay(smartContractExecutor, () -> {
     try {
       while (queue.size() < MAX_SMART_CONTRACT_SUBMIT_SIZE && smartContractQueue.size() > 0) {
         TrxEvent event = smartContractQueue.take();
-        ExecutorServiceManager.submit(
-            trxHandlePool, () -> handleTransaction(event.getPeer(), event.getMsg()));
+        try {
+          ExecutorServiceManager.submit(
+              trxHandlePool, () -> handleTransaction(event.getPeer(), event.getMsg()));
+        } catch (RejectedExecutionException e) {
+          logger.warn("Submit task to {} failed during shutdown", trxEsName);
+          break;
+        }
       }
     } catch (InterruptedException e) {
       logger.warn("Handle smart server interrupted");
       Thread.currentThread().interrupt();
     } catch (Exception e) {
       logger.error("Handle smart contract exception", e);
     }
   }, 1000, 20, TimeUnit.MILLISECONDS);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@framework/src/main/java/org/tron/core/net/messagehandler/TransactionsMsgHandler.java`
around lines 136 - 151, The scheduled task in handleSmartContract submits jobs
to trxHandlePool without guarding for RejectedExecutionException; wrap the
ExecutorServiceManager.submit(trxHandlePool, () ->
handleTransaction(event.getPeer(), event.getMsg())) call in its own try/catch
that catches RejectedExecutionException and logs a warning (including
identifying info such as event.getPeer() or event) so the failure is visible and
the loop can continue; keep existing InterruptedException and generic Exception
handlers as-is.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@framework/src/main/java/org/tron/core/net/messagehandler/TransactionsMsgHandler.java`:
- Around line 136-151: The scheduled task in handleSmartContract submits jobs to
trxHandlePool without guarding for RejectedExecutionException; wrap the
ExecutorServiceManager.submit(trxHandlePool, () ->
handleTransaction(event.getPeer(), event.getMsg())) call in its own try/catch
that catches RejectedExecutionException and logs a warning (including
identifying info such as event.getPeer() or event) so the failure is visible and
the loop can continue; keep existing InterruptedException and generic Exception
handlers as-is.

In
`@framework/src/test/java/org/tron/core/net/messagehandler/TransactionsMsgHandlerTest.java`:
- Around line 154-206: The test leaks executor threads because
TransactionsMsgHandler's field initializers create real thread pools
(trxHandlePool and smartContractExecutor) that are not shut down; fix by
ensuring these are closed after the test—either call handler.close() (or if no
public close exists, reflectively get and shutdown the trxHandlePool and
smartContractExecutor fields) in a finally block at the end of
testHandleTransaction or move cleanup into an `@After` method to always shut down
those executors; reference TransactionsMsgHandler, trxHandlePool,
smartContractExecutor, and close()/shutdown() when applying the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a71c64cd-51ca-44f4-921c-93b3f371bb29

📥 Commits

Reviewing files that changed from the base of the PR and between 208807d and 595f6a8.

📒 Files selected for processing (2)
  • framework/src/main/java/org/tron/core/net/messagehandler/TransactionsMsgHandler.java
  • framework/src/test/java/org/tron/core/net/messagehandler/TransactionsMsgHandlerTest.java

@0xbigapple 0xbigapple force-pushed the fix/threadpool-submit-issue branch from 595f6a8 to b530d30 Compare April 16, 2026 12:08
@0xbigapple
Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 17, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +79 to 83
if (isClosed) {
logger.warn("TransactionsMsgHandler is closed, drop message");
return;
}
TransactionsMessage transactionsMessage = (TransactionsMessage) msg;
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

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

processMessage returns immediately when isClosed is true, before removing the received transactions from peer.getAdvInvRequest(). If close() is called while peers are still connected, those request entries can linger and later trigger TIME_OUT disconnects in PeerStatusCheck even though the peer actually responded. Consider still doing the minimal request cleanup (remove advInvRequest entries for txs in the message) while skipping validation/submission when closed, or clearing relevant state as part of shutdown.

Suggested change
if (isClosed) {
logger.warn("TransactionsMsgHandler is closed, drop message");
return;
}
TransactionsMessage transactionsMessage = (TransactionsMessage) msg;
TransactionsMessage transactionsMessage = (TransactionsMessage) msg;
if (isClosed) {
for (Transaction trx : transactionsMessage.getTransactions().getTransactionsList()) {
Item item = new Item(new TransactionMessage(trx).getMessageId(), InventoryType.TRX);
peer.getAdvInvRequest().remove(item);
}
logger.warn("TransactionsMsgHandler is closed, drop message");
return;
}

Copilot uses AI. Check for mistakes.
ExecutorServiceManager.submit(
trxHandlePool, () -> handleTransaction(peer, new TransactionMessage(trx)));
} catch (RejectedExecutionException e) {
logger.warn("Submit task to {} failed", trxEsName);
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

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

The RejectedExecutionException catch logs only a generic message and drops the exception details. Including e (or at least e.getMessage()) in the log will make shutdown/rejection issues diagnosable in production, especially since this path is explicitly handling a failure mode.

Suggested change
logger.warn("Submit task to {} failed", trxEsName);
logger.warn("Submit task to {} failed", trxEsName, e);

Copilot uses AI. Check for mistakes.
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.

2 participants