Now the pieces come together into a Balancer that owns a pool and a selector. Its core move is a lease - pick a backend and mark it busy, handing back a release you call when the work is done.
Build a Balancer whose Begin selects a backend, raises its active count, and returns a release that lowers it exactly once.
The Balancer is the object a caller actually holds. It owns the pool and a
chosen Selector, and its job is to turn “give me a backend for this request” into
a backend plus the bookkeeping that keeps the active counts honest. The clean way to
express that is a lease: Begin selects a backend, raises its active count so
least-connections and friends see it as busy, and returns a release closure. The
caller runs the request, then calls release to lower the count.
The one correctness rule is that release must decrement exactly once, no
matter how many times it is called. A double release would push the active count
below the real number of in-flight requests and corrupt every load-aware decision.
Guard it with a done flag captured in the closure. This lease shape is what lets
the next lessons run a request through a transport and, crucially, hold several
requests in flight at once to prove least-connections reacts to live load.
type Balancer struct { pool *Pool; sel Selector }func (bal *Balancer) Begin() (*Backend, func(), error) {b, err := bal.sel.Select()if err != nil { return nil, nil, err }b.Incr()done := falserelease := func() { if !done { done = true; b.Decr() } } // decrement oncereturn b, release, nil}