Motivation
Sending AdjustPoolSize to a pool router can grow or shrink the pool beyond configured limits (nr-of-instances, resizer lowerBound/upperBound). The handler directly creates or removes routees without any bounds checking.
Current Behavior
RoutedActorCell.scala:205-213:
case AdjustPoolSize(change: Int) =>
if (change > 0) {
val newRoutees = Vector.fill(change)(pool.newRoutee(cell.routeeProps, context))
cell.addRoutees(newRoutees)
} else if (change < 0) {
val currentRoutees = cell.router.routees
val abandon = currentRoutees.drop(currentRoutees.length + change)
cell.removeRoutees(abandon, stopChild = true)
}
- No check against
pool.nrOfInstances(system) (initial pool size)
- No check against resizer
lowerBound or upperBound
pool.newRoutee() is just a factory — creates routee with correct dispatcher but no bounds check
Resizer Bounds Bypassed
DefaultResizer.capacity() (Resizer.scala:178-187) does enforce bounds, but it's only called from ResizablePoolCell.resize() — NOT from the AdjustPoolSize handler. Even ResizablePoolActor delegates AdjustPoolSize to super.receive (the unbounded handler).
Test Confirms Unbounded Behavior
RoundRobinSpec.scala:117-120 shows AdjustPoolSize(+4) growing a pool from 3 to 7 (beyond configured size), with no bounds test asserting this is correct.
Expected Behavior
AdjustPoolSize should respect pool configuration bounds. If a resizer is configured, the handler should delegate to the resizer's capacity logic. If no resizer is configured, the handler should at minimum not shrink below 1 or grow beyond a reasonable limit.
Environment
- Pekko Actor (any version)
RoutedActorCell.scala:205-213
References
None
Motivation
Sending
AdjustPoolSizeto a pool router can grow or shrink the pool beyond configured limits (nr-of-instances, resizerlowerBound/upperBound). The handler directly creates or removes routees without any bounds checking.Current Behavior
RoutedActorCell.scala:205-213:pool.nrOfInstances(system)(initial pool size)lowerBoundorupperBoundpool.newRoutee()is just a factory — creates routee with correct dispatcher but no bounds checkResizer Bounds Bypassed
DefaultResizer.capacity()(Resizer.scala:178-187) does enforce bounds, but it's only called fromResizablePoolCell.resize()— NOT from theAdjustPoolSizehandler. EvenResizablePoolActordelegatesAdjustPoolSizetosuper.receive(the unbounded handler).Test Confirms Unbounded Behavior
RoundRobinSpec.scala:117-120showsAdjustPoolSize(+4)growing a pool from 3 to 7 (beyond configured size), with no bounds test asserting this is correct.Expected Behavior
AdjustPoolSizeshould respect pool configuration bounds. If a resizer is configured, the handler should delegate to the resizer's capacity logic. If no resizer is configured, the handler should at minimum not shrink below 1 or grow beyond a reasonable limit.Environment
RoutedActorCell.scala:205-213References
None