This is an automated email from the ASF dual-hosted git repository.

dmsolr pushed a commit to branch fix/p2c-singleton-inflight
in repository https://gitbox.apache.org/repos/asf/shenyu.git

commit a4dbcd6b9f7bef41c408b3d735f6389618621933
Author: Luke Haochao Zhuang <[email protected]>
AuthorDate: Thu Oct 22 15:09:21 2026 +0800

    fix: pair P2C inflight accounting for singleton upstream selection
    
    AbstractLoadBalancer.select short-circuits a singleton candidate list and
    returns the only upstream without calling P2cLoadBalancer.doSelect, so the
    inflight increment is skipped. Divide's P2C completion callback still
    decrements the selected upstream, so repeated singleton requests drive the
    inflight counter below its baseline and eventually negative, which P2C then
    uses in its load calculation.
    
    Override select in P2cLoadBalancer to perform the same increment (and
    lastPicked update) that doSelect does when the candidate list is a 
singleton,
    keeping start/completion accounting paired.
    
    Add regression tests covering the single increment and the paired
    select/decrement loop from the issue reproduction.
---
 .../shenyu/loadbalancer/spi/P2cLoadBalancer.java   | 17 ++++++++++-
 .../loadbalancer/spi/P2cLoadBalancerTest.java      | 34 ++++++++++++++++++++++
 2 files changed, 50 insertions(+), 1 deletion(-)

diff --git 
a/shenyu-loadbalancer/src/main/java/org/apache/shenyu/loadbalancer/spi/P2cLoadBalancer.java
 
b/shenyu-loadbalancer/src/main/java/org/apache/shenyu/loadbalancer/spi/P2cLoadBalancer.java
index dd0edb7ceb..ddd0977599 100644
--- 
a/shenyu-loadbalancer/src/main/java/org/apache/shenyu/loadbalancer/spi/P2cLoadBalancer.java
+++ 
b/shenyu-loadbalancer/src/main/java/org/apache/shenyu/loadbalancer/spi/P2cLoadBalancer.java
@@ -22,6 +22,7 @@ import org.apache.shenyu.loadbalancer.entity.Upstream;
 import org.apache.shenyu.spi.Join;
 
 import java.util.List;
+import java.util.Objects;
 import java.util.Random;
 
 /**
@@ -46,7 +47,21 @@ public class P2cLoadBalancer extends AbstractLoadBalancer {
     private static final int PICK_TIMES = 3;
 
     private final Random random = new Random();
-    
+
+    @Override
+    public Upstream select(final List<Upstream> upstreamList, final 
LoadBalanceData data) {
+        Upstream picked = super.select(upstreamList, data);
+        // AbstractLoadBalancer short-circuits a singleton candidate list 
before doSelect runs, so the
+        // inflight increment in doSelect is skipped while the P2C completion 
accounting (DividePlugin's
+        // response trigger) still decrements it. Pair the accounting here so 
repeated singleton
+        // selections keep inflight at its baseline instead of driving it 
negative. See #7974.
+        if (Objects.nonNull(picked) && upstreamList.size() == 1) {
+            picked.setLastPicked(System.currentTimeMillis());
+            picked.getInflight().incrementAndGet();
+        }
+        return picked;
+    }
+
     @Override
     protected Upstream doSelect(final List<Upstream> upstreamList, final 
LoadBalanceData data) {
         long start = System.currentTimeMillis();
diff --git 
a/shenyu-loadbalancer/src/test/java/org/apache/shenyu/loadbalancer/spi/P2cLoadBalancerTest.java
 
b/shenyu-loadbalancer/src/test/java/org/apache/shenyu/loadbalancer/spi/P2cLoadBalancerTest.java
index 5b760949fc..a5ae73afcb 100644
--- 
a/shenyu-loadbalancer/src/test/java/org/apache/shenyu/loadbalancer/spi/P2cLoadBalancerTest.java
+++ 
b/shenyu-loadbalancer/src/test/java/org/apache/shenyu/loadbalancer/spi/P2cLoadBalancerTest.java
@@ -63,4 +63,38 @@ public class P2cLoadBalancerTest {
         Upstream upstream1 = p2cLoadBalancer.doSelect(upstreamList, new 
LoadBalanceData());
         Assertions.assertTrue(upstream.getUrl().equals("baidu.com") && 
upstream1.getUrl().equals("pro.jd.com"));
     }
+
+    @Test
+    public void testSingletonSelectionIncrementsInflightOnce() {
+        Upstream upstream = Upstream.builder()
+                .url("a:80")
+                .protocol("http://";)
+                .weight(100)
+                .status(true)
+                .build();
+        final P2cLoadBalancer p2cLoadBalancer = new P2cLoadBalancer();
+        long before = upstream.getInflight().get();
+        Upstream picked = p2cLoadBalancer.select(List.of(upstream), new 
LoadBalanceData());
+        Assertions.assertSame(upstream, picked);
+        Assertions.assertEquals(before + 1, upstream.getInflight().get());
+    }
+
+    @Test
+    public void testSingletonSelectionStaysPairedWithCompletionDecrement() {
+        Upstream upstream = Upstream.builder()
+                .url("a:80")
+                .protocol("http://";)
+                .weight(100)
+                .status(true)
+                .build();
+        final P2cLoadBalancer p2cLoadBalancer = new P2cLoadBalancer();
+        long baseline = upstream.getInflight().get();
+        for (int i = 0; i < 2; i++) {
+            p2cLoadBalancer.select(List.of(upstream), new LoadBalanceData());
+            // mirrors DividePlugin's P2C completion callback (responseTrigger)
+            upstream.getInflight().decrementAndGet();
+        }
+        Assertions.assertEquals(baseline, upstream.getInflight().get());
+        Assertions.assertTrue(upstream.getInflight().get() >= 0);
+    }
 }

Reply via email to