Skip to content

Fix duplicate full-mode URI heartbeat registration after repeated refresh#6337

Open
Aias00 wants to merge 1 commit intoapache:masterfrom
Aias00:fix/full-mode-heartbeat-duplicate-registration
Open

Fix duplicate full-mode URI heartbeat registration after repeated refresh#6337
Aias00 wants to merge 1 commit intoapache:masterfrom
Aias00:fix/full-mode-heartbeat-duplicate-registration

Conversation

@Aias00
Copy link
Copy Markdown
Contributor

@Aias00 Aias00 commented May 7, 2026

Fixes duplicate full-mode URI registration when the client receives repeated ContextRefreshedEvent callbacks.

Summary

  • route full-mode Spring MVC and WebSocket registration through the shared one-time registration gate
  • remove duplicated direct publisher fields from the concrete listeners and reuse the base publisher access
  • add regression tests covering repeated refresh events for both full-mode listeners

Verification

  • You have read the contribution guidelines.
  • You submit test cases (unit or integration tests) that back your changes.
  • Your local test passed ./mvnw clean install -Dmaven.javadoc.skip=true.

Test command run:
mvn -pl shenyu-client/shenyu-client-core,shenyu-client/shenyu-client-http/shenyu-client-springmvc,shenyu-client/shenyu-client-websocket/shenyu-client-spring-websocket -am -DfailIfNoTests=false -Dtest=ShenyuClientURIExecutorSubscriberTest,SpringMvcClientEventListenerTest,SpringWebSocketClientEventListenerTest test

Copilot AI review requested due to automatic review settings May 7, 2026 01:24
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 prevents duplicate “full-mode” URI (and MVC metadata) heartbeat registrations when Spring emits ContextRefreshedEvent multiple times, by reusing a shared one-time registration gate in the client listeners and adding regression tests.

Changes:

  • Add a reusable markRegistered() hook in AbstractContextRefreshedEventListener and use it from full-mode Spring MVC + WebSocket listeners to ensure one-time registration.
  • Remove direct ShenyuClientRegisterEventPublisher fields from concrete listeners and consistently use the base getPublisher().
  • Add regression tests to ensure repeated refresh events don’t re-register in full-mode (MVC + WebSocket).

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
shenyu-client/shenyu-client-websocket/shenyu-client-spring-websocket/src/test/java/org/apache/shenyu/client/spring/websocket/init/SpringWebSocketClientEventListenerTest.java Adds regression test + refactors setup via a helper builder.
shenyu-client/shenyu-client-websocket/shenyu-client-spring-websocket/src/main/java/org/apache/shenyu/client/spring/websocket/init/SpringWebSocketClientEventListener.java Gates full-mode URI publishing through one-time registration and uses base publisher.
shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/test/java/org/apache/shenyu/client/springmvc/init/SpringMvcClientEventListenerTest.java Adds regression test ensuring full-mode repeated refresh doesn’t duplicate registrations.
shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/main/java/org/apache/shenyu/client/springmvc/init/SpringMvcClientEventListener.java Gates full-mode URI/metadata publishing through one-time registration and uses base publisher.
shenyu-client/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/client/AbstractContextRefreshedEventListener.java Introduces markRegistered() helper and reuses it in the base event handler.
.github/workflows/k8s-examples-http.yml Simplifies Maven caching via setup-java cache and streamlines Linux-only build steps.
.github/workflows/integrated-test.yml Simplifies Maven caching via setup-java cache and streamlines Linux-only build steps.
.github/workflows/integrated-test-k8s-ingress.yml Simplifies Maven caching via setup-java cache and streamlines Linux-only build steps.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 89 to +92
@BeforeEach
public void setUp() {
MockitoAnnotations.openMocks(this);
Properties properties = mock(Properties.class);
when(properties.getProperty("appName")).thenReturn("appName");
when(properties.getProperty("contextPath")).thenReturn("contextPath");
when(properties.getProperty(ShenyuClientConstants.PORT)).thenReturn("8080");
when(properties.getProperty(ShenyuClientConstants.HOST)).thenReturn("127.0.0.1");
when(properties.getProperty(ShenyuClientConstants.IP_PORT)).thenReturn("127.0.0.1:8080");

ShenyuClientConfig clientConfig = mock(ShenyuClientConfig.class);
Map<String, ClientPropertiesConfig> client = new HashMap<>();
ClientPropertiesConfig clientPropertiesConfig = new ClientPropertiesConfig();
clientPropertiesConfig.setProps(properties);
client.put(RpcTypeEnum.WEB_SOCKET.getName(), clientPropertiesConfig);
when(clientConfig.getClient()).thenReturn(client);
eventListener = new SpringWebSocketClientEventListener(clientConfig, registerRepository);
eventListener = buildEventListener(false);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Removed MockitoAnnotations.openMocks(this) and the unused import. @ExtendWith(MockitoExtension.class) now owns mock initialization for this test.

…resh

Full-mode client listeners published URI registration before the shared
one-time registration gate ran, so repeated ContextRefreshedEvent
invocations could enqueue the same URI multiple times and amplify
heartbeat logging. This change routes full-mode registration through the
same gate as the standard listener flow and locks the behavior with
Spring MVC and WebSocket regression tests.

Constraint: Full-mode registration must preserve standard one-shot listener semantics
Rejected: Deduplicate in the heartbeat subscriber | leaves duplicate registration side effects in place
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep any full-mode registration path behind the shared registration gate
Tested: mvn -pl shenyu-client/shenyu-client-core,shenyu-client/shenyu-client-http/shenyu-client-springmvc,shenyu-client/shenyu-client-websocket/shenyu-client-spring-websocket -am -DfailIfNoTests=false -Dtest=ShenyuClientURIExecutorSubscriberTest,SpringMvcClientEventListenerTest,SpringWebSocketClientEventListenerTest test
Not-tested: Live expos-admin-server runtime verification
@Aias00 Aias00 force-pushed the fix/full-mode-heartbeat-duplicate-registration branch from 679a749 to 7fb6e22 Compare May 7, 2026 01:36
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.

2 participants