You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I noticed the current load-balancing strategy in MultiPool is implemented as an enum with a switch in next(). It works well, but it has a few limitations:
Not extensible — users can't plug in a custom strategy. Take a "least waiting" strategy as an example — picking the pool with the fewest queued tasks rather than the fewest running workers better reflects real backlog pressure when pools are saturated. This could of course be added as a new built-in enum value, but the deeper issue is extensibility: developers should be able to control the selection logic themselves without waiting for upstream changes
Duplicated logic — next() is copy-pasted across MultiPool, MultiPoolWithFunc, and MultiPoolWithFuncGeneric
State coupling — mp.index (the RoundRobin counter) lives on the pool struct rather than the strategy itself
Proposed Design
Introduce a LoadBalancer interface:
typeLoadBalancerinterface {
Pick(pools []*Pool) intFallback(pools []*Pool) int// -1 means no fallback supported
}
Built-in strategies (RoundRobin, LeastTasks) become structs implementing this interface. The RoundRobin counter moves into the strategy struct itself.
No breaking change to existing API — NewMultiPool signature stays the same. A new constructor is added for custom strategies:
Internally, NewMultiPool converts the enum to the corresponding built-in LoadBalancer implementation, so MultiPool only holds a single lb LoadBalancer field. The lbs enum field and mp.index field are removed from the struct entirely. The existing fallback behavior (RoundRobin → LeastTasks on overload) is preserved via Fallback().
This keeps full backward compatibility while opening up the strategy to user customization. Would you be open to this direction? Happy to adjust the design based on your feedback before opening a PR.
If this proposal is accepted, the same changes will be applied consistently across all three MultiPool variants — MultiPool, MultiPoolWithFunc, and MultiPoolWithFuncGeneric — not just the simplest one.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Hi @panjf2000 ,
I noticed the current load-balancing strategy in
MultiPoolis implemented as an enum with aswitchinnext(). It works well, but it has a few limitations:next()is copy-pasted acrossMultiPool,MultiPoolWithFunc, andMultiPoolWithFuncGenericmp.index(the RoundRobin counter) lives on the pool struct rather than the strategy itselfProposed Design
Introduce a
LoadBalancerinterface:Built-in strategies (
RoundRobin,LeastTasks) become structs implementing this interface. The RoundRobin counter moves into the strategy struct itself.No breaking change to existing API —
NewMultiPoolsignature stays the same. A new constructor is added for custom strategies:Internally,
NewMultiPoolconverts the enum to the corresponding built-inLoadBalancerimplementation, soMultiPoolonly holds a singlelb LoadBalancerfield. Thelbsenum field andmp.indexfield are removed from the struct entirely. The existing fallback behavior (RoundRobin→LeastTaskson overload) is preserved viaFallback().The result is that
Submitbecomes simply:This keeps full backward compatibility while opening up the strategy to user customization. Would you be open to this direction? Happy to adjust the design based on your feedback before opening a PR.
If this proposal is accepted, the same changes will be applied consistently across all three MultiPool variants —
MultiPool,MultiPoolWithFunc, andMultiPoolWithFuncGeneric— not just the simplest one.All reactions