Skip to content

ci: add Integration Smoke workflow#5

Open
warku123 wants to merge 10 commits into
developfrom
feat/ci-integration-smoke
Open

ci: add Integration Smoke workflow#5
warku123 wants to merge 10 commits into
developfrom
feat/ci-integration-smoke

Conversation

@warku123
Copy link
Copy Markdown
Owner

@warku123 warku123 commented May 11, 2026

Summary

  • Adds .github/workflows/integration-test.yml triggered on PR (open/sync/reopen) to develop and release_**.
  • Builds FullNode.jar with JDK 8, pulls warku123/java-tron-integration-test:latest, runs --smoke --clean against the freshly-built jar.
  • Test reports are extracted from the container via docker cp and uploaded as a build artifact.
  • JAVA_HOME overridden inside the container to JDK 8 (production parity for java.version assertions); JAVA_HOME_17 keeps Gradle on JDK 17.

Test plan

  • This PR's own CI run goes green (or fails with clear log we can iterate on)
  • Verify the uploaded integration-smoke-report artifact contains reports/, test-results/, and node.log
  • Confirm total wall-clock time is reasonable (~10-13 min)

Summary by cubic

Adds a CI workflow that builds FullNode.jar with JDK 8 and runs the full single-node tests on PRs. Also adds a blocking/non-blocking switch for API rate limiting, fixes --es startup ordering, and rejects old event‑plugin versions.

  • New Features

    • CI: triggers on PR open/sync/reopen to develop/release_** and manual dispatch; cancels duplicates; builds FullNode.jar; pulls and runs troninfra/troninfra-ci:latest with --clean (no DockerHub login); uploads reports/, test-results/, test-output.log, node.log, and tron.log as integration-test-report; caches Gradle; 45‑min timeout.
    • Rate limiting: adds rate.limiter.apiNonBlocking (default false) to choose blocking vs immediate-reject; introduces acquirePermit on per-endpoint/global limiters; preserves per-endpoint‑before‑global and IP‑before‑global ordering; tests cover routing and failure cases.
    • Event plugin: enforce minimum event-plugin version >= 3.0.0 at startup.
  • Bug Fixes

    • --es now applies after config so event plugin config builds when only the CLI flag enables subscribe.
    • Default event.subscribe.native.useNativeQueue restored to false.
    • Null‑safe relay node check in PeerConnection.

Written for commit ca60f0f. Summary will update on new commits. Review in cubic

Summary by CodeRabbit

  • Chores

    • Added automated integration test workflow to run on pull requests and publish test reports.
  • New Features

    • Introduced a configurable rate-limiter mode allowing blocking or non-blocking API behavior (default: blocking).
    • Added plugin version gating to enforce a minimum supported event plugin version.
  • Bug Fixes / Stability

    • Safer handling of null relay-node lists to prevent connection errors.
  • Tests

    • Added and updated unit tests covering rate-limiter settings, plugin version checks, and event config behavior.

Review Change Stack

317787106 and others added 3 commits May 10, 2026 16:09
* fix(config): remove redundant JSON-RPC config fields and consolidate parameter binding

Remove maxBlockFilterNum, maxAddressSize, and maxRequestTimeout from NodeConfig/CommonParameter
since they were superseded by the jsonRpcMaxBatchSize/jsonRpcMaxResponseSize parameters.
Consolidate the config binding path so all JSON-RPC size limits flow through a single
reference.conf entry, and clean up stale test-config fixtures.

* fix bug of NodeConfigTest

* remove allowShieldedTransactionApi from reference.conf

* add testcase of external.ip is null

* change comment

* fix the bug of --es and 7 failed testcases
Pulls warku123/java-tron-integration-test image and runs --smoke against
the FullNode.jar built from the PR. Triggers on PR to develop/release_**.
Test reports are extracted from the container and uploaded as artifacts.

JAVA_HOME is pinned to JDK 8 inside the container so FullNode runs on the
production runtime (a few smoke tests assert java.version starts with
"1.8"); JAVA_HOME_17 keeps Gradle on JDK 17 for the test tooling.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 11, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds an integration-test GitHub Actions workflow; implements a configurable blocking/non-blocking rate-limiter API with refactors across strategies/adapters and runtime callers; gates event plugins by minimum version and adjusts event-config application; and updates tests and small runtime guards.

Changes

Integration Test Workflow

