Skip to content

Fix GraalJS resource lifecycle in ScriptMediator to prevent heap growth#2568

Open
ochnios wants to merge 1 commit into
wso2:masterfrom
ochnios:fix/script_mediator_graaljs_resource_lifecycle
Open

Fix GraalJS resource lifecycle in ScriptMediator to prevent heap growth#2568
ochnios wants to merge 1 commit into
wso2:masterfrom
ochnios:fix/script_mediator_graaljs_resource_lifecycle

Conversation

@ochnios

@ochnios ochnios commented Jun 15, 2026

Copy link
Copy Markdown

Purpose

This PR aims to resolve wso2/product-integrator-mi#4949

A custom synapse-extensions build with this change applied no longer reproduces the problem in my validation.

Goals

Ensure GraalJS resources are released after each mediation so repeated invocations do not lead to heap growth and eventual OOM.

Approach

The observed leak pattern points to a per-invocation resource lifecycle issue in ScriptMediator. The underlying problem is likely deeper in the Graal engine layer and became visible after the Graal version upgrade on the Micro Integrator side (22.3.4 -> 25.0.2). It is also possible that the leak was introduced elsewhere in the wso2-synapse stack but I was not able to identify the exact root cause.

The fixes applied:

  • Context.leave() is still called for GraalJS, but the context is now also closed afterwards.
  • Inline script bindings are closed.

Further enhancements

This fix is intentionally minimal, potential follow-up improvements include sharing a Graal Engine, adding ManagedLifecycle cleanup on undeploy/redeploy, and closing overflow engines when the pool drops them.

@ochnios ochnios requested a review from chanikag as a code owner June 15, 2026 13:24
@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1f330fed-55c2-4011-806d-05a134ced6db

📥 Commits

Reviewing files that changed from the base of the PR and between 67ee70a and f2f2d5a.

📒 Files selected for processing (2)
  • modules/extensions/src/main/java/org/apache/synapse/mediators/bsf/ScriptMediator.java
  • modules/extensions/src/test/java/org/apache/synapse/mediators/bsf/javascript/GraalVMJavaScriptMediatorTest.java

📝 Walkthrough

Description

This PR resolves a resource lifecycle issue in the ScriptMediator that causes heap growth when using GraalJS, which became apparent after the Micro Integrator upgraded to Graal version 25.0.2. The issue manifests as steady memory accumulation leading to OutOfMemoryError under sustained load, even with minimal scripts.

Changes

ScriptMediator lifecycle management

The ScriptMediator now performs explicit cleanup of Graal scripting contexts and inline-script bindings:

  • For GraalJS/Rhino contexts, the entered Context is now properly closed after Context.leave() is called, ensuring complete resource release after each mediation cycle
  • Inline script bindings are explicitly closed after evaluation through a try/finally block
  • A new closeQuietly() helper method safely closes any AutoCloseable resources and logs failures gracefully

Test coverage

Two new test cases verify the behavior of inline mediators with Graal contexts across multiple mediations:

  • testInlineMediatorWithRawJsObjectAcrossMediators(): Validates that raw JavaScript objects stored in MessageContext become inaccessible after context cleanup
  • testInlineMediatorWithPrimitivesAcrossMediators(): Validates that primitive values and strings remain accessible across mediations

Impact

These changes ensure GraalJS resources are properly released after each invocation, preventing repeated calls from accumulating memory overhead. The fix has been validated in a custom build and resolves the reported OutOfMemoryError issues under sustained load.

Walkthrough

ScriptMediator adds explicit resource cleanup for GraalVM and JSR-223 scripting contexts. In invokeScript, after context.leave() for JAVA_SCRIPT/GRAAL_JAVA_SCRIPT, a new closeQuietly(context) call is made. In mediateForInlineScript, script evaluation is now wrapped in a try/finally block that closes the created Bindings via closeQuietly. A private closeQuietly(Object) helper casts to AutoCloseable and logs warnings on failure without propagating exceptions. The mediateWithExternalScript finally block's pool-return guard is reformatted to an explicit if (sew != null) check. Two new tests are added to GraalVMJavaScriptMediatorTest verifying cross-mediator property behavior after context release.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 62.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: fixing GraalJS resource lifecycle in ScriptMediator to prevent heap growth, directly addressing the core issue in the PR.
Description check ✅ Passed The description includes all essential sections: Purpose (with issue reference), Goals, Approach with specific implementation details, and supporting context about the problem and validation.
Linked Issues check ✅ Passed The code changes address the resource lifecycle requirements from issue #4949: closing GraalJS contexts after mediation and releasing inline script bindings to prevent heap growth under sustained load.
Out of Scope Changes check ✅ Passed All changes are directly related to the stated objectives: ScriptMediator resource cleanup and test additions to validate the fix, with no extraneous modifications.

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

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

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.

[MI 4.6.0] OutOfMemoryError when using inline Script mediator with GraalJS after upgrading

1 participant