This is an automated email from the ASF dual-hosted git repository.
Aias00 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shenyu.git
The following commit(s) were added to refs/heads/master by this push:
new 0e3404562b fix(consul): keep TTL heartbeats beating after a failed
check (#7432)
0e3404562b is described below
commit 0e3404562b4958529839a9286da2542d54fc187e
Author: Sean-Walker0 <[email protected]>
AuthorDate: Thu Oct 8 15:03:35 2026 +0800
fix(consul): keep TTL heartbeats beating after a failed check (#7432)
ConsulHeartbeatTask called client.agentCheckPass directly inside a
scheduleAtFixedRate task with no exception guard.
ScheduledThreadPoolExecutor
suppresses all later executions of a periodic task once an exception
escapes it, so one transient Consul error permanently stopped the TTL
heartbeat and the registered instance was eventually marked unhealthy.
Catch the failure, log it, and let the next scheduled beat retry.
Regression test: the first agentCheckPass throws, the second succeeds,
and the heartbeat must fire at least twice (red on the previous code,
which stopped after the first failure).
Fixes #6498
Co-authored-by: Sean-Walker0
<[email protected]>
---
.../shenyu/registry/consul/TtlScheduler.java | 12 ++++--
.../shenyu/registry/consul/TtlSchedulerTest.java | 48 ++++++++++++++++++++++
2 files changed, 57 insertions(+), 3 deletions(-)
diff --git
a/shenyu-registry/shenyu-registry-consul/src/main/java/org/apache/shenyu/registry/consul/TtlScheduler.java
b/shenyu-registry/shenyu-registry-consul/src/main/java/org/apache/shenyu/registry/consul/TtlScheduler.java
index 583390fa50..71c143b87f 100644
---
a/shenyu-registry/shenyu-registry-consul/src/main/java/org/apache/shenyu/registry/consul/TtlScheduler.java
+++
b/shenyu-registry/shenyu-registry-consul/src/main/java/org/apache/shenyu/registry/consul/TtlScheduler.java
@@ -95,9 +95,15 @@ public class TtlScheduler {
@Override
public void run() {
- TtlScheduler.this.client.agentCheckPass(this.checkId);
- if (log.isDebugEnabled()) {
- log.debug("Sending consul heartbeat for: " + this.checkId);
+ try {
+ TtlScheduler.this.client.agentCheckPass(this.checkId);
+ if (log.isDebugEnabled()) {
+ log.debug("Sending consul heartbeat for: " + this.checkId);
+ }
+ } catch (Exception e) {
+ // an exception escaping a fixed-rate task suppresses all
later executions,
+ // so a single failed heartbeat must only be logged to keep
beating
+ log.error("Sending consul heartbeat for: " + this.checkId + "
failed", e);
}
}
}
diff --git
a/shenyu-registry/shenyu-registry-consul/src/test/java/org/apache/shenyu/registry/consul/TtlSchedulerTest.java
b/shenyu-registry/shenyu-registry-consul/src/test/java/org/apache/shenyu/registry/consul/TtlSchedulerTest.java
new file mode 100644
index 0000000000..1fec36305d
--- /dev/null
+++
b/shenyu-registry/shenyu-registry-consul/src/test/java/org/apache/shenyu/registry/consul/TtlSchedulerTest.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.registry.consul;
+
+import com.ecwid.consul.v1.ConsulClient;
+import org.junit.jupiter.api.Test;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * Test case for {@link TtlScheduler}.
+ */
+public final class TtlSchedulerTest {
+
+ @Test
+ public void heartbeatKeepsBeatingAfterFailure() {
+ final ConsulClient client = mock(ConsulClient.class);
+ when(client.agentCheckPass(anyString()))
+ .thenThrow(new RuntimeException("consul temporarily
unavailable"))
+ .thenReturn(null);
+
+ final TtlScheduler ttlScheduler = new TtlScheduler(1, client);
+ ttlScheduler.add("test-service");
+
+ verify(client,
timeout(5000).atLeast(2)).agentCheckPass(eq("service:test-service"));
+ ttlScheduler.shutdown();
+ }
+}