nikhiln64 commented on code in PR #6900:
URL: https://github.com/apache/shenyu/pull/6900#discussion_r3744478795


##########
shenyu-loadbalancer/src/main/java/org/apache/shenyu/loadbalancer/spi/LeastActiveLoadBalance.java:
##########
@@ -45,6 +45,8 @@ protected Upstream doSelect(final List<Upstream> 
upstreamList, final LoadBalance
                 .filter(key -> !countMap.containsKey(key))
                 .forEach(domain -> countMap.put(domain, Long.MIN_VALUE));
 
+        countMap.keySet().retainAll(domainMap.keySet());

Review Comment:
   This looks like the right direction, and you are right that the size gate 
does not work here. On a single shared map `countMap.size()` is the union of 
every selector's domains, so it is not comparable to one selector's 
`upstreamList.size()`, and it cannot see a same length member swap, so my 
suggestion would have left the cross selector deletion in place. The time based 
recycle avoids that cleanly. Refreshing `lastUpdate` for every live domain on 
each request means an entry can only be evicted after it has genuinely gone 
quiet for `recyclePeriod`, so one selector's cleanup can no longer drop another 
selector's live entries and the leak is bounded to about `recyclePeriod`. The 
updateLock and the once per period throttle keep the cleanup off the hot path, 
and the selected domain is always one you refreshed this call, so the 
countMap.get(domain) with the `Objects.nonNull` guard cannot be caught by a 
concurrent removeIf. The concurrency reads as sound to me.
   
   The one thing I would think about is the `Long.MIN_VALUE` seed on a revived 
entry. If an upstream goes quiet longer than `recyclePeriod` it gets recycled, 
and when traffic returns its `ActiveCount` is recreated at `Long.MIN_VALUE`. 
Because selection picks the minimum count, that upstream is then chosen on 
almost every request until its counter climbs back to its peers, and since the 
count is cumulative and never decremented that catch up can take as many 
requests as the peers have served in total, so a node that just came back can 
absorb nearly all the traffic for a long stretch rather than a brief burst. A 
brand new upstream already has this property today, but recycling means an idle 
then active upstream hits it too. It might be worth seeding a new or revived 
entry to the current minimum of its live peers rather than `Long.MIN_VALUE`, so 
it rejoins at parity. Not a blocker, more a behavior to decide on consciously.
   
   On the tests, the stale eviction and multi selector isolation cases you 
mentioned are the ones I would want to see. One more worth adding is the revive 
case: let an entry age past recyclePeriod so it is recycled, then send traffic 
again and assert the distribution does not collapse onto the revived node. 
Thanks for turning this around so quickly.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to