From d5b60ce88c5b71f07c9f492282e37ec16ed180c5 Mon Sep 17 00:00:00 2001 From: Au1s1 <75768170+Wubabalala@users.noreply.github.com> Date: Thu, 19 Mar 2026 12:50:40 +0800 Subject: [PATCH 1/4] fix(adaptive-lb): replace broken equality-based penalty with explicit freshness tracking Replace the incorrect currentProviderTime == currentTime penalty trigger with a volatile boolean providerUpdated flag. When fresh metrics exist, use real latency. When decaying, apply a floor of max(1, timeout/100). Closes #15810 --- .../org/apache/dubbo/rpc/AdaptiveMetrics.java | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AdaptiveMetrics.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AdaptiveMetrics.java index dfd077f947a..4627c6b8774 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AdaptiveMetrics.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AdaptiveMetrics.java @@ -46,6 +46,14 @@ public class AdaptiveMetrics { private final AtomicLong errorReq = new AtomicLong(); private double ewma = 0; + // Coalescing freshness signal: set by setProviderMetrics(), cleared by getLoad(). + // Indicates whether new provider metrics have arrived since the last getLoad() call. + // volatile for cross-thread visibility (setProviderMetrics runs in async executor, + // getLoad runs in caller thread). Not an event counter — intermediate updates are + // coalesced, which is acceptable because lastLatency and ewma are already updated + // to the latest values in setProviderMetrics(). + private volatile boolean providerUpdated = false; + public double getLoad(String idKey, int weight, int timeout) { AdaptiveMetrics metrics = getStatus(idKey); @@ -57,11 +65,13 @@ public double getLoad(String idKey, int weight, int timeout) { if (metrics.currentTime > 0) { long multiple = (System.currentTimeMillis() - metrics.currentTime) / timeout + 1; if (multiple > 0) { - if (metrics.currentProviderTime == metrics.currentTime) { - // penalty value - metrics.lastLatency = timeout * 2L; + if (metrics.providerUpdated) { + // Fresh metrics arrived — use real lastLatency (already set by setProviderMetrics) + metrics.providerUpdated = false; } else { - metrics.lastLatency = metrics.lastLatency >> multiple; + // No fresh metrics — decay with floor to prevent collapse to zero + long floor = Math.max(1L, timeout / 100L); + metrics.lastLatency = Math.max(floor, metrics.lastLatency >> multiple); } metrics.ewma = metrics.beta * metrics.ewma + (1 - metrics.beta) * metrics.lastLatency; metrics.currentTime = System.currentTimeMillis(); @@ -123,5 +133,6 @@ public void setProviderMetrics(String idKey, Map metricsMap) { metrics.beta = 0.5; // Vt = β * Vt-1 + (1 - β ) * θt metrics.ewma = metrics.beta * metrics.ewma + (1 - metrics.beta) * metrics.lastLatency; + metrics.providerUpdated = true; } } From b576ccd598a79cd7cd283d54d64de55ed37937e1 Mon Sep 17 00:00:00 2001 From: Au1s1 <75768170+Wubabalala@users.noreply.github.com> Date: Thu, 19 Mar 2026 12:52:09 +0800 Subject: [PATCH 2/4] test: add AdaptiveMetrics unit tests for penalty and decay fix 6 test cases through public API (no reflection, no Thread.sleep): - freshMetrics_usesRealLatency_notPenalty - decayWithoutUpdate_doesNotReachZero - multipleServers_fastServerPreferred - degradedServer_loadIncreases - outOfOrderServiceTime_isDiscarded - forcedPick_notBroken --- .../apache/dubbo/rpc/AdaptiveMetricsTest.java | 223 ++++++++++++++++++ 1 file changed, 223 insertions(+) create mode 100644 dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/AdaptiveMetricsTest.java diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/AdaptiveMetricsTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/AdaptiveMetricsTest.java new file mode 100644 index 00000000000..134a882c422 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/AdaptiveMetricsTest.java @@ -0,0 +1,223 @@ +/* + * 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.dubbo.rpc; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Unit tests for {@link AdaptiveMetrics}. + * + *

All tests drive through the public API only (no reflection, no Thread.sleep). + * Verifies the fix for #15810: + *

