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]

Reply via email to