Skip to content

[Feature] decouple json-rpc filter processing from Manager #6963

Description

@0xbigapple

Summary

The json-rpc filter event-processing thread currently is located within Manager (core layer), forcing it to hold TronJsonRpcImpl (API layer) via a @Lazy injection — a reverse circular dependency. This proposal moves that thread into TronJsonRpcImpl; Manager only posts filter events into a standalone queue bean, and the reverse edge disappears.

Problem

Motivation

#6732 turned handleLogsFilter/handleBLockFilter from static into instance methods, so Manager's event-processing thread needs a TronJsonRpcImpl instance; the review deferred the resulting @Lazy cycle to a follow-up (discussion) — this issue serves as the dedicated follow-up to address that technical debt.

Current State

Manager ──@Lazy @Autowired field──▶ TronJsonRpcImpl ──@Autowired field──▶ Manager

Each arrow reads "depends on"; the labels are injection styles. TronJsonRpcImpl's constructor parameters Wallet/NodeInfoService each depend on Manager as well, closing the same cycle indirectly.

Manager owns the filter queue, the consumer thread, and its shutdown; the consumer calls back into the API layer.

Limitations or Risks

  • FullNode sets allowCircularReferences(false), so a directly injected cycle is rejected at startup; @Lazy (like ObjectProvider or a runtime getBean()) only makes Spring tolerate the cycle — the reverse edge stays, obscuring the component graph and complicating lifecycle changes.
  • The reverse core → API edge risks encouraging future changes to repeat the same @Lazy pattern, widening the breach.
  • One latent defect remains in the current implementation: the consumer stop flag is a non-volatile boolean (the shutdown-time write has no visibility guarantee for that thread), and the loop does not exit on InterruptedException — under the Java memory model, neither the normal nor the forced stop path is reliable.

Proposed Solution

Proposed Design

Eliminate the reverse dependency entirely rather than suppressing it via @lazy: a standalone FilterCapsuleQueue bean is introduced as an intermediary between the two layers — Manager only produces into it; TronJsonRpcImpl owns the consumer thread (start on @PostConstruct, stop via idempotent close()). ApplicationImpl.shutdown() calls close() explicitly after producers stop and before dbManager.close() — Spring destroys beans only after the database closes, so bean destruction alone would let the consumer outlive it (ApplicationImpl is already the shutdown-orchestrating composition root). Filter API behavior stays unchanged (unbounded queue; pending entries are not drained on shutdown, while an in-flight capsule may complete).

Key Changes

  • Module: framework only
  • org.tron.common.logsfilter.queue.FilterCapsuleQueue (new bean, beside the existing org.tron.common.logsfilter.capsule package)
  • Manager: drop the @Lazy field, the consumer loop, and its thread lifecycle
  • TronJsonRpcImpl: constructor gains a Manager parameter, consumer lifecycle, rewritten close() ordering (stop and await the consumer first, then shut down logsFilterPool)
  • ApplicationImpl.shutdown(): explicit consumer stop
  • No configuration or API changes

Impact

  • Stability: fixes the latent stop-path defect above along the way; graceful shutdown stops the consumer before the database closes.
  • Performance: no steady-state change expected — processing path, queue, and thread count are unchanged.
  • Developer Experience: the core layer no longer references the API layer; framework/src/main becomes @Lazy-free.

Compatibility

  • Breaking Change: No
  • Default Behavior Change: No — filter delivery, queue semantics, and shutdown semantics are preserved
  • Migration Required: No

Additional Notes

  • Do you have ideas regarding implementation? Yes
  • Are you willing to implement this feature? Yes

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions