fix(jsonrpc): enforce log filter cap and improve match efficiency#71
fix(jsonrpc): enforce log filter cap and improve match efficiency#71317787106 wants to merge 23 commits into
Conversation
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds a configurable limit for JSON‑RPC log filters (default 20000), enforces it in Changes
Sequence Diagram(s)sequenceDiagram
participant Block as BlockProcessor
participant FilterSvc as TronJsonRpcImpl
participant Pool as ForkJoinPool(2)
participant Matcher as LogMatch
participant Cache as FilterResultCache
Block->>FilterSvc: handleLogsFilter(filterCapsule)
FilterSvc->>FilterSvc: collect active filters
alt many filters (>= threshold)
FilterSvc->>Pool: submit partitions of filters
Pool->>Matcher: process partition A (expiry, bloom, match)
Pool->>Matcher: process partition B (expiry, bloom, match)
Matcher->>Cache: append matched results (bulk)
Pool-->>FilterSvc: tasks complete
else few filters
FilterSvc->>Matcher: sequentially process filters
Matcher->>Cache: append matched results
end
FilterSvc->>FilterSvc: log elapsed time & active filter count
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
2 issues found across 10 files
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="framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java">
<violation number="1" location="framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java:236">
P0: The `ForkJoinPool` is created on every `handleLogsFilter` call but never shut down. This leaks worker threads on each invocation. Either shut down the pool in a `finally` block after `join()`, or make it a static/instance-level field that is reused across calls.</violation>
<violation number="2" location="framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java:1431">
P2: Inconsistent boundary check: block filter uses `>=` but log filter uses `>`, allowing one extra filter beyond `maxLogFilterNum`. Use `>=` to match the block filter behavior.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
20f0cc1 to
9897ea8
Compare
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java (1)
251-291: Add a clarifying comment for ConcurrentHashMap iteration and removal.Line 257's
eventFilterMap.remove(entry.getKey())during iteration is safe forConcurrentHashMap(weakly consistent iterator), but consider adding a brief inline comment explaining this is intentional and safe—preventing future contributors from mistakenly porting this pattern to a non-concurrent collection.The concurrent mutation concern at line 290 is mitigated:
LogFilterAndResult.resultis aLinkedBlockingQueue, so concurrentaddAll()calls fromForkJoinPoolworkers anddrainTo()calls from RPC reader threads are both thread-safe.🤖 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/services/jsonrpc/TronJsonRpcImpl.java` around lines 251 - 291, The code in processLogFilterEntry calls eventFilterMap.remove(entry.getKey()) while iterating a ConcurrentHashMap; add a concise inline comment next to that remove explaining this is intentional and safe because eventFilterMap is a ConcurrentHashMap with a weakly-consistent iterator (so removals during iteration are allowed), and warn contributors not to copy this pattern to non-concurrent maps; also add a short comment by the getResult().addAll(...) call noting that LogFilterAndResult.getResult() is a LinkedBlockingQueue so concurrent addAll() from ForkJoinPool workers and drainTo() from RPC reader threads is thread-safe.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@framework/src/main/java/org/tron/core/config/args/Args.java`:
- Around line 268-271: When reading ConfigKey.NODE_JSONRPC_MAX_LOG_FILTER_NUM in
Args (setting PARAMETER.jsonRpcMaxLogFilterNum), validate the value is a
positive integer (>0) and reject non-positive values; if invalid, log an error
and fail fast (throw an IllegalArgumentException or exit) so the node won't
start with zero/negative maxLogFilterNum which breaks TronJsonRpcImpl's
eth_newFilter logic (see TronJsonRpcImpl.java:1443). Locate the assignment in
Args and add the check immediately after config.getInt(...), referencing
PARAMETER.jsonRpcMaxLogFilterNum for the value and ensuring a clear error
message that includes the invalid value.
In `@framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java`:
- Around line 228-249: The current per-block timing log in handleLogsFilter uses
logger.info which floods INFO logs; change it to logger.debug (or wrap with
logger.isDebugEnabled()) so timing is only emitted at DEBUG level, or
alternatively emit at INFO only when (t2 - t1) exceeds a chosen threshold;
update the call that currently uses logger.info(...) to use logger.debug(...) or
add the conditional check and keep the same message, referencing
handleLogsFilter, logger.info, logger.debug, and the timing variables t1/t2 and
eventFilterMap.size().
- Around line 174-175: The static LOGS_FILTER_POOL is created with new
ForkJoinPool(2) while the PR text said parallelism 3 and it is never shut down,
causing thread leaks; reconcile the intended parallelism (use 3 or make it
configurable using available processors), replace the hardcoded new
ForkJoinPool(2) by the chosen value (or a supplier/config param) and ensure the
pool is properly shutdown in the class close() method alongside sectionExecutor
(call LOGS_FILTER_POOL.shutdown() and await termination or forceShutdown as
appropriate); reference FILTER_PARALLEL_THRESHOLD, LOGS_FILTER_POOL, close(),
and sectionExecutor when making these changes.
- Around line 1443-1446: The size() check on eventFilter2Result and the
subsequent put() can race under concurrent eth_newFilter calls; change the
insertion to an atomic compute/computeIfAbsent or use eventFilter2Result.compute
to both test and insert in one atomic operation (referencing eventFilter2Result,
maxLogFilterNum, and the eth_newFilter insertion logic) so that you only
create/accept a new filter if the total stays <= maxLogFilterNum; alternatively,
perform the put and then immediately re-check the map size and remove/rollback
the newly inserted entry if the cap is exceeded to enforce the limit atomically.
---
Nitpick comments:
In `@framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java`:
- Around line 251-291: The code in processLogFilterEntry calls
eventFilterMap.remove(entry.getKey()) while iterating a ConcurrentHashMap; add a
concise inline comment next to that remove explaining this is intentional and
safe because eventFilterMap is a ConcurrentHashMap with a weakly-consistent
iterator (so removals during iteration are allowed), and warn contributors not
to copy this pattern to non-concurrent maps; also add a short comment by the
getResult().addAll(...) call noting that LogFilterAndResult.getResult() is a
LinkedBlockingQueue so concurrent addAll() from ForkJoinPool workers and
drainTo() from RPC reader threads is thread-safe.
🪄 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: 2f546df6-e48d-4a43-8ae7-6129380594c1
📒 Files selected for processing (11)
common/src/main/java/org/tron/common/parameter/CommonParameter.javaframework/src/main/java/org/tron/core/config/args/Args.javaframework/src/main/java/org/tron/core/config/args/ConfigKey.javaframework/src/main/java/org/tron/core/db/Manager.javaframework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpc.javaframework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.javaframework/src/main/java/org/tron/core/services/jsonrpc/filters/LogMatch.javaframework/src/main/resources/config.confframework/src/test/resources/config-localtest.confframework/src/test/resources/config-test-mainnet.confframework/src/test/resources/config-test.conf
| if (config.hasPath(ConfigKey.NODE_JSONRPC_MAX_LOG_FILTER_NUM)) { | ||
| PARAMETER.jsonRpcMaxLogFilterNum = | ||
| config.getInt(ConfigKey.NODE_JSONRPC_MAX_LOG_FILTER_NUM); | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Inspect all uses of the new JSON-RPC log filter cap and related tests.
# Expectation: Enforcement assumes a strictly positive cap, and tests cover zero/negative config values if allowed.
rg -n -C4 '\bjsonRpcMaxLogFilterNum\b|getJsonRpcMaxLogFilterNum|NODE_JSONRPC_MAX_LOG_FILTER_NUM|eth_newFilter|newFilter\s*\(' --glob '*.java'Repository: 317787106/java-tron
Length of output: 17055
🏁 Script executed:
#!/bin/bash
# Find where maxLogFilterNum is compared/used in enforcement logic
rg -n -B2 -A2 'maxLogFilterNum\s*[<>]=?\s*|<\s*maxLogFilterNum|>\s*maxLogFilterNum' --glob '*.java'Repository: 317787106/java-tron
Length of output: 45
🏁 Script executed:
#!/bin/bash
# Broader search for all usages of maxLogFilterNum variable
rg -n 'maxLogFilterNum' --glob '*.java' -A3 -B1Repository: 317787106/java-tron
Length of output: 2332
🏁 Script executed:
#!/bin/bash
# Verify the exact context around lines 268-271 in Args.java
sed -n '260,280p' framework/src/main/java/org/tron/core/config/args/Args.javaRepository: 317787106/java-tron
Length of output: 789
Add validation for maxLogFilterNum to reject non-positive values.
Line 270 accepts zero or negative values, which would break all eth_newFilter calls due to the check at TronJsonRpcImpl.java:1443 (if (eventFilter2Result.size() >= maxLogFilterNum)). Validate that the parameter is positive when loading config to fail fast.
Proposed validation
if (config.hasPath(ConfigKey.NODE_JSONRPC_MAX_LOG_FILTER_NUM)) {
- PARAMETER.jsonRpcMaxLogFilterNum =
- config.getInt(ConfigKey.NODE_JSONRPC_MAX_LOG_FILTER_NUM);
+ int maxLogFilterNum = config.getInt(ConfigKey.NODE_JSONRPC_MAX_LOG_FILTER_NUM);
+ if (maxLogFilterNum <= 0) {
+ throw new IllegalArgumentException(
+ ConfigKey.NODE_JSONRPC_MAX_LOG_FILTER_NUM + " must be greater than 0");
+ }
+ PARAMETER.jsonRpcMaxLogFilterNum = maxLogFilterNum;
}🤖 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/config/args/Args.java` around lines 268
- 271, When reading ConfigKey.NODE_JSONRPC_MAX_LOG_FILTER_NUM in Args (setting
PARAMETER.jsonRpcMaxLogFilterNum), validate the value is a positive integer (>0)
and reject non-positive values; if invalid, log an error and fail fast (throw an
IllegalArgumentException or exit) so the node won't start with zero/negative
maxLogFilterNum which breaks TronJsonRpcImpl's eth_newFilter logic (see
TronJsonRpcImpl.java:1443). Locate the assignment in Args and add the check
immediately after config.getInt(...), referencing
PARAMETER.jsonRpcMaxLogFilterNum for the value and ensuring a clear error
message that includes the invalid value.
| if (eventFilter2Result.size() >= maxLogFilterNum) { | ||
| throw new JsonRpcExceedLimitException( | ||
| "exceed max log filters: " + maxLogFilterNum + ", try again later"); | ||
| } |
There was a problem hiding this comment.
TOCTOU race: size check is not atomic with insert.
size() check on line 1443 and put() on line 1450 are not performed atomically on the ConcurrentHashMap. Under concurrent eth_newFilter requests, multiple callers can observe size() < maxLogFilterNum simultaneously and all proceed to insert, exceeding the configured cap. Given the default cap is 20,000 this is a soft limit abuse concern rather than a correctness bug, but it defeats the purpose of the cap under burst load.
Consider using compute/computeIfAbsent pattern, or re-check size after put and rollback if exceeded:
🛡️ Example hardening
- if (eventFilter2Result.size() >= maxLogFilterNum) {
- throw new JsonRpcExceedLimitException(
- "exceed max log filters: " + maxLogFilterNum + ", try again later");
- }
long currentMaxFullNum = wallet.getNowBlock().getBlockHeader().getRawData().getNumber();
LogFilterAndResult logFilterAndResult = new LogFilterAndResult(fr, currentMaxFullNum, wallet);
String filterID = generateFilterId();
- eventFilter2Result.put(filterID, logFilterAndResult);
+ if (eventFilter2Result.size() >= maxLogFilterNum) {
+ throw new JsonRpcExceedLimitException(
+ "exceed max log filters: " + maxLogFilterNum + ", try again later");
+ }
+ eventFilter2Result.put(filterID, logFilterAndResult);
+ if (eventFilter2Result.size() > maxLogFilterNum) {
+ eventFilter2Result.remove(filterID);
+ throw new JsonRpcExceedLimitException(
+ "exceed max log filters: " + maxLogFilterNum + ", try again later");
+ }
return ByteArray.toJsonHex(filterID);The same concern applies to newBlockFilter (lines 1465-1468), which is preexisting and out of scope but worth noting for a follow-up.
🤖 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/services/jsonrpc/TronJsonRpcImpl.java`
around lines 1443 - 1446, The size() check on eventFilter2Result and the
subsequent put() can race under concurrent eth_newFilter calls; change the
insertion to an atomic compute/computeIfAbsent or use eventFilter2Result.compute
to both test and insert in one atomic operation (referencing eventFilter2Result,
maxLogFilterNum, and the eth_newFilter insertion logic) so that you only
create/accept a new filter if the total stays <= maxLogFilterNum; alternatively,
perform the put and then immediately re-check the map size and remove/rollback
the newly inserted entry if the cap is exceeded to enforce the limit atomically.
|
|
||
| private static final String ERROR_SELECTOR = "08c379a0"; // Function selector for Error(string) | ||
| private static final int FILTER_PARALLEL_THRESHOLD = 10000; | ||
| private static final ForkJoinPool LOGS_FILTER_POOL = new ForkJoinPool(); |
There was a problem hiding this comment.
[SHOULD] Static LOGS_FILTER_POOL shut down by instance close() breaks reuse
LOGS_FILTER_POOL is private static final, but close() (line 1598) calls shutdownAndAwaitTermination on it. If any instance of TronJsonRpcImpl is closed (Spring @DirtiesContext integration tests, or any future lifecycle that recreates the bean), subsequent handleLogsFilter calls that hit the parallel branch will throw RejectedExecutionException — the class-level pool is permanently dead after the first close. Separately, shutdownAndAwaitTermination(LOGS_FILTER_POOL, "") passes an empty name, so the log line becomes Pool shutdown... (two spaces), which is useless for operators.
Suggestion: Either make the pool an instance field (constructed in the constructor, torn down in close()), or keep it static and stop shutting it down from close() — use daemon worker threads so the JVM can exit cleanly. Pass a meaningful name (e.g. "jsonrpc-logs-filter-pool") either way.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
framework/src/test/java/org/tron/core/jsonrpc/WalletCursorTest.java (1)
88-94:⚠️ Potential issue | 🟡 Minor
close()is unreachable on the happy path of this test; move tofinally.This test asserts that
buildTransactionthrows because the cursor isSOLIDITY(see Line 92-93). When the test passes as intended, line 90 is never executed, so theTronJsonRpcImplinstance — and the staticLOGS_FILTER_POOLit owns — is never cleaned up. If the test ever regresses to a non-throwing path,close()will run and (as noted onTronJsonRpcImpl.javaLine 175) shut down the static pool for the rest of the JVM.Move the cleanup into a
finallyblock so it’s unconditional, and co-locate it with the instance lifecycle:🧹 Proposed fix
TronJsonRpcImpl tronJsonRpc = new TronJsonRpcImpl(nodeInfoService, wallet, dbManager); try { tronJsonRpc.buildTransaction(buildArguments); - tronJsonRpc.close(); } catch (Exception e) { Assert.assertEquals("the method buildTransaction does not exist/is not available in " + "SOLIDITY", e.getMessage()); + } finally { + try { + tronJsonRpc.close(); + } catch (IOException ioe) { + // ignore on teardown + } }🤖 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/jsonrpc/WalletCursorTest.java` around lines 88 - 94, The test currently calls tronJsonRpc.close() only in the try path so the TronJsonRpcImpl instance and its static LOGS_FILTER_POOL may not be cleaned up when buildTransaction throws; move the close() call into a finally block and ensure the tronJsonRpc variable is declared/initialized outside the try so it can be closed unconditionally (i.e., surround the try/catch with try { ... } catch(...) { ... } finally { tronJsonRpc.close(); }) referencing buildTransaction and close on the TronJsonRpcImpl instance used in WalletCursorTest.
♻️ Duplicate comments (1)
framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java (1)
175-175:⚠️ Potential issue | 🟠 MajorStatic
LOGS_FILTER_POOLpermanently dies after firstclose().
LOGS_FILTER_POOLisprivate static final(Line 175) butclose()shuts it down on every instance tear-down (Line 1598). After the first instance is closed, the class-scoped pool is terminated for the lifetime of the JVM, and any subsequentTronJsonRpcImplthat hits the parallel branch inhandleLogsFilterwill throwRejectedExecutionExceptionatLOGS_FILTER_POOL.submit(...).This is now actively triggerable:
WalletCursorTest.testEnableInFullNodeandtestDisableInSolidityhave been updated in this PR to calltronJsonRpc.close(), so test suites that construct multiple instances in the same JVM (Spring@DirtiesContext, multiple@Testmethods, or any lifecycle that recreates the bean) will leave the class in a broken state.Pick one of the two ownership models:
Option A — keep pool static; stop shutting it down from instance close()
- private static final ForkJoinPool LOGS_FILTER_POOL = new ForkJoinPool(2); + private static final ForkJoinPool LOGS_FILTER_POOL = new ForkJoinPool( + 2, + pool -> { + ForkJoinWorkerThread w = ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool); + w.setName("jsonrpc-logs-filter-" + w.getPoolIndex()); + w.setDaemon(true); + return w; + }, + null, + false);public void close() throws IOException { - ExecutorServiceManager.shutdownAndAwaitTermination(LOGS_FILTER_POOL, "logs-filter-pool"); logElementCache.invalidateAll(); blockHashCache.invalidateAll(); ExecutorServiceManager.shutdownAndAwaitTermination(sectionExecutor, esName); }Option B — make the pool an instance field tied to the bean lifecycle
- private static final ForkJoinPool LOGS_FILTER_POOL = new ForkJoinPool(2); + private final ForkJoinPool logsFilterPool = new ForkJoinPool(2);Then update
handleLogsFilter/processLogFilterEntryto be instance methods (or pass the pool in), and keep the existing shutdown inclose(). Note this also requires rethinking thestaticcallers inManager/event dispatch.Option A is the minimal fix and matches the intent that
handleLogsFilteris a static class-level service.🤖 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/services/jsonrpc/TronJsonRpcImpl.java` at line 175, The static LOGS_FILTER_POOL is being shut down in the instance close() causing RejectedExecutionException for subsequent instances; to fix, adopt Option A: treat LOGS_FILTER_POOL as a static shared resource and remove (or guard against) any shutdown/close logic from TronJsonRpcImpl.close() that calls LOGS_FILTER_POOL.shutdown()/shutdownNow()/close; ensure handleLogsFilter and processLogFilterEntry continue to submit to LOGS_FILTER_POOL as a static pool and verify no other code paths attempt to terminate that static pool.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@framework/src/test/java/org/tron/core/jsonrpc/WalletCursorTest.java`:
- Around line 88-94: The test currently calls tronJsonRpc.close() only in the
try path so the TronJsonRpcImpl instance and its static LOGS_FILTER_POOL may not
be cleaned up when buildTransaction throws; move the close() call into a finally
block and ensure the tronJsonRpc variable is declared/initialized outside the
try so it can be closed unconditionally (i.e., surround the try/catch with try {
... } catch(...) { ... } finally { tronJsonRpc.close(); }) referencing
buildTransaction and close on the TronJsonRpcImpl instance used in
WalletCursorTest.
---
Duplicate comments:
In `@framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java`:
- Line 175: The static LOGS_FILTER_POOL is being shut down in the instance
close() causing RejectedExecutionException for subsequent instances; to fix,
adopt Option A: treat LOGS_FILTER_POOL as a static shared resource and remove
(or guard against) any shutdown/close logic from TronJsonRpcImpl.close() that
calls LOGS_FILTER_POOL.shutdown()/shutdownNow()/close; ensure handleLogsFilter
and processLogFilterEntry continue to submit to LOGS_FILTER_POOL as a static
pool and verify no other code paths attempt to terminate that static pool.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: f848f185-10c5-4ee0-9a9f-05c3d8e47094
📒 Files selected for processing (2)
framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.javaframework/src/test/java/org/tron/core/jsonrpc/WalletCursorTest.java
There was a problem hiding this comment.
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="framework/src/main/java/org/tron/core/db/Manager.java">
<violation number="1">
P1: EventBloomException is no longer included in the fork-apply catch, so EventBloom failures skip the switch-back rollback path and can leave fork switching in a partially applied state.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
26c89bf to
2551c5a
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
framework/src/test/java/org/tron/core/jsonrpc/WalletCursorTest.java (1)
88-94:⚠️ Potential issue | 🟡 Minor
close()unreachable when exception is expected.In
testDisableInSolidity, the test expectsbuildTransaction()to throw an exception because the cursor is set toSOLIDITY. Theclose()call on line 90 is inside the try block after the throwing call, so it will never execute. Consider using try-with-resources or a finally block.Additionally,
testSource()andtestDisableInPBFT()also createTronJsonRpcImplinstances but never callclose().🛠️ Suggested pattern
TronJsonRpcImpl tronJsonRpc = new TronJsonRpcImpl(nodeInfoService, wallet, dbManager); try { tronJsonRpc.buildTransaction(buildArguments); - tronJsonRpc.close(); + Assert.fail("Expected exception was not thrown"); } catch (Exception e) { Assert.assertEquals("the method buildTransaction does not exist/is not available in " + "SOLIDITY", e.getMessage()); + } finally { + tronJsonRpc.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/jsonrpc/WalletCursorTest.java` around lines 88 - 94, The tests (testDisableInSolidity, testSource, testDisableInPBFT) create TronJsonRpcImpl instances but may skip calling close() because tronJsonRpc.buildTransaction() is expected to throw, making the close() call unreachable; update these tests to ensure TronJsonRpcImpl.close() always runs—either instantiate TronJsonRpcImpl in a try-with-resources (implementing AutoCloseable) or use a try/finally around the code that calls tronJsonRpc.buildTransaction() so tronJsonRpc.close() is invoked in the finally block; specifically modify testDisableInSolidity to move tronJsonRpc.close() into a finally (or use try-with-resources) and apply the same pattern to testSource and testDisableInPBFT to guarantee resources are closed.
♻️ Duplicate comments (1)
framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java (1)
174-175:⚠️ Potential issue | 🟠 MajorStatic
ForkJoinPoolshutdown via instanceclose()breaks reuse.
LOGS_FILTER_POOLisprivate static final, butclose()at line 1598 callsshutdownAndAwaitTerminationon it. If anyTronJsonRpcImplinstance is closed (e.g., in tests with@DirtiesContext, or future lifecycle changes), subsequenthandleLogsFiltercalls hitting the parallel branch will throwRejectedExecutionException— the class-level pool is permanently dead after the first close.Additionally, context snippets show
JsonrpcServiceTestandCreate2Testcreate instances without callingclose(), which isn't ideal, but callingclose()would be worse since it kills the shared pool.Consider making the pool an instance field (constructed in constructor, shutdown in
close()), or keep it static and use daemon worker threads so the JVM can exit cleanly without explicit shutdown.🛠️ Option 1: Make pool instance-scoped
- private static final int FILTER_PARALLEL_THRESHOLD = 10000; - private static final ForkJoinPool LOGS_FILTER_POOL = new ForkJoinPool(2); + private static final int FILTER_PARALLEL_THRESHOLD = 10000; + private final ForkJoinPool logsFilterPool; `@Autowired` public TronJsonRpcImpl(...) { ... + this.logsFilterPool = new ForkJoinPool(2); }And update
handleLogsFilterto accept the pool as a parameter or make it non-static.🛠️ Option 2: Keep static, use daemon threads, don't shutdown
- private static final ForkJoinPool LOGS_FILTER_POOL = new ForkJoinPool(2); + private static final ForkJoinPool LOGS_FILTER_POOL = new ForkJoinPool( + 2, + pool -> { + ForkJoinWorkerThread t = ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool); + t.setDaemon(true); + t.setName("jsonrpc-logs-filter-" + t.getPoolIndex()); + return t; + }, + null, false); `@Override` public void close() throws IOException { - ExecutorServiceManager.shutdownAndAwaitTermination(LOGS_FILTER_POOL, "logs-filter-pool"); logElementCache.invalidateAll();🤖 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/services/jsonrpc/TronJsonRpcImpl.java` around lines 174 - 175, LOGS_FILTER_POOL is a static shared ForkJoinPool that is shutdown in close(), causing RejectedExecutionException for any other TronJsonRpcImpl instances; change it to an instance field (remove static) and initialize it in the TronJsonRpcImpl constructor (e.g., new ForkJoinPool(2)), update handleLogsFilter (make non-static or pass the instance pool) to use this instance pool, and then call shutdownAndAwaitTermination on that instance pool in close() so each instance manages its own pool lifecycle.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@common/src/main/resources/reference.conf`:
- Around line 404-405: The comment above the config key maxLogFilterNum is
misleading: change it to state that maxLogFilterNum limits the number of active
log filters that can be registered via eth_newFilter (not the number of log
entries returned by eth_getLogs), and add a note pointing to
LogBlockQuery.MAX_RESULT as the place that controls per-query log entry limits;
reference the config key name maxLogFilterNum, the RPC methods eth_newFilter /
eth_getLogs, and LogBlockQuery.MAX_RESULT so reviewers can find the related
code.
---
Outside diff comments:
In `@framework/src/test/java/org/tron/core/jsonrpc/WalletCursorTest.java`:
- Around line 88-94: The tests (testDisableInSolidity, testSource,
testDisableInPBFT) create TronJsonRpcImpl instances but may skip calling close()
because tronJsonRpc.buildTransaction() is expected to throw, making the close()
call unreachable; update these tests to ensure TronJsonRpcImpl.close() always
runs—either instantiate TronJsonRpcImpl in a try-with-resources (implementing
AutoCloseable) or use a try/finally around the code that calls
tronJsonRpc.buildTransaction() so tronJsonRpc.close() is invoked in the finally
block; specifically modify testDisableInSolidity to move tronJsonRpc.close()
into a finally (or use try-with-resources) and apply the same pattern to
testSource and testDisableInPBFT to guarantee resources are closed.
---
Duplicate comments:
In `@framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java`:
- Around line 174-175: LOGS_FILTER_POOL is a static shared ForkJoinPool that is
shutdown in close(), causing RejectedExecutionException for any other
TronJsonRpcImpl instances; change it to an instance field (remove static) and
initialize it in the TronJsonRpcImpl constructor (e.g., new ForkJoinPool(2)),
update handleLogsFilter (make non-static or pass the instance pool) to use this
instance pool, and then call shutdownAndAwaitTermination on that instance pool
in close() so each instance manages its own pool lifecycle.
🪄 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: f3c347f2-bc76-4474-9882-3898bcf6c561
📒 Files selected for processing (12)
common/src/main/java/org/tron/common/parameter/CommonParameter.javacommon/src/main/java/org/tron/core/config/args/NodeConfig.javacommon/src/main/resources/reference.confframework/src/main/java/org/tron/core/config/args/Args.javaframework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpc.javaframework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.javaframework/src/main/java/org/tron/core/services/jsonrpc/filters/LogMatch.javaframework/src/main/resources/config.confframework/src/test/java/org/tron/core/jsonrpc/WalletCursorTest.javaframework/src/test/resources/config-localtest.confframework/src/test/resources/config-test-mainnet.confframework/src/test/resources/config-test.conf
✅ Files skipped from review due to trivial changes (5)
- framework/src/test/resources/config-test-mainnet.conf
- framework/src/test/resources/config-test.conf
- framework/src/test/resources/config-localtest.conf
- framework/src/main/java/org/tron/core/config/args/Args.java
- framework/src/main/resources/config.conf
❌ Math Usage Detection ResultsFound forbidden usage of Please review if this usage is intended. Caution Note: You should use |
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (2)
framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java (2)
1443-1450:⚠️ Potential issue | 🟠 MajorThe filter-cap check is still racy.
Line 1443/Line 1465 checks
size()before Line 1450/Line 1472 inserts into theConcurrentHashMap. Concurrenteth_newFilter/eth_newBlockFiltercalls can all pass the pre-check and overshoot the configured caps, which undermines the resource-exhaustion guard this PR is adding.Possible hardening
- if (maxLogFilterNum > 0 && eventFilter2Result.size() >= maxLogFilterNum) { - throw new JsonRpcExceedLimitException( - "exceed max log filters: " + maxLogFilterNum + ", try again later"); - } long currentMaxFullNum = wallet.getNowBlock().getBlockHeader().getRawData().getNumber(); LogFilterAndResult logFilterAndResult = new LogFilterAndResult(fr, currentMaxFullNum, wallet); String filterID = generateFilterId(); eventFilter2Result.put(filterID, logFilterAndResult); + if (maxLogFilterNum > 0 && eventFilter2Result.size() > maxLogFilterNum) { + eventFilter2Result.remove(filterID, logFilterAndResult); + throw new JsonRpcExceedLimitException( + "exceed max log filters: " + maxLogFilterNum + ", try again later"); + }Also applies to: 1465-1472
🤖 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/services/jsonrpc/TronJsonRpcImpl.java` around lines 1443 - 1450, The pre-check against maxLogFilterNum is racy because multiple threads can pass size() then concurrently insert into eventFilter2Result; change to an atomic check-and-insert: maintain an AtomicInteger (e.g., filterCount) tracking current filters or use eventFilter2Result.compute(...) / putIfAbsent inside a synchronized block so you only insert when the incremented count is still <= maxLogFilterNum, otherwise reject with JsonRpcExceedLimitException; update places that create filters (the code paths using generateFilterId(), new LogFilterAndResult(...), and eventFilter2Result.put(...)) to increment the counter before creating/putting and decrement on failure/removal so the cap is never exceeded concurrently.
174-175:⚠️ Potential issue | 🟠 MajorDon't shut down a class-level pool from an instance
close().Line 175 creates a static
LOGS_FILTER_POOL, but Line 1598 tears it down every time aTronJsonRpcImplinstance is closed. After the first close, laterhandleLogsFilter()calls that hit the parallel branch will submit into a dead pool and fail.Also applies to: 1597-1601
🤖 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/services/jsonrpc/TronJsonRpcImpl.java` around lines 174 - 175, The static LOGS_FILTER_POOL is being shutdown from the instance close() of TronJsonRpcImpl which leaves the class-level ForkJoinPool unusable for subsequent handleLogsFilter() calls that rely on FILTER_PARALLEL_THRESHOLD; to fix, remove the shutdown of LOGS_FILTER_POOL from TronJsonRpcImpl.close() and either (a) move the pool to an instance field and shutdown it in close(), or (b) keep it static and provide a single application-lifecycle shutdown hook (or rely on JVM exit) so handleLogsFilter() continues to submit tasks safely; update references to LOGS_FILTER_POOL and close() accordingly and ensure no other instance methods call shutdown() on the static pool.
🧹 Nitpick comments (2)
framework/src/test/java/org/tron/core/jsonrpc/HandleLogsFilterTest.java (1)
59-193: Add one test that crosses the parallel threshold.All cases here keep the filter map at 1-2 entries, so the new
> FILTER_PARALLEL_THRESHOLDbranch inTronJsonRpcImpl.handleLogsFilter()never runs. A single test withFILTER_PARALLEL_THRESHOLD + 1matching filters would give this PR coverage for the actualForkJoinPoolpath.🤖 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/jsonrpc/HandleLogsFilterTest.java` around lines 59 - 193, Add a test that triggers the parallel branch in TronJsonRpcImpl.handleLogsFilter() by populating TronJsonRpcImpl.getEventFilter2ResultFull() with FILTER_PARALLEL_THRESHOLD + 1 matching LogFilterAndResult entries (use LogFilterAndResult and the same FilterRequest used in other tests), then create a LogsFilterCapsule that matches and call handleLogsFilter(capsule); assert that all inserted filters receive results (e.g., each LogFilterAndResult.getResult() has the expected entry) to cover the ForkJoinPool path. Ensure you use the same FILTER_ID naming scheme (e.g., generate unique IDs) so entries are distinct and the test cleans up the map before/after to avoid interference with other tests.framework/src/main/java/org/tron/core/services/jsonrpc/JsonRpcApiUtil.java (1)
60-60: Make the shared RNGprivate static final.Line 60 exposes a mutable package-scoped
SecureRandomeven though callers only needgenerateFilterId(). Locking this down avoids accidental reassignment of the filter-id generator from package peers.Suggested change
- static SecureRandom random = new SecureRandom(); + private static final SecureRandom random = new SecureRandom();🤖 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/services/jsonrpc/JsonRpcApiUtil.java` at line 60, The shared SecureRandom field "random" in JsonRpcApiUtil is package-visible and mutable; change its declaration to "private static final SecureRandom random = new SecureRandom();" inside class JsonRpcApiUtil so it cannot be reassigned by package peers, and keep existing usages (e.g., generateFilterId()) unchanged to preserve behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@framework/src/test/java/org/tron/core/jsonrpc/WalletCursorTest.java`:
- Around line 157-184: The test relies on the shared static map from
TronJsonRpcImpl.getEventFilter2ResultFull(), so make it independent by
snapshotting and restoring the map: at the start of
testNewFilter_exceedsCapThrowsException copy the current map contents (e.g., new
HashMap<>(map)), then clear the shared map before inserting exactly cap
synthetic entries into map and running tronJsonRpc.newFilter(fr); in the outer
finally, restore the original contents by clearing the map and
putAll(originalSnapshot) so other tests are unaffected. Ensure you still assert
cap behavior using Args.getInstance().getJsonRpcMaxLogFilterNum() and clean up
with tronJsonRpc.close() in the inner finally.
---
Duplicate comments:
In `@framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java`:
- Around line 1443-1450: The pre-check against maxLogFilterNum is racy because
multiple threads can pass size() then concurrently insert into
eventFilter2Result; change to an atomic check-and-insert: maintain an
AtomicInteger (e.g., filterCount) tracking current filters or use
eventFilter2Result.compute(...) / putIfAbsent inside a synchronized block so you
only insert when the incremented count is still <= maxLogFilterNum, otherwise
reject with JsonRpcExceedLimitException; update places that create filters (the
code paths using generateFilterId(), new LogFilterAndResult(...), and
eventFilter2Result.put(...)) to increment the counter before creating/putting
and decrement on failure/removal so the cap is never exceeded concurrently.
- Around line 174-175: The static LOGS_FILTER_POOL is being shutdown from the
instance close() of TronJsonRpcImpl which leaves the class-level ForkJoinPool
unusable for subsequent handleLogsFilter() calls that rely on
FILTER_PARALLEL_THRESHOLD; to fix, remove the shutdown of LOGS_FILTER_POOL from
TronJsonRpcImpl.close() and either (a) move the pool to an instance field and
shutdown it in close(), or (b) keep it static and provide a single
application-lifecycle shutdown hook (or rely on JVM exit) so handleLogsFilter()
continues to submit tasks safely; update references to LOGS_FILTER_POOL and
close() accordingly and ensure no other instance methods call shutdown() on the
static pool.
---
Nitpick comments:
In `@framework/src/main/java/org/tron/core/services/jsonrpc/JsonRpcApiUtil.java`:
- Line 60: The shared SecureRandom field "random" in JsonRpcApiUtil is
package-visible and mutable; change its declaration to "private static final
SecureRandom random = new SecureRandom();" inside class JsonRpcApiUtil so it
cannot be reassigned by package peers, and keep existing usages (e.g.,
generateFilterId()) unchanged to preserve behavior.
In `@framework/src/test/java/org/tron/core/jsonrpc/HandleLogsFilterTest.java`:
- Around line 59-193: Add a test that triggers the parallel branch in
TronJsonRpcImpl.handleLogsFilter() by populating
TronJsonRpcImpl.getEventFilter2ResultFull() with FILTER_PARALLEL_THRESHOLD + 1
matching LogFilterAndResult entries (use LogFilterAndResult and the same
FilterRequest used in other tests), then create a LogsFilterCapsule that matches
and call handleLogsFilter(capsule); assert that all inserted filters receive
results (e.g., each LogFilterAndResult.getResult() has the expected entry) to
cover the ForkJoinPool path. Ensure you use the same FILTER_ID naming scheme
(e.g., generate unique IDs) so entries are distinct and the test cleans up the
map before/after to avoid interference with other tests.
🪄 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: c9931960-0cc8-42ee-b49f-c2abc988d81c
📒 Files selected for processing (5)
framework/src/main/java/org/tron/core/services/jsonrpc/JsonRpcApiUtil.javaframework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.javaframework/src/test/java/org/tron/core/jsonrpc/HandleLogsFilterTest.javaframework/src/test/java/org/tron/core/jsonrpc/LogMatchOverLimitTest.javaframework/src/test/java/org/tron/core/jsonrpc/WalletCursorTest.java
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
framework/src/main/resources/config.conf (1)
378-379: Optional: align wording withreference.conffor clarity.Consider using
eth_newFilterand explicitly saying “concurrent/active registrations” to avoid ambiguity witheth_getLogslimits.Suggested wording tweak
- # Allowed maximum number for newFilter, >0 otherwise no limit + # Maximum number of concurrent eth_newFilter registrations, >0 otherwise no limit maxLogFilterNum = 20000🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@framework/src/main/resources/config.conf` around lines 378 - 379, Update the config key description for maxLogFilterNum to match reference.conf wording by explicitly referencing eth_newFilter and clarifying it limits concurrent/active registrations (not eth_getLogs retrievals); locate the maxLogFilterNum setting and adjust its comment text to read something like "Allowed maximum number of concurrent/active eth_newFilter registrations; >0 means no limit" so the intent is unambiguous.framework/src/test/java/org/tron/core/jsonrpc/HandleLogsFilterTest.java (1)
152-170: Ordering guarantee is not asserted.The objective requires preserving per-subscription event order, but Line 168–169 only checks counts. Add a case with multiple matching logs and assert ordered indices in each filter result.
🤖 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/jsonrpc/HandleLogsFilterTest.java` around lines 152 - 170, Update the testMultipleMatchingFilters_bothReceiveEvents test to verify ordering: instead of a single log, create multiple matching TransactionInfo entries (e.g., two or three) and send them via handleLogsFilter using the LogsFilterCapsule; after invoking handleLogsFilter, assert not only that filter1.getResult().size() and filter2.getResult().size() equal the expected count but also that the sequence of events in each filter's result preserves input order (check the per-result entries' indices or timestamps in the returned objects to match the creation order). Ensure you reference the existing filter instances (filter1, filter2), the handleLogsFilter call, and LogsFilterCapsule when adding and asserting ordered entries.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@framework/src/test/java/org/tron/core/jsonrpc/HandleLogsFilterTest.java`:
- Around line 51-185: Add a unit test that triggers the >10,000-entry parallel
branch in handleLogsFilter by populating the event-filter map with more than
10_000 LogFilterAndResult entries (use
TronJsonRpcImpl.getEventFilter2ResultFull()), then create a LogsFilterCapsule
with matching TransactionInfo and call handleLogsFilter(capsule); assert that at
least the intended filter(s) receive results (e.g., check
filter.getResult().size()) and that the test exercises the parallel path rather
than the serial one. Ensure you reference and use LogFilterAndResult,
FilterRequest, LogsFilterCapsule and handleLogsFilter to build the test, and
keep the filter expiration/state consistent with other tests so it is not
removed prematurely.
---
Nitpick comments:
In `@framework/src/main/resources/config.conf`:
- Around line 378-379: Update the config key description for maxLogFilterNum to
match reference.conf wording by explicitly referencing eth_newFilter and
clarifying it limits concurrent/active registrations (not eth_getLogs
retrievals); locate the maxLogFilterNum setting and adjust its comment text to
read something like "Allowed maximum number of concurrent/active eth_newFilter
registrations; >0 means no limit" so the intent is unambiguous.
In `@framework/src/test/java/org/tron/core/jsonrpc/HandleLogsFilterTest.java`:
- Around line 152-170: Update the testMultipleMatchingFilters_bothReceiveEvents
test to verify ordering: instead of a single log, create multiple matching
TransactionInfo entries (e.g., two or three) and send them via handleLogsFilter
using the LogsFilterCapsule; after invoking handleLogsFilter, assert not only
that filter1.getResult().size() and filter2.getResult().size() equal the
expected count but also that the sequence of events in each filter's result
preserves input order (check the per-result entries' indices or timestamps in
the returned objects to match the creation order). Ensure you reference the
existing filter instances (filter1, filter2), the handleLogsFilter call, and
LogsFilterCapsule when adding and asserting ordered entries.
🪄 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: 0a5ff1d3-86d5-4315-895f-d446dd1301d0
📒 Files selected for processing (3)
common/src/main/resources/reference.confframework/src/main/resources/config.confframework/src/test/java/org/tron/core/jsonrpc/HandleLogsFilterTest.java
…e addAll matchedLog instead after
e6354d0 to
ad0d688
Compare
|
You're iterating quickly on this pull request. To help protect your rate limits, cubic has paused automatic reviews on new pushes for now—when you're ready for another review, comment |
Problem
Three issues exist in the JSON-RPC log filter subsystem (
eth_newFilter/eth_getLogs), reported in tronprotocol#6510:Unbounded memory growth —
eth_newFilterimposes no limit on the number of active filters held in memory. A client can register filters indefinitely and eventually exhaust heap on the node.Correctness bug —
LogMatch.matchBlockOneByOneevaluated theMAX_RESULTguard afteraddAll, so the result list could transiently contain more thanMAX_RESULTentries before the exception was thrown.Performance bottleneck under high filter count —
handleLogsFilteriterated the filter map with anIteratorand calledresult.add()once per element, which is slow and contention-prone when thousands of filters are active.Changes
1. Enforce a configurable cap on active log filters
Adds
node.jsonrpc.maxLogFilterNum(default:20000). When the cap is reached,eth_newFilterimmediately returnsJsonRpcExceedLimitException(JSON-RPC code-32005) instead of growing without bound.2. Fix the MAX_RESULT boundary check in
LogMatch.matchBlockOneByOne(correctness)Moves the
size + matchedLog.size() > MAX_RESULTguard beforeaddAll. The result list now never exceedsMAX_RESULTentries regardless of how many logs a single block contributes.3. Optimize
handleLogsFilterfor large filter mapsIteratorloop,result.add()per elementConcurrentHashMap.forEachwith local list + singleaddAllForkJoinPool(2)+parallelStreamKey improvements:
addAllon the sharedLinkedBlockingQueue.ForkJoinPoolis created once and reused (namedlogs-filter-poolfor thread-dump visibility), avoiding unbounded thread creation under spike load.logger.debugtiming line records dispatch cost and filter-map size.Testing
HandleLogsFilterTest— 7 cases covering event dispatch, block-range filtering, expired-filter removal, and solidified/non-solidified routingLogMatchOverLimitTest— 4 cases covering under-limit, exact-limit, exceed-limit (verifies exception is thrown beforeaddAll), and empty-block skipWalletCursorTest.testNewFilter_exceedsCapThrowsException— verifies the cap throws the correct exception with the limit value in the messageCloses tronprotocol#6510
Summary by CodeRabbit
New Features
-32005when exceeded.Bug Fixes
Performance