+ */ +class AdaptiveMetricsTest { + + private static final String FAST_KEY = "10.0.0.1:20880/com.example.TestService"; + private static final String SLOW_KEY = "10.0.0.2:20880/com.example.TestService"; + private static final String MEDIUM_KEY = "10.0.0.3:20880/com.example.TestService"; + private static final int WEIGHT = 100; + private static final int TIMEOUT = 100; + + private Map metricsMap(long curTime, long rt, String load) { + Map map = new HashMap<>(); + map.put("curTime", String.valueOf(curTime)); + map.put("rt", String.valueOf(rt)); + map.put("load", load); + return map; + } + + /** + * Bug 1 regression: after setProviderMetrics, getLoad should use the real RT, + * not overwrite it with timeout * 2. A fast server (10ms) must have strictly + * lower load than a slow server (200ms). + */ + @Test + void freshMetrics_usesRealLatency_notPenalty() { + AdaptiveMetrics am = new AdaptiveMetrics(); + long now = System.currentTimeMillis(); + + // Fast server: 10ms RT + am.addConsumerReq(FAST_KEY); + am.setProviderMetrics(FAST_KEY, metricsMap(now, 10, "0.5")); + am.addConsumerSuccess(FAST_KEY); + + double fastLoad = am.getLoad(FAST_KEY, WEIGHT, TIMEOUT); + + // Slow server: 200ms RT + am.addConsumerReq(SLOW_KEY); + am.setProviderMetrics(SLOW_KEY, metricsMap(now, 200, "0.5")); + am.addConsumerSuccess(SLOW_KEY); + + double slowLoad = am.getLoad(SLOW_KEY, WEIGHT, TIMEOUT); + + // With the penalty bug, both would converge to timeout*2 → loads nearly equal + assertTrue(fastLoad < slowLoad, + "Fast server (10ms) should have lower load than slow server (200ms), " + + "got fast=" + fastLoad + " slow=" + slowLoad); + } + + /** + * Bug 2 regression: after multiple decay rounds without new provider metrics, + * load must remain above zero. Decay-to-zero would make idle servers appear + * "fastest" and attract all traffic. + */ + @Test + void decayWithoutUpdate_doesNotReachZero() { + AdaptiveMetrics am = new AdaptiveMetrics(); + long now = System.currentTimeMillis(); + + am.addConsumerReq(FAST_KEY); + am.setProviderMetrics(FAST_KEY, metricsMap(now, 200, "0.5")); + am.addConsumerSuccess(FAST_KEY); + + // First getLoad consumes the providerUpdated flag + am.getLoad(FAST_KEY, WEIGHT, TIMEOUT); + + // Multiple getLoad calls without new metrics — all enter decay branch + double load = 0; + for (int i = 0; i < 50; i++) { + load = am.getLoad(FAST_KEY, WEIGHT, TIMEOUT); + } + + // With the bug: lastLatency >> large_multiple = 0, ewma → 0, load → 0 + assertTrue(load > 0, "Load should not decay to zero after idle period, got " + load); + } + + /** + * End-to-end: with 3 servers at different RTs, adaptive metrics should + * consistently rank them correctly after multiple rounds. + */ + @Test + void multipleServers_fastServerPreferred() { + AdaptiveMetrics am = new AdaptiveMetrics(); + + // Simulate 20 rounds of traffic + for (int round = 0; round < 20; round++) { + long now = System.currentTimeMillis(); + + am.addConsumerReq(FAST_KEY); + am.setProviderMetrics(FAST_KEY, metricsMap(now, 10, "0.5")); + am.addConsumerSuccess(FAST_KEY); + + am.addConsumerReq(MEDIUM_KEY); + am.setProviderMetrics(MEDIUM_KEY, metricsMap(now, 50, "0.5")); + am.addConsumerSuccess(MEDIUM_KEY); + + am.addConsumerReq(SLOW_KEY); + am.setProviderMetrics(SLOW_KEY, metricsMap(now, 200, "0.5")); + am.addConsumerSuccess(SLOW_KEY); + } + + double loadFast = am.getLoad(FAST_KEY, WEIGHT, TIMEOUT); + double loadMedium = am.getLoad(MEDIUM_KEY, WEIGHT, TIMEOUT); + double loadSlow = am.getLoad(SLOW_KEY, WEIGHT, TIMEOUT); + + // Strict ordering: fast < medium < slow + assertTrue(loadFast < loadMedium, + "fast(" + loadFast + ") should be < medium(" + loadMedium + ")"); + assertTrue(loadMedium < loadSlow, + "medium(" + loadMedium + ") should be < slow(" + loadSlow + ")"); + } + + /** + * A server that degrades (RT jumps from 10ms to 500ms) should see its + * load score increase, so the algorithm can shift traffic away. + */ + @Test + void degradedServer_loadIncreases() { + AdaptiveMetrics am = new AdaptiveMetrics(); + + // Warm up with fast responses + for (int i = 0; i < 10; i++) { + long now = System.currentTimeMillis(); + am.addConsumerReq(FAST_KEY); + am.setProviderMetrics(FAST_KEY, metricsMap(now, 10, "0.5")); + am.addConsumerSuccess(FAST_KEY); + } + double loadBefore = am.getLoad(FAST_KEY, WEIGHT, TIMEOUT); + + // Server degrades: RT jumps to 500ms, CPU load spikes + for (int i = 0; i < 10; i++) { + long now = System.currentTimeMillis(); + am.addConsumerReq(FAST_KEY); + am.setProviderMetrics(FAST_KEY, metricsMap(now, 500, "0.9")); + am.addConsumerSuccess(FAST_KEY); + } + double loadAfter = am.getLoad(FAST_KEY, WEIGHT, TIMEOUT); + + assertTrue(loadAfter > loadBefore, + "Load should increase after degradation: before=" + loadBefore + " after=" + loadAfter); + } + + /** + * Guard: out-of-order serviceTime should be discarded by setProviderMetrics. + * An older timestamp must not overwrite newer metrics. + */ + @Test + void outOfOrderServiceTime_isDiscarded() { + AdaptiveMetrics am = new AdaptiveMetrics(); + long now = System.currentTimeMillis(); + + // Set metrics with current time, RT=50ms + am.addConsumerReq(FAST_KEY); + am.setProviderMetrics(FAST_KEY, metricsMap(now, 50, "0.5")); + am.addConsumerSuccess(FAST_KEY); + double loadAfterFresh = am.getLoad(FAST_KEY, WEIGHT, TIMEOUT); + + // Attempt to set metrics with an older timestamp, RT=999ms + am.addConsumerReq(FAST_KEY); + am.setProviderMetrics(FAST_KEY, metricsMap(now - 10000, 999, "1.0")); + am.addConsumerSuccess(FAST_KEY); + double loadAfterStale = am.getLoad(FAST_KEY, WEIGHT, TIMEOUT); + + // The stale update should have been discarded — load should not spike + // (if the stale 999ms RT were accepted, loadAfterStale would be much higher) + assertTrue(loadAfterStale <= loadAfterFresh, + "Stale metrics should be discarded: loadAfterFresh=" + loadAfterFresh + + " loadAfterStale=" + loadAfterStale); + } + + /** + * Guard: when pickTime exceeds timeout * 2, getLoad must return 0 (forced pick) + * to prevent starvation. This behavior must not be broken by the fix. + */ + @Test + void forcedPick_notBroken() { + AdaptiveMetrics am = new AdaptiveMetrics(); + long now = System.currentTimeMillis(); + + am.addConsumerReq(FAST_KEY); + am.setProviderMetrics(FAST_KEY, metricsMap(now, 50, "0.5")); + am.addConsumerSuccess(FAST_KEY); + + // Set pickTime far in the past → triggers forced pick (return 0) + am.setPickTime(FAST_KEY, now - TIMEOUT * 3); + + double load = am.getLoad(FAST_KEY, WEIGHT, TIMEOUT); + assertEquals(0.0, load, "getLoad should return 0 when pickTime exceeds timeout * 2"); + } +} From 67ac35eac88fb6775e2434476dc0ac47f7b43413 Mon Sep 17 00:00:00 2001 From: Au1s1 <75768170+Wubabalala@users.noreply.github.com> Date: Thu, 19 Mar 2026 13:12:27 +0800 Subject: [PATCH 3/4] test: strengthen Bug 1 tests to fail on buggy code - freshMetrics: each round does setProviderMetrics+getLoad (the real penalty trigger path), asserts slow/fast ratio > 2x - multipleServers: same per-round pattern instead of batch - degradedServer: hold CPU constant, only change RT to isolate signal --- .../apache/dubbo/rpc/AdaptiveMetricsTest.java | 84 +++++++++++-------- 1 file changed, 50 insertions(+), 34 deletions(-) diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/AdaptiveMetricsTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/AdaptiveMetricsTest.java index 134a882c422..4c5eb3b68e9 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/AdaptiveMetricsTest.java +++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/AdaptiveMetricsTest.java @@ -51,33 +51,43 @@ private Map metricsMap(long curTime, long rt, String load) { } /** - * Bug 1 regression: after setProviderMetrics, getLoad should use the real RT, - * not overwrite it with timeout * 2. A fast server (10ms) must have strictly - * lower load than a slow server (200ms). + * Bug 1 core regression: simulate the real request cycle where each + * setProviderMetrics() is immediately followed by a getLoad() call. + * This is the exact path that triggers the penalty bug on main branch. + * + *

With the bug: penalty overwrites all RTs to timeout*2, so after + * 20 rounds fast(10ms) EWMA ≈ 137 and slow(200ms) EWMA ≈ 200, ratio < 1.5x. + * With the fix: fast EWMA stays near 10, slow near 200, ratio > 5x. */ @Test void freshMetrics_usesRealLatency_notPenalty() { AdaptiveMetrics am = new AdaptiveMetrics(); - long now = System.currentTimeMillis(); - - // Fast server: 10ms RT - am.addConsumerReq(FAST_KEY); - am.setProviderMetrics(FAST_KEY, metricsMap(now, 10, "0.5")); - am.addConsumerSuccess(FAST_KEY); - double fastLoad = am.getLoad(FAST_KEY, WEIGHT, TIMEOUT); + // 20 rounds: each round = setProviderMetrics + immediate getLoad (the bug path) + double fastLoad = 0; + double slowLoad = 0; + for (int i = 0; i < 20; i++) { + long now = System.currentTimeMillis(); - // Slow server: 200ms RT - am.addConsumerReq(SLOW_KEY); - am.setProviderMetrics(SLOW_KEY, metricsMap(now, 200, "0.5")); - am.addConsumerSuccess(SLOW_KEY); + am.addConsumerReq(FAST_KEY); + am.setProviderMetrics(FAST_KEY, metricsMap(now, 10, "0.5")); + am.addConsumerSuccess(FAST_KEY); + fastLoad = am.getLoad(FAST_KEY, WEIGHT, TIMEOUT); - double slowLoad = am.getLoad(SLOW_KEY, WEIGHT, TIMEOUT); + am.addConsumerReq(SLOW_KEY); + am.setProviderMetrics(SLOW_KEY, metricsMap(now, 200, "0.5")); + am.addConsumerSuccess(SLOW_KEY); + slowLoad = am.getLoad(SLOW_KEY, WEIGHT, TIMEOUT); + } - // With the penalty bug, both would converge to timeout*2 → loads nearly equal - assertTrue(fastLoad < slowLoad, - "Fast server (10ms) should have lower load than slow server (200ms), " - + "got fast=" + fastLoad + " slow=" + slowLoad); + // Strong assertion: the ratio must be > 2x. + // With penalty bug: ratio ≈ 1.46 (136.7 vs 200.0 EWMA). Fails. + // With fix: ratio ≈ 20x (10 vs 200 EWMA). Passes easily. + assertTrue(fastLoad > 0, "fastLoad should be > 0"); + assertTrue(slowLoad / fastLoad > 2.0, + "Slow/fast load ratio should be > 2x to prove real RT is used, " + + "got ratio=" + (slowLoad / fastLoad) + + " (fast=" + fastLoad + " slow=" + slowLoad + ")"); } /** @@ -103,39 +113,43 @@ void decayWithoutUpdate_doesNotReachZero() { load = am.getLoad(FAST_KEY, WEIGHT, TIMEOUT); } - // With the bug: lastLatency >> large_multiple = 0, ewma → 0, load → 0 + // With the bug: lastLatency >> large_multiple = 0, ewma -> 0, load -> 0 assertTrue(load > 0, "Load should not decay to zero after idle period, got " + load); } /** - * End-to-end: with 3 servers at different RTs, adaptive metrics should - * consistently rank them correctly after multiple rounds. + * End-to-end: with 3 servers at different RTs, simulate the real request + * cycle (setProviderMetrics + getLoad each round). After 20 rounds the + * ordering must be strictly fast < medium < slow. */ @Test void multipleServers_fastServerPreferred() { AdaptiveMetrics am = new AdaptiveMetrics(); - // Simulate 20 rounds of traffic + double loadFast = 0; + double loadMedium = 0; + double loadSlow = 0; + + // Each round: report metrics then immediately call getLoad (the real path) for (int round = 0; round < 20; round++) { long now = System.currentTimeMillis(); am.addConsumerReq(FAST_KEY); am.setProviderMetrics(FAST_KEY, metricsMap(now, 10, "0.5")); am.addConsumerSuccess(FAST_KEY); + loadFast = am.getLoad(FAST_KEY, WEIGHT, TIMEOUT); am.addConsumerReq(MEDIUM_KEY); am.setProviderMetrics(MEDIUM_KEY, metricsMap(now, 50, "0.5")); am.addConsumerSuccess(MEDIUM_KEY); + loadMedium = am.getLoad(MEDIUM_KEY, WEIGHT, TIMEOUT); am.addConsumerReq(SLOW_KEY); am.setProviderMetrics(SLOW_KEY, metricsMap(now, 200, "0.5")); am.addConsumerSuccess(SLOW_KEY); + loadSlow = am.getLoad(SLOW_KEY, WEIGHT, TIMEOUT); } - double loadFast = am.getLoad(FAST_KEY, WEIGHT, TIMEOUT); - double loadMedium = am.getLoad(MEDIUM_KEY, WEIGHT, TIMEOUT); - double loadSlow = am.getLoad(SLOW_KEY, WEIGHT, TIMEOUT); - // Strict ordering: fast < medium < slow assertTrue(loadFast < loadMedium, "fast(" + loadFast + ") should be < medium(" + loadMedium + ")"); @@ -145,32 +159,35 @@ void multipleServers_fastServerPreferred() { /** * A server that degrades (RT jumps from 10ms to 500ms) should see its - * load score increase, so the algorithm can shift traffic away. + * load score increase. CPU load is held constant to isolate the RT signal. */ @Test void degradedServer_loadIncreases() { AdaptiveMetrics am = new AdaptiveMetrics(); - // Warm up with fast responses + // Warm up with fast responses (CPU fixed at 0.5) for (int i = 0; i < 10; i++) { long now = System.currentTimeMillis(); am.addConsumerReq(FAST_KEY); am.setProviderMetrics(FAST_KEY, metricsMap(now, 10, "0.5")); am.addConsumerSuccess(FAST_KEY); + am.getLoad(FAST_KEY, WEIGHT, TIMEOUT); } double loadBefore = am.getLoad(FAST_KEY, WEIGHT, TIMEOUT); - // Server degrades: RT jumps to 500ms, CPU load spikes + // Server degrades: RT jumps to 500ms, CPU stays at 0.5 for (int i = 0; i < 10; i++) { long now = System.currentTimeMillis(); am.addConsumerReq(FAST_KEY); - am.setProviderMetrics(FAST_KEY, metricsMap(now, 500, "0.9")); + am.setProviderMetrics(FAST_KEY, metricsMap(now, 500, "0.5")); am.addConsumerSuccess(FAST_KEY); + am.getLoad(FAST_KEY, WEIGHT, TIMEOUT); } double loadAfter = am.getLoad(FAST_KEY, WEIGHT, TIMEOUT); assertTrue(loadAfter > loadBefore, - "Load should increase after degradation: before=" + loadBefore + " after=" + loadAfter); + "Load should increase after RT degradation (CPU held constant): " + + "before=" + loadBefore + " after=" + loadAfter); } /** @@ -195,7 +212,6 @@ void outOfOrderServiceTime_isDiscarded() { double loadAfterStale = am.getLoad(FAST_KEY, WEIGHT, TIMEOUT); // The stale update should have been discarded — load should not spike - // (if the stale 999ms RT were accepted, loadAfterStale would be much higher) assertTrue(loadAfterStale <= loadAfterFresh, "Stale metrics should be discarded: loadAfterFresh=" + loadAfterFresh + " loadAfterStale=" + loadAfterStale); @@ -214,7 +230,7 @@ void forcedPick_notBroken() { am.setProviderMetrics(FAST_KEY, metricsMap(now, 50, "0.5")); am.addConsumerSuccess(FAST_KEY); - // Set pickTime far in the past → triggers forced pick (return 0) + // Set pickTime far in the past -> triggers forced pick (return 0) am.setPickTime(FAST_KEY, now - TIMEOUT * 3); double load = am.getLoad(FAST_KEY, WEIGHT, TIMEOUT); From 0b805ac95d43672dbcbab2fee611e64216ca85bc Mon Sep 17 00:00:00 2001 From: hyc <2768167716@qq.com> Date: Thu, 19 Mar 2026 13:32:50 +0800 Subject: [PATCH 4/4] style: apply spotless formatting to AdaptiveMetricsTest --- .../apache/dubbo/rpc/AdaptiveMetricsTest.java | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/AdaptiveMetricsTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/AdaptiveMetricsTest.java index 4c5eb3b68e9..b024a538fc0 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/AdaptiveMetricsTest.java +++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/AdaptiveMetricsTest.java @@ -84,7 +84,8 @@ void freshMetrics_usesRealLatency_notPenalty() { // With penalty bug: ratio ≈ 1.46 (136.7 vs 200.0 EWMA). Fails. // With fix: ratio ≈ 20x (10 vs 200 EWMA). Passes easily. assertTrue(fastLoad > 0, "fastLoad should be > 0"); - assertTrue(slowLoad / fastLoad > 2.0, + assertTrue( + slowLoad / fastLoad > 2.0, "Slow/fast load ratio should be > 2x to prove real RT is used, " + "got ratio=" + (slowLoad / fastLoad) + " (fast=" + fastLoad + " slow=" + slowLoad + ")"); @@ -151,10 +152,8 @@ void multipleServers_fastServerPreferred() { } // Strict ordering: fast < medium < slow - assertTrue(loadFast < loadMedium, - "fast(" + loadFast + ") should be < medium(" + loadMedium + ")"); - assertTrue(loadMedium < loadSlow, - "medium(" + loadMedium + ") should be < slow(" + loadSlow + ")"); + assertTrue(loadFast < loadMedium, "fast(" + loadFast + ") should be < medium(" + loadMedium + ")"); + assertTrue(loadMedium < loadSlow, "medium(" + loadMedium + ") should be < slow(" + loadSlow + ")"); } /** @@ -185,9 +184,10 @@ void degradedServer_loadIncreases() { } double loadAfter = am.getLoad(FAST_KEY, WEIGHT, TIMEOUT); - assertTrue(loadAfter > loadBefore, - "Load should increase after RT degradation (CPU held constant): " - + "before=" + loadBefore + " after=" + loadAfter); + assertTrue( + loadAfter > loadBefore, + "Load should increase after RT degradation (CPU held constant): " + "before=" + loadBefore + " after=" + + loadAfter); } /** @@ -212,9 +212,10 @@ void outOfOrderServiceTime_isDiscarded() { double loadAfterStale = am.getLoad(FAST_KEY, WEIGHT, TIMEOUT); // The stale update should have been discarded — load should not spike - assertTrue(loadAfterStale <= loadAfterFresh, - "Stale metrics should be discarded: loadAfterFresh=" + loadAfterFresh - + " loadAfterStale=" + loadAfterStale); + assertTrue( + loadAfterStale <= loadAfterFresh, + "Stale metrics should be discarded: loadAfterFresh=" + loadAfterFresh + " loadAfterStale=" + + loadAfterStale); } /**