beemines opened a new issue, #16455:
URL: https://github.com/apache/dubbo/issues/16455
### Pre-check
- [x] All content is in English.
- [x] I searched existing issues and pull requests for ActiveLimitFilter,
ExecuteLimitFilter, active count, and limit-exception cleanup.
### Apache Dubbo Component
Java SDK (apache/dubbo), dubbo-rpc-api filters and the dubbo-cluster filter
chain.
### Dubbo Version
3.3.7-SNAPSHOT, branch `3.3` at `dab47b7843`; Windows, OpenJDK 17.0.17,
Maven 3.9.4.
### Steps to reproduce this issue
Both concurrency filters skip `RpcStatus.endCount()` whenever their error
listener receives a `RpcException` with `LIMIT_EXCEEDED_EXCEPTION`. This
correctly skips cleanup when the filter itself rejects admission, but also
skips it when admission succeeded and a downstream invoker throws that
exception (or its response future completes exceptionally with it).
A minimal synchronous reproduction in a test in `dubbo-cluster` (JUnit 5 /
Mockito; ordinary imports omitted):
```java
URL url =
URL.valueOf("test://localhost:12345/limit-repro?actives=1&timeout=1");
Invoker<Object> target = mock(Invoker.class);
when(target.getUrl()).thenReturn(url);
when(target.getInterface()).thenReturn(Object.class);
when(target.invoke(any())).thenThrow(
new RpcException(RpcException.LIMIT_EXCEEDED_EXCEPTION, "downstream
rejected"));
Filter filter = new ActiveLimitFilter();
Invoker<Object> chain = new FilterChainBuilder.CallbackRegistrationInvoker<>(
new FilterChainBuilder.CopyOfFilterChainNode<>(target, target,
filter),
Collections.singletonList(filter));
RpcInvocation invocation = new RpcInvocation();
invocation.setMethodName("invoke");
assertThrows(RpcException.class, () -> chain.invoke(invocation));
assertEquals(0, RpcStatus.getStatus(url, "invoke").getActive()); // actual: 1
```
Replace `actives=1` with `executes=1` and use `ExecuteLimitFilter` to
reproduce the provider-side case.
For the asynchronous case, return `new AsyncRpcResult(pending, invocation)`
from the target and complete `pending` exceptionally with the same limit
exception after calling the chain. The active count also remains 1.
### What you expected to happen
A call that acquired a concurrency slot must release it on downstream
failure and record the failure. A local admission rejection must not release
another in-flight call's slot.
With a limit of 1, the leaked count causes later active-limit calls to time
out or execute-limit calls to be rejected even though the failed call is no
longer running.
### Verification
I added eight parameterized regression cases through the production
`CopyOfFilterChainNode` / `CallbackRegistrationInvoker` lifecycle. Before the
fix, all four downstream-failure cases fail with `expected: <0> but was: <1>`;
the four local-rejection / invocation-reuse controls pass. There are 0 test
errors.
The fix will track whether this filter acquired a slot for the current
invocation attempt, resetting that state on each entry so sequential Invocation
reuse does not carry stale admission state.
This concerns exceptions delivered through `onError`, not ordinary business
exceptions carried inside an otherwise successfully completed `AppResponse`. No
registry, network service, or load generator is needed for the reproduction.
AI assistance was used for source inspection, implementation, and regression
tests. The failures above were reproduced locally, and I will submit the fix
with the complete tests.
--
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]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]