He-Pin opened a new issue, #3688:
URL: https://github.com/apache/pekko/issues/3688
## Summary
`ThrottlerManager` creates a `ThrottledAssociation` child per association
and sends it a single `Handle` message. It declares no `supervisorStrategy` and
never watches its children, so a child failure leaves the throttling machinery
silently broken instead of failing loudly.
## Evidence
`remote/src/main/scala/org/apache/pekko/remote/transport/ThrottlerTransportAdapter.scala:282-290`:
```scala
private[transport] class ThrottlerManager(wrappedTransport: Transport)
extends ActorTransportAdapterManager
with ActorLogging {
import ThrottlerManager._
import context.dispatcher
private var throttlingModes = Map[Address, (ThrottleMode, Direction)]()
private var handleTable = List[(Address, ThrottlerHandle)]()
```
There is no `supervisorStrategy` override in the class and no
`context.watch`. `Handle` is sent exactly once, at `:296`:
```scala
case InboundAssociation(handle) =>
val wrappedHandle = wrapHandle(handle, associationListener, inbound =
true)
wrappedHandle.throttlerActor ! Handle(wrappedHandle)
```
The child FSM starts in a state that only advances on `Handle` (`:475`,
`:481-482`):
```scala
if (inbound) startWith(WaitExposedHandle, Uninitialized)
...
when(WaitExposedHandle) {
case Event(Handle(handle), Uninitialized) => ...
```
`handleTable` is only ever appended to (`:311`, `:341`) and iterated
(`:318`, `:326`, `:333`); nothing removes an entry when a child terminates.
The sibling adapter in the same package does declare a strategy —
`remote/src/main/scala/org/apache/pekko/remote/transport/PekkoProtocolTransport.scala:160-162`:
```scala
override val supervisorStrategy = OneForOneStrategy() {
case NonFatal(_) => Stop
}
```
## Impact
Under the default strategy a failure in a `ThrottledAssociation` restarts
it. The restarted instance re-enters `startWith(WaitExposedHandle,
Uninitialized)`, but `Handle` is never re-sent, so it stays in that state
permanently and silently drops `InboundPayload`. Separately, terminated
children remain in `handleTable`, so later `SetThrottle` calls iterate dead
refs — and `askModeWithDeathCompletion` (`:383`) treats an already-terminated
target as success, so `SetThrottle` replies `SetThrottleAck` while having no
effect.
These are the `trttl` and `gremlin` transport adapters, used to inject
throttling, blackholes and failures in multi-node tests. The failure mode is
therefore that a test's fault injection silently stops working rather than the
test failing — which can turn a real assertion into a pass, or produce a
confusing downstream timeout far from the cause.
## Suggested direction
- Declare `OneForOneStrategy { case NonFatal(_) => Stop }`, matching
`PekkoProtocolTransport`.
- `context.watch` each `ThrottledAssociation` and drop its `handleTable`
entry on `Terminated`.
- In `WaitExposedHandle`, handle an `InboundPayload` that arrives before
`Handle` by disassociating and stopping, rather than dropping it.
## Test
`remote/src/test/scala/org/apache/pekko/remote/classic/transport/ThrottlerTransportAdapterSpec.scala`
and `ThrottlerHandleSpec.scala` — a case that fails a throttled association
and then asserts a subsequent `SetThrottle` is either effective or reports
failure, rather than silently acknowledging.
--
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]