Skip to content

[BUG] — HierarchicalWheelTimer: non-daemon worker thread never stopped on shutdown() + unbounded single-thread task executor #6668

Closed

Description

@Aias00
  • severity: High (leak) / Medium (bottleneck)
  • files: shenyu-common/src/main/java/org/apache/shenyu/common/timer/HierarchicalWheelTimer.java:83-86 (ShenyuThreadFactory.create(executorName, false) → non-daemon), :84-85 (taskExecutor = new ThreadPoolExecutor(1,1,0L,MS,new LinkedBlockingQueue<>())), :146-148 (shutdown only stops taskExecutor, never workerThread), :164-170 (Worker.run while(true) swallows InterruptedException)
  • description: shutdown() never stops/interrupts the non-daemon workerThread, so its while(true) loop keeps running and ignores interrupts → thread leak + JVM hangs on redeploy. All expired timer tasks run on a single-thread executor with an unbounded LinkedBlockingQueue → burst of simultaneous expirations or any slow task creates head-of-line blocking and unbounded queue growth. Used by FailbackRegistryRepository (client) and FallbackShenyuClientRegisterService (admin).
  • impact: Leaked non-daemon thread on redeploy; retry backpressure bottleneck.
  • suggested_fix: Make workerThread a daemon (or interrupt+join in shutdown); bound taskExecutor's queue with a real rejection handler.
  • confidence: High

Identified during the 2026-08-02 deep re-scan; full list in docs/scan2-2026-08-02/00-consolidated-critical-high.md.

Activity

  1. github-actions commented on Aug 3, 2026

    @github-actions
    Contributor

    👋 Hello @yuluo-yx

    A new issue has been opened by @Aias00:

    Please review when you have time. Thank you! 🙏

  2. Aias00 commented on Aug 3, 2026

    @Aias00
    ContributorAuthor

    Cross-link: related to the periodic-task / worker-thread lifecycle hardening pass #6608 (tracked there as a related, non-duplicate defect — this is an idempotent-start / worker-shutdown leak, not the exception-cancellation family). Folding into the same hardening pass if its scope is widened.

  3. sunnysabor commented on Oct 1, 2026

    @sunnysabor
    Contributor

    I checked the current upstream/master (09c6a528) against this report. The worker-thread shutdown part is already addressed by merged #6982 (58dd5a517): shutdown() interrupts the worker under the write lock, Worker.run() exits when interrupted, and HierarchicalWheelTimerTest#testShutdownStopsWorkerAndRejectsNewTasks joins the worker and asserts it has terminated.

    The unbounded LinkedBlockingQueue remains. That is a separate overload/backpressure question: a bounded queue needs an explicit overflow policy because rejecting an expired timer callback can leave caller state stale, while caller-runs can stall the timing-wheel worker. Could we narrow this issue to the queue/backpressure behavior and agree on the expected overload semantics, while marking the worker-leak portion resolved by #6982?

  4. wy471x commented on Oct 4, 2026

    @wy471x
    Contributor

    /assign

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions