Skip to content

[ISSUE #6781] Fix stale proxy selector data after empty HTTP refresh. - #6909

Merged
Aias00 merged 5 commits into
apache:masterfrom
lymerin:fix/6781-proxy-selector-empty-refresh
Aug 14, 2026
Merged

[ISSUE #6781] Fix stale proxy selector data after empty HTTP refresh.#6909
Aias00 merged 5 commits into
apache:masterfrom
lymerin:fix/6781-proxy-selector-empty-refresh

Conversation

@lymerin

@lymerin lymerin commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Fixes #6781.

This PR clears stale proxy selector runtime data when HTTP synchronization receives an empty full snapshot.

Changes:

  • propagate empty proxy selector snapshots to subscribers through refresh();
  • propagate refresh operations to proxy selector handlers;
  • shut down and remove cached TCP bootstrap servers;
  • continue clearing remaining servers when one shutdown fails;
  • avoid a null-pointer race between individual deletion and full refresh;
  • add unit, integration, and public HTTP synchronization path tests.

Although the issue is triggered by HTTP synchronization, CommonProxySelectorDataSubscriber is shared. Any synchronization mode that invokes refresh() will now clear the TCP proxy selector runtime cache.

Tests:

  • full repository clean install passed;
  • affected 14-module Maven reactor passed;
  • Checkstyle passed with 0 violations;
  • git diff --check passed.

Make sure that:

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes stale TCP proxy selector runtime state when HTTP synchronization receives an empty full snapshot by ensuring empty snapshots trigger refresh() propagation through proxy selector subscribers/handlers, and by clearing/shutting down cached TCP bootstrap servers.

Changes:

  • Invoke ProxySelectorDataSubscriber.refresh() when ProxySelectorRefresh.refresh(...) receives an empty snapshot.
  • Propagate refresh from CommonProxySelectorDataSubscriber into ProxySelectorDataHandler.refresh() and implement TCP handler refresh to clear the bootstrap cache.
  • Add unit/integration tests covering empty snapshot refresh propagation and TCP bootstrap cache shutdown/clearing behavior (including failure tolerance).

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated no comments.

Show a summary per file
File Description
shenyu-sync-data-center/shenyu-sync-data-http/src/main/java/org/apache/shenyu/sync/data/http/refresh/ProxySelectorRefresh.java Ensures empty proxy selector snapshots propagate refresh to subscribers to clear stale runtime state.
shenyu-sync-data-center/shenyu-sync-data-http/src/test/java/org/apache/shenyu/sync/data/http/refresh/ProxySelectorRefreshTest.java Adds unit coverage verifying empty snapshot triggers subscriber.refresh() and non-empty snapshots subscribe data.
shenyu-sync-data-center/shenyu-sync-data-http/src/test/java/org/apache/shenyu/sync/data/http/HttpSyncDataServiceTest.java Extends HTTP sync test fixture/expectations to include PROXY_SELECTOR group and verify refresh is invoked.
shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/handler/ProxySelectorDataHandler.java Adds a default refresh() hook for proxy selector handlers.
shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/cache/CommonProxySelectorDataSubscriber.java Implements subscriber-level refresh() by delegating to all handler refresh operations.
shenyu-plugin/shenyu-plugin-base/src/test/java/org/apache/shenyu/plugin/base/cache/CommonProxySelectorDataSubscriberTest.java Adds tests asserting refresh() is propagated to all handlers and is safe with no handlers.
shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tcp/src/main/java/org/apache/shenyu/plugin/tcp/handler/TcpProxySelectorDataHandler.java Makes TCP handler refresh clear the bootstrap cache; hardens removal against null cache entries.
shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tcp/src/main/java/org/apache/shenyu/plugin/tcp/handler/TcpBootstrapFactory.java Adds cache clearing that shuts down servers, removes entries, and continues on shutdown failures.
shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tcp/src/test/java/org/apache/shenyu/plugin/tcp/handler/TcpProxySelectorDataHandlerTest.java Adds coverage for refresh-driven shutdown/removal, failure-tolerant shutdown, and idempotent selector removal.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@Aias00 Aias00 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review: #6909 — [ISSUE #6781] Fix stale proxy selector data after empty HTTP refresh

Verdict: ✅ Approve (with minor non-blocking notes)

This is a clean, well-scoped fix for #6781. Good work.

What's correct

  • Root cause addressed. CommonProxySelectorDataSubscriber.refresh() previously just called ProxySelectorDataSubscriber.super.refresh(), which I verified is a no-op (default void refresh() {} in the interface). So an empty full snapshot never cleared proxy-selector runtime data. The new implementation delegates to each ProxySelectorDataHandler.refresh(), which actually tears down state. This is strictly an improvement with no behavior lost from the (empty) interface default.
  • ProxySelectorDataHandler.refresh() added as a default method → existing handlers keep compiling; only the TCP handler overrides it. Good backward-compat design.
  • TcpBootstrapFactory.clearCache() shuts down each cached BootstrapServer with a try/catch so one failing shutdown doesn't abort the rest — exactly the "continue clearing remaining servers when one fails" goal. Using the value-conditional cache.remove(name, server) is the safe way to mutate a ConcurrentHashMap during iteration (no CME, no stale removal).
  • TcpProxySelectorDataHandler.removeProxySelector now does removeCache() + null-check before .shutdown(), closing a TOCTOU race where the old inCache()-then-removeCache().shutdown() could NPE if another thread removed the entry in between.
  • Propagation wired through ProxySelectorRefresh: empty snapshot now calls proxySelectorDataSubscribers.forEach(ProxySelectorDataSubscriber::refresh).
  • Test coverage is solid: subscriber refresh (with/without handlers), TCP handler refresh incl. the "continues when shutdown fails" case and removeProxySelector, the HTTP sync data-service path, and a dedicated ProxySelectorRefreshTest asserting empty→clear and non-empty→subscribe.

Non-blocking suggestions

  1. Empty-snapshot semantics are global. Any sync mode that sends an empty PROXY_SELECTOR snapshot will now hard-stop all TCP bootstrap servers. That matches the documented intent (#6781), but it's worth a one-line comment in ProxySelectorRefresh.refresh() noting that an empty snapshot = "clear everything", so future readers don't treat it as a no-op/clear-local-only path.
  2. clearCache() mutates the map from inside forEach. Current implementation is safe for ConcurrentHashMap, but if shutdown() ever triggers re-registration (a lifecycle listener), a server could be missed. Not an issue today — just flagging the invariant to preserve.

Verdict

Approving. The fix is correct, the NPE race is real, and the regression tests back the behavior. Nice job.

@Aias00

Aias00 commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

thx for contribution, pls add my wechat: aias00

@Aias00
Aias00 merged commit 8f141a1 into apache:master Aug 14, 2026
50 of 51 checks passed
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.

[BUG] ProxySelectorRefresh.refresh() does not clear stale data on empty refresh (HTTP)

3 participants