Layer / File(s) Summary
Workflow Triggers and Concurrency
.github/workflows/integration-test.yml
PR and manual triggers with path/branch filters and concurrency cancellation keyed by workflow name and PR number.
Job Configuration
.github/workflows/integration-test.yml
Declares integration job on ubuntu-latest runner with 45-minute timeout.
Environment Setup
.github/workflows/integration-test.yml
Checks out repo, provisions Temurin JDK 8, and configures Gradle cache keys for faster builds.
Build and Integration Test Execution
.github/workflows/integration-test.yml
Builds FullNode.jar (skips unit tests), pulls integration-test Docker image, mounts jar read-only, and runs containerized tests with JAVA_HOME envs.
Report Extraction and Cleanup
.github/workflows/integration-test.yml
Creates integration-reports/, copies reports/logs from container while tolerating missing files, and removes container (always-run).
Test Artifacts Upload
.github/workflows/integration-test.yml
Uploads integration-reports/ as artifact integration-test-report with warn-if-empty (always-run).

Rate limiter blocking/non-blocking API and refactor

Layer / File(s) Summary
Config & parameter bridging
common/.../CommonParameter.java, common/.../RateLimiterConfig.java, common/.../reference.conf, framework/.../config.conf, common/.../test/RateLimiterConfigTest.java
Adds rate.limiter.apiNonBlocking default=false, RateLimiterConfig.apiNonBlocking, bridges value into PARAMETER.rateLimiterApiNonBlocking, and updates config tests.
IRateLimiter API and acquirePermit helper
framework/.../adapter/IRateLimiter.java
Adds acquire(RuntimeData) and default acquirePermit(RuntimeData) that selects blocking vs non-blocking behavior based on Args.
Adapters: acquire implementations
framework/.../adapter/*
Adds acquire(RuntimeData) implementations in QpsRateLimiterAdapter, IPQPSRateLimiterAdapter, GlobalPreemptibleAdapter, DefaultBaseQqsAdapter delegating to strategies.
Strategies: timed/blocking acquire and per-IP loading
framework/.../strategy/*
Adds QpsStrategy.acquire(), GlobalPreemptibleStrategy.acquire() (timed) and updated tryAcquire, and IPQpsStrategy.loadLimiter/acquire.
GlobalRateLimiter refactor
framework/.../ratelimiter/GlobalRateLimiter.java
Centralizes per-IP limiter loading via loadIpLimiter, refactors tryAcquire/acquire to use it, and returns false on load errors.
Runtime callers: servlet & interceptor
framework/.../http/RateLimiterServlet.java, framework/.../ratelimiter/RateLimiterInterceptor.java
Switches runtime calls to acquirePermit so behavior follows configured blocking/non-blocking semantics.

Event plugin gating, Args flow, and tests

Layer / File(s) Summary
EventPluginLoader version check
framework/.../logsfilter/EventPluginLoader.java, framework/.../test/EventLoaderTest.java
Adds MIN_PLUGIN_VERSION, VersionManager, and isPluginVersionSupported(pm, pluginId) used before plugin startup to reject empty/older versions; tests added.
Args: applyEventConfig timing and bridging
framework/.../config/args/Args.java, framework/.../test/ArgsTest.java
Moves applyEventConfig to after CLI overrides, makes eventSubscribe a logical OR with CLI state, bridges rateLimiterApiNonBlocking into runtime PARAMETER, and exposes a package-private wrapper for tests; adds regression test for CLI --es and updates assertions.
Event config defaults
common/.../config/args/EventConfig.java, common/.../reference.conf
Flips event.subscribe.native.useNativeQueue default from true to false and updates reference defaults.

Misc test lifecycle and small runtime guards

Layer / File(s) Summary
PeerConnection relayNodes null guard
framework/.../net/peer/PeerConnection.java
Adds relayNodes != null guard before stream().anyMatch(...) in setChannel.
ChainInventoryMsgHandlerTest lifecycle hooks
framework/.../net/messagehandler/ChainInventoryMsgHandlerTest.java
Adds @BeforeClass/@AfterClass init()/destroy() to set and clear Args for tests.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I dug a tunnel through the CI hill,
Built jars and ran tests with a careful skill,
Permits may wait or sprint away,
Plugins checked before they play,
Tests tidy and green — the bunny's thrilled!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 21.15% 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 'ci: add Integration Smoke workflow' accurately describes the main change—adding a GitHub Actions workflow for integration testing. It is concise and clear about the primary modification.
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ 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 feat/ci-integration-smoke

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

@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 (1)
.github/workflows/integration-test.yml (1)

41-42: ⚡ Quick win

Consider validating FullNode.jar existence before Docker mount.

While Docker will fail if the jar doesn't exist, an explicit check would provide clearer error messages during debugging.

✅ Add validation step
 - name: Build FullNode.jar
   run: ./gradlew clean build -x test --no-daemon
+
+- name: Verify FullNode.jar exists
+  run: test -f build/libs/FullNode.jar || (echo "FullNode.jar not found" && exit 1)
🤖 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/workflows/integration-test.yml around lines 41 - 42, Add an explicit
validation step immediately after the "Build FullNode.jar" step that checks for
the built JAR file produced by the ./gradlew clean build -x test --no-daemon
command (e.g., the expected FullNode.jar path under build/libs or the configured
output location) and fail the job with a clear error message if the file is
missing; update the workflow to reference the same build step name ("Build
FullNode.jar") and the exact expected artifact path so the check uses a
deterministic -f (or equivalent) test and reports a helpful diagnostic before
the Docker mount runs.
🤖 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.

Nitpick comments:
In @.github/workflows/integration-test.yml:
- Around line 41-42: Add an explicit validation step immediately after the
"Build FullNode.jar" step that checks for the built JAR file produced by the
./gradlew clean build -x test --no-daemon command (e.g., the expected
FullNode.jar path under build/libs or the configured output location) and fail
the job with a clear error message if the file is missing; update the workflow
to reference the same build step name ("Build FullNode.jar") and the exact
expected artifact path so the check uses a deterministic -f (or equivalent) test
and reports a helpful diagnostic before the Docker mount runs.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9358e348-5ad5-43a8-b4b2-1ed45c18c4fa

📥 Commits

Reviewing files that changed from the base of the PR and between 22e0aa3 and 994b5e6.

📒 Files selected for processing (1)
  • .github/workflows/integration-test.yml

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.

1 issue found across 1 file

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name=".github/workflows/integration-test.yml">

<violation number="1" location=".github/workflows/integration-test.yml:58">
P2: Pin the integration test image to an immutable digest (or fixed version tag) instead of `:latest` so smoke runs are reproducible and safer.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread .github/workflows/integration-test.yml Outdated
-e JAVA_HOME=/usr/lib/jvm/temurin-8 \
-e JAVA_HOME_17=/opt/java/openjdk \
-v "${{ github.workspace }}/build/libs/FullNode.jar:/javatron/FullNode.jar:ro" \
warku123/java-tron-integration-test:latest \
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Pin the integration test image to an immutable digest (or fixed version tag) instead of :latest so smoke runs are reproducible and safer.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/integration-test.yml, line 58:

<comment>Pin the integration test image to an immutable digest (or fixed version tag) instead of `:latest` so smoke runs are reproducible and safer.</comment>

<file context>
@@ -0,0 +1,78 @@
+            -e JAVA_HOME=/usr/lib/jvm/temurin-8 \
+            -e JAVA_HOME_17=/opt/java/openjdk \
+            -v "${{ github.workspace }}/build/libs/FullNode.jar:/javatron/FullNode.jar:ro" \
+            warku123/java-tron-integration-test:latest \
+            --smoke --clean
+
</file context>

…onprotocol#6760)

Pre-3.0.0(The previous event-plugin public release is 2.2.0)
event-plugin builds still link against com.alibaba.fastjson, which
was removed from java-tron in tronprotocol#6701. When such a plugin is loaded, the
NoClassDefFoundError surfaces inside the plugin's own worker thread and is
swallowed by its catch(Throwable) handler. The node keeps running while
silently dropping every trigger, leaving operators with no signal that the
event subscription is broken.

Enforce a minimum Plugin-Version at startup in EventPluginLoader.startPlugin
using pf4j's VersionManager (semver). When the descriptor version is below
3.0.0, log a clear upgrade hint and return false; the existing call chain in
Manager.startEventSubscribing wraps that into TronError(EVENT_SUBSCRIBE_INIT)
and aborts node startup instead of silently degrading.
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.

1 issue found across 1 file (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name=".github/workflows/integration-test.yml">

<violation number="1" location=".github/workflows/integration-test.yml:59">
P1: The workflow no longer passes the `--smoke` flag to the integration test container, which changes test scope from smoke-only to default behavior. This can make PR checks slower and less reliable than intended for smoke gating.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread .github/workflows/integration-test-single-node.yml
@warku123 warku123 closed this May 12, 2026
@warku123 warku123 reopened this May 12, 2026
The smoke target (~74 cases) is a strict subset of the default `test`
target (~hundreds of cases). Smoke gives a 5-min PR signal but misses
regressions outside the @tag("smoke") classes. Switch to running the
full single-node suite (~12-15 min) so PRs catch more issues.

Renames job/container/artifact from 'smoke' to 'integration' to reflect
the wider scope. Bumps timeout from 30 to 45 minutes.
@warku123 warku123 force-pushed the feat/ci-integration-smoke branch from af0b1ce to 1384b91 Compare May 12, 2026 08:13
xxo1shine and others added 2 commits May 12, 2026 16:38
…rs (tronprotocol#6761)

- Add `rate.limiter.apiNonBlocking` switch (default false). When off, callers wait for a permit; when on, they reject immediately and shed load.
- Wire the switch through `RateLimiterConfig` → `Args` → `CommonParameter`; update `reference.conf` / `config.conf`.
- Add blocking `acquire()` paths on `IRateLimiter`, `GlobalRateLimiter`, and `QpsStrategy` / `IPQpsStrategy` / `GlobalPreemptibleStrategy`. Only the semaphore-based strategy is bounded (2s); the QPS-based paths use unbounded Guava `RateLimiter.acquire()`.
- Introduce `acquirePermit()` dispatcher (default method on `IRateLimiter`; static on `GlobalRateLimiter`) that picks blocking vs non-blocking based on the switch.
- Swap `tryAcquire` → `acquirePermit` at the `RateLimiterServlet` and `RateLimiterInterceptor` call sites; preserve per-endpoint-before-global ordering to avoid consuming global quota on per-endpoint rejection.
- Extract `loadIpLimiter()` in `GlobalRateLimiter` and `loadLimiter()` in `IPQpsStrategy` to share cache+exception handling between `tryAcquire` and `acquire`.
- Add unit tests covering dispatcher routing (both directions), acquire IP-before-global ordering, and IP-loader-failure fail-closed behaviour.
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.

Actionable comments posted: 5

🧹 Nitpick comments (1)
framework/src/test/java/org/tron/common/logsfilter/EventLoaderTest.java (1)

56-80: ⚡ Quick win

Add a malformed-version regression case for checkVersion(...).

Please add a case like "v3" or "3.x" in Line 60-70 expectations to ensure invalid format is rejected without throwing after validation hardening.

🤖 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 `@framework/src/test/java/org/tron/common/logsfilter/EventLoaderTest.java`
around lines 56 - 80, Add a malformed-version regression case to the test:
update EventLoaderTest.testIsPluginVersionSupported by adding one or more
assertions calling checkVersion("v3") and/or checkVersion("3.x") (or similar
malformed strings) in the block that asserts rejected versions (around the
existing false checks for "1.0.0","2.2.0","2.9.9") and assertFalse on those
calls; this ensures EventPluginLoader.isPluginVersionSupported (which uses
EventPluginLoader.MIN_PLUGIN_VERSION and is exercised via the helper
checkVersion) returns false for malformed version formats without throwing.
🤖 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 `@framework/src/main/java/org/tron/common/logsfilter/EventPluginLoader.java`:
- Around line 491-503: The isPluginVersionSupported method currently
dereferences pm.getPlugin(pluginId).getDescriptor().getVersion() without null
checks and calls VERSION_MANAGER.compareVersions which can throw LexerException
on malformed versions; update isPluginVersionSupported to defensively check for
nulls from pm.getPlugin(pluginId) and plugin.getDescriptor() and return false
with a clear logger.warn/error if metadata is missing, then wrap
VERSION_MANAGER.compareVersions(pluginVersion, MIN_PLUGIN_VERSION) in a
try/catch that catches LexerException (and optionally IllegalArgumentException),
logs the malformed version including pluginId and pluginVersion, and returns
false instead of letting the exception propagate.

In
`@framework/src/main/java/org/tron/core/services/ratelimiter/GlobalRateLimiter.java`:
- Around line 34-45: The current GlobalRateLimiter.acquire(RuntimeData) uses
unbounded blocking calls (r.acquire() and rateLimiter.acquire()) which can stall
threads; change both acquires to bounded/time-limited waits (e.g., tryAcquire
with a timeout like the 2-second pattern used in GlobalPreemptibleStrategy) and
return false when a timeout occurs; update the per-IP loader path (loadIpLimiter
-> r) and the global rateLimiter usage to use the timed-try-acquire pattern and
ensure both success checks are handled (and mirror the same timeout behavior for
the other acquire overload referenced around lines 47-51).

In
`@framework/src/main/java/org/tron/core/services/ratelimiter/RateLimiterInterceptor.java`:
- Around line 111-114: The interceptor currently calls blocking acquirePermit()
on RateLimiter and GlobalRateLimiter (variables perEndpointAcquired and
acquireResource) before next.startCall(), which can park RPC worker threads;
change this to a non-blocking or deadline-bound acquisition: use a
non-blocking/tryAcquire API (e.g., tryAcquirePermit or an acquirePermit variant
that accepts a timeout) for RateLimiter.acquirePermit(runtimeData) and
GlobalRateLimiter.acquirePermit(runtimeData), or compute the remaining RPC
deadline from Context.current().getDeadline() and pass that as a bounded timeout
to the acquire methods and immediately fail fast if the deadline has elapsed;
ensure next.startCall(...) is never preceded by an unbounded blocking call in
RateLimiterInterceptor.

In
`@framework/src/main/java/org/tron/core/services/ratelimiter/strategy/IPQpsStrategy.java`:
- Around line 29-36: In IPQpsStrategy.acquire, avoid uninterruptible blocking by
replacing limiter.acquire() with a bounded, interruptible attempt: call
limiter.tryAcquire(timeout, TimeUnit) (choose a sensible timeout) and handle
InterruptedException by restoring the thread interrupt status and returning
false; if tryAcquire returns false (timeout) return false as well. Locate this
logic in the acquire(String ip) method (and use loadLimiter(ip) and the limiter
variable) and ensure the method returns true only when tryAcquire succeeds.

In
`@framework/src/main/java/org/tron/core/services/ratelimiter/strategy/QpsStrategy.java`:
- Around line 33-36: QpsStrategy.acquire() currently calls rateLimiter.acquire()
which blocks uninterruptibly; change it to use a timeout-based acquisition
similar to GlobalPreemptibleStrategy.acquire(): call
rateLimiter.tryAcquire(timeout, TimeUnit.MILLISECONDS) (choose a sensible
timeout), handle InterruptedException by restoring the thread interrupt status
and returning false, and return the boolean result of tryAcquire so callers can
detect failure instead of blocking forever; update the QpsStrategy.acquire()
implementation and any callers/tests expecting unconditional true.

---

Nitpick comments:
In `@framework/src/test/java/org/tron/common/logsfilter/EventLoaderTest.java`:
- Around line 56-80: Add a malformed-version regression case to the test: update
EventLoaderTest.testIsPluginVersionSupported by adding one or more assertions
calling checkVersion("v3") and/or checkVersion("3.x") (or similar malformed
strings) in the block that asserts rejected versions (around the existing false
checks for "1.0.0","2.2.0","2.9.9") and assertFalse on those calls; this ensures
EventPluginLoader.isPluginVersionSupported (which uses
EventPluginLoader.MIN_PLUGIN_VERSION and is exercised via the helper
checkVersion) returns false for malformed version formats without throwing.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6ce1df56-5376-40ce-99ee-188fb656acb0

📥 Commits

Reviewing files that changed from the base of the PR and between 1384b91 and ebbad66.

📒 Files selected for processing (23)
  • common/src/main/java/org/tron/common/parameter/CommonParameter.java
  • common/src/main/java/org/tron/core/config/args/EventConfig.java
  • common/src/main/java/org/tron/core/config/args/RateLimiterConfig.java
  • common/src/main/resources/reference.conf
  • common/src/test/java/org/tron/core/config/args/RateLimiterConfigTest.java
  • framework/src/main/java/org/tron/common/logsfilter/EventPluginLoader.java
  • framework/src/main/java/org/tron/core/config/args/Args.java
  • framework/src/main/java/org/tron/core/net/peer/PeerConnection.java
  • framework/src/main/java/org/tron/core/services/http/RateLimiterServlet.java
  • framework/src/main/java/org/tron/core/services/ratelimiter/GlobalRateLimiter.java
  • framework/src/main/java/org/tron/core/services/ratelimiter/RateLimiterInterceptor.java
  • framework/src/main/java/org/tron/core/services/ratelimiter/adapter/DefaultBaseQqsAdapter.java
  • framework/src/main/java/org/tron/core/services/ratelimiter/adapter/GlobalPreemptibleAdapter.java
  • framework/src/main/java/org/tron/core/services/ratelimiter/adapter/IPQPSRateLimiterAdapter.java
  • framework/src/main/java/org/tron/core/services/ratelimiter/adapter/IRateLimiter.java
  • framework/src/main/java/org/tron/core/services/ratelimiter/adapter/QpsRateLimiterAdapter.java
  • framework/src/main/java/org/tron/core/services/ratelimiter/strategy/GlobalPreemptibleStrategy.java
  • framework/src/main/java/org/tron/core/services/ratelimiter/strategy/IPQpsStrategy.java
  • framework/src/main/java/org/tron/core/services/ratelimiter/strategy/QpsStrategy.java
  • framework/src/main/resources/config.conf
  • framework/src/test/java/org/tron/common/logsfilter/EventLoaderTest.java
  • framework/src/test/java/org/tron/core/config/args/ArgsTest.java
  • framework/src/test/java/org/tron/core/net/messagehandler/ChainInventoryMsgHandlerTest.java
✅ Files skipped from review due to trivial changes (2)
  • common/src/main/java/org/tron/core/config/args/EventConfig.java
  • common/src/main/java/org/tron/common/parameter/CommonParameter.java

Comment on lines +491 to +503
static boolean isPluginVersionSupported(PluginManager pm, String pluginId) {
String pluginVersion = pm.getPlugin(pluginId).getDescriptor().getVersion();
if (Strings.isNullOrEmpty(pluginVersion)) {
return false;
}
boolean isSupported = VERSION_MANAGER.compareVersions(pluginVersion, MIN_PLUGIN_VERSION) >= 0;

if (!isSupported) {
logger.error(
"event-plugin '{}' version {} is older than required {}, please upgrade event-plugin",
pluginId, pluginVersion, MIN_PLUGIN_VERSION);
}
return isSupported;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

In PF4J, does DefaultVersionManager.compareVersions(...) throw on non-semver strings (e.g., "v3", "3.x"), and what exception type is documented?

💡 Result:

Yes—PF4J’s DefaultVersionManager.compareVersions(...) will throw when given non-SemVer-ish version strings such as "v3" or "3.x". PF4J’s default VersionManager implementation is built on the external SemVer parsing library, and failures to tokenize/parse a version/constraint surface as com.github.zafarkhaja.semver.expr.LexerException (thrown from the SemVer expression/version parser) [1]. Exception type documented/observed: com.github.zafarkhaja.semver.expr.LexerException [1].

Citations:


🏁 Script executed:

# First, check the file exists and get its size
wc -l framework/src/main/java/org/tron/common/logsfilter/EventPluginLoader.java

Repository: warku123/java-tron

Length of output: 140


🏁 Script executed:

# Read the specific method and surrounding context (lines 485-510)
sed -n '485,510p' framework/src/main/java/org/tron/common/logsfilter/EventPluginLoader.java

Repository: warku123/java-tron

Length of output: 802


🏁 Script executed:

# Look for imports to understand what PluginManager, PluginWrapper are
head -50 framework/src/main/java/org/tron/common/logsfilter/EventPluginLoader.java | grep -E "import|package"

Repository: warku123/java-tron

Length of output: 1310


🏁 Script executed:

# Check if getPlugin() is used elsewhere and how null safety is handled
rg "pm\.getPlugin|PluginManager" framework/src/main/java/org/tron/common/logsfilter/EventPluginLoader.java -B 2 -A 2

Repository: warku123/java-tron

Length of output: 981


🏁 Script executed:

# Look for where startPlugin calls isPluginVersionSupported to understand the context
rg "isPluginVersionSupported|startPlugin" framework/src/main/java/org/tron/common/logsfilter/EventPluginLoader.java -B 2 -A 5

Repository: warku123/java-tron

Length of output: 1093


🏁 Script executed:

# Search for other usages of pm.getPlugin() in the codebase to see if null checks are used
rg "getPlugin\(" framework/src/main/java/org/tron/common/logsfilter/ -B 1 -A 1

Repository: warku123/java-tron

Length of output: 491


🏁 Script executed:

# Check if there are other null safety patterns for plugin operations
rg "Objects\.isNull|Objects\.nonNull|== null" framework/src/main/java/org/tron/common/logsfilter/EventPluginLoader.java

Repository: warku123/java-tron

Length of output: 719


Harden version validation to avoid startup-time exceptions on bad plugin metadata.

Line 492 chains three dereferences (pm.getPlugin(pluginId).getDescriptor().getVersion()) without null checks. Both getPlugin() and getDescriptor() can return null in PF4J, causing a NullPointerException if metadata is missing. Additionally, at line 496, VERSION_MANAGER.compareVersions() throws LexerException when given malformed version strings (e.g., non-SemVer formats), which will abort startup instead of cleanly rejecting the plugin.

Proposed fix
 static boolean isPluginVersionSupported(PluginManager pm, String pluginId) {
-    String pluginVersion = pm.getPlugin(pluginId).getDescriptor().getVersion();
-    if (Strings.isNullOrEmpty(pluginVersion)) {
-      return false;
-    }
-    boolean isSupported = VERSION_MANAGER.compareVersions(pluginVersion, MIN_PLUGIN_VERSION) >= 0;
+    org.pf4j.PluginWrapper wrapper = pm.getPlugin(pluginId);
+    if (wrapper == null || wrapper.getDescriptor() == null) {
+      logger.error("event-plugin '{}' descriptor is missing", pluginId);
+      return false;
+    }
+
+    String pluginVersion = wrapper.getDescriptor().getVersion();
+    if (Strings.isNullOrEmpty(pluginVersion)) {
+      logger.error("event-plugin '{}' version is empty", pluginId);
+      return false;
+    }
+
+    final boolean isSupported;
+    try {
+      isSupported = VERSION_MANAGER.compareVersions(pluginVersion, MIN_PLUGIN_VERSION) >= 0;
+    } catch (RuntimeException e) {
+      logger.error("event-plugin '{}' has invalid version '{}'", pluginId, pluginVersion, e);
+      return false;
+    }
 
     if (!isSupported) {
       logger.error(
           "event-plugin '{}' version {} is older than required {}, please upgrade event-plugin",
           pluginId, pluginVersion, MIN_PLUGIN_VERSION);
     }
     return isSupported;
   }
🤖 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 `@framework/src/main/java/org/tron/common/logsfilter/EventPluginLoader.java`
around lines 491 - 503, The isPluginVersionSupported method currently
dereferences pm.getPlugin(pluginId).getDescriptor().getVersion() without null
checks and calls VERSION_MANAGER.compareVersions which can throw LexerException
on malformed versions; update isPluginVersionSupported to defensively check for
nulls from pm.getPlugin(pluginId) and plugin.getDescriptor() and return false
with a clear logger.warn/error if metadata is missing, then wrap
VERSION_MANAGER.compareVersions(pluginVersion, MIN_PLUGIN_VERSION) in a
try/catch that catches LexerException (and optionally IllegalArgumentException),
logs the malformed version including pluginId and pluginVersion, and returns
false instead of letting the exception propagate.

Comment on lines +34 to +45
public static boolean acquire(RuntimeData runtimeData) {
String ip = runtimeData.getRemoteAddr();
if (!Strings.isNullOrEmpty(ip)) {
RateLimiter r = loadIpLimiter(ip);
if (r == null) {
return false;
}
r.acquire();
}
rateLimiter.acquire();
return true;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== request-path permit usage ==="
rg -nP '\bacquirePermit\s*\(' --type=java framework/src/main/java/org/tron/core/services/http framework/src/main/java/org/tron/core/services/ratelimiter

echo
echo "=== blocking acquire callsites in ratelimiter package ==="
rg -nP '\.\s*acquire\s*\(' --type=java framework/src/main/java/org/tron/core/services/ratelimiter

Repository: warku123/java-tron

Length of output: 2495


🏁 Script executed:

cat -n framework/src/main/java/org/tron/core/services/ratelimiter/GlobalRateLimiter.java

Repository: warku123/java-tron

Length of output: 2557


🏁 Script executed:

# Check for ACQUIRE_TIMEOUT_MS or similar timeout constants
rg -n 'ACQUIRE_TIMEOUT|TIMEOUT_MS' --type=java framework/src/main/java/org/tron/core/services/ratelimiter

# Check the Args configuration for the default value
rg -n 'isRateLimiterApiNonBlocking|rateLimiterApiNonBlocking' --type=java framework/src/main/java

Repository: warku123/java-tron

Length of output: 814


🏁 Script executed:

# Check default value of isRateLimiterApiNonBlocking
rg -B5 -A5 'rateLimiterApiNonBlocking' --type=java framework/src/main/java/org/tron/core/config/args/Args.java | head -30

# Verify that request paths only use acquirePermit()
rg -n 'GlobalRateLimiter\.acquire\(' --type=java framework/src/main/java/org/tron/core/services

Repository: warku123/java-tron

Length of output: 802


🏁 Script executed:

# Check the RateLimiter configuration defaults
rg -n 'isApiNonBlocking|class RateLimiter' --type=java framework/src/main/java/org/tron/core/config | head -20

# Check if there's a default config file
fd -i 'config\|application' framework/src/main/resources | head -20

Repository: warku123/java-tron

Length of output: 191


🏁 Script executed:

# Look for RateLimiter configuration classes and default values
rg -n 'isApiNonBlocking|ApiNonBlocking' --type=java framework/src/main/java/org/tron/core/config

# Check if there's a default config file in resources
find framework/src/main/resources -name '*.properties' -o -name '*.yml' -o -name '*.yaml' -o -name '*.conf' 2>/dev/null | head -20

Repository: warku123/java-tron

Length of output: 232


🏁 Script executed:

# Check the default config file
cat framework/src/main/resources/config.conf | grep -A10 -B2 'api.*[Nn]on[Bb]locking\|[Rr]ate.*[Ll]imiter' | head -50

Repository: warku123/java-tron

Length of output: 1558


🏁 Script executed:

# Search for isApiNonBlocking in config.conf
grep -n -i 'apinonblocking\|api.*non' framework/src/main/resources/config.conf

# Also check the RateLimiterConfig bean class to understand the default
rg -n 'class RateLimiterConfig|isApiNonBlocking' --type=java framework/src/main/java/org/tron/core/config

Repository: warku123/java-tron

Length of output: 302


Blocking acquire path can indefinitely stall request threads under default configuration.

acquire() waits unbounded on both per-IP and global limiters. With the default configuration (apiNonBlocking = false), request threads can be indefinitely blocked, causing thread pool saturation and long tail latency. Use bounded waits (timeout-based acquire) instead, as already demonstrated in GlobalPreemptibleStrategy which uses a 2-second timeout.

Suggested direction
-      r.acquire();
+      if (!r.tryAcquire(DEFAULT_ACQUIRE_TIMEOUT, TimeUnit.SECONDS)) {
+        return false;
+      }
...
-    rateLimiter.acquire();
-    return true;
+    return rateLimiter.tryAcquire(DEFAULT_ACQUIRE_TIMEOUT, TimeUnit.SECONDS);

Also applies to: 47-51

🤖 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
`@framework/src/main/java/org/tron/core/services/ratelimiter/GlobalRateLimiter.java`
around lines 34 - 45, The current GlobalRateLimiter.acquire(RuntimeData) uses
unbounded blocking calls (r.acquire() and rateLimiter.acquire()) which can stall
threads; change both acquires to bounded/time-limited waits (e.g., tryAcquire
with a timeout like the 2-second pattern used in GlobalPreemptibleStrategy) and
return false when a timeout occurs; update the per-IP loader path (loadIpLimiter
-> r) and the global rateLimiter usage to use the timed-try-acquire pattern and
ensure both success checks are handled (and mirror the same timeout behavior for
the other acquire overload referenced around lines 47-51).

Comment on lines +29 to +36
public boolean acquire(String ip) {
RateLimiter limiter = loadLimiter(ip);
if (limiter == null) {
return false;
}
limiter.acquire();
return true;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Uninterruptible blocking in acquire() may tie up threads indefinitely.

Similar to QpsStrategy, calling limiter.acquire() on Line 34 blocks without a timeout and cannot be interrupted. Under sustained per-IP overload, request-handling threads may wait indefinitely.

Consider using limiter.tryAcquire(timeout, TimeUnit) with interrupt handling to bound the wait time and allow graceful shutdown.

🔒 Proposed fix to add timeout-based acquisition
+  public static final int DEFAULT_ACQUIRE_TIMEOUT = 2;
+
   public boolean acquire(String ip) {
     RateLimiter limiter = loadLimiter(ip);
     if (limiter == null) {
       return false;
     }
-    limiter.acquire();
-    return true;
+    try {
+      return limiter.tryAcquire(DEFAULT_ACQUIRE_TIMEOUT, java.util.concurrent.TimeUnit.SECONDS);
+    } catch (InterruptedException e) {
+      logger.warn("acquire permit interrupted for IP {}: {}", ip, e.getMessage());
+      Thread.currentThread().interrupt();
+      return false;
+    }
   }
🤖 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
`@framework/src/main/java/org/tron/core/services/ratelimiter/strategy/IPQpsStrategy.java`
around lines 29 - 36, In IPQpsStrategy.acquire, avoid uninterruptible blocking
by replacing limiter.acquire() with a bounded, interruptible attempt: call
limiter.tryAcquire(timeout, TimeUnit) (choose a sensible timeout) and handle
InterruptedException by restoring the thread interrupt status and returning
false; if tryAcquire returns false (timeout) return false as well. Locate this
logic in the acquire(String ip) method (and use loadLimiter(ip) and the limiter
variable) and ensure the method returns true only when tryAcquire succeeds.

Comment on lines +33 to +36
public boolean acquire() {
rateLimiter.acquire();
return true;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Uninterruptible blocking in acquire() may tie up threads indefinitely.

rateLimiter.acquire() blocks without a timeout and cannot be interrupted. If permits are scarce, Netty I/O or gRPC executor threads may wait indefinitely, risking thread starvation under sustained load.

Consider using tryAcquire(timeout, TimeUnit) with interrupt handling (as in GlobalPreemptibleStrategy.acquire()), or document that QpsStrategy is only suitable when overload shedding is managed at a higher level.

🔒 Proposed fix to add timeout-based acquisition with interrupt handling
+  public static final int DEFAULT_ACQUIRE_TIMEOUT = 2;
+
   public boolean acquire() {
-    rateLimiter.acquire();
-    return true;
+    try {
+      return rateLimiter.tryAcquire(DEFAULT_ACQUIRE_TIMEOUT, java.util.concurrent.TimeUnit.SECONDS);
+    } catch (InterruptedException e) {
+      logger.error("acquire permit interrupted: {}", e.getMessage());
+      Thread.currentThread().interrupt();
+      return false;
+    }
   }
🤖 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
`@framework/src/main/java/org/tron/core/services/ratelimiter/strategy/QpsStrategy.java`
around lines 33 - 36, QpsStrategy.acquire() currently calls
rateLimiter.acquire() which blocks uninterruptibly; change it to use a
timeout-based acquisition similar to GlobalPreemptibleStrategy.acquire(): call
rateLimiter.tryAcquire(timeout, TimeUnit.MILLISECONDS) (choose a sensible
timeout), handle InterruptedException by restoring the thread interrupt status
and returning false, and return the boolean result of tryAcquire so callers can
detect failure instead of blocking forever; update the QpsStrategy.acquire()
implementation and any callers/tests expecting unconditional true.

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.

1 issue found across 1 file (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name=".github/workflows/integration-test.yml">

<violation number="1" location=".github/workflows/integration-test.yml:51">
P2: This step unconditionally requires secrets in a `pull_request` workflow, which will fail for fork PRs. Guard fork PRs (or skip the private-image path) so external PRs don't hard-fail before tests.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic

Comment thread .github/workflows/integration-test.yml Outdated
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.

4 participants