build-a-load-balancer / lesson-06.md
Lesson 06 · Selection algorithms

Round-robin selection

The workhorse balancing algorithm is round-robin - hand each request to the next backend in turn, wrapping back to the first after the last. Today you build it and pin the wrap.

The goal

Select backends in turn from the healthy set, wrapping from the last back to the first.

Start here - the target
TO DO
Scenario: Round-robin cycles through the backends and wraps
Givena round-robin selector over a pool of A, B, C all up
WhenSelect is called four times in a row
Thenit returns A, then B, then C, then A again (wrapping from the last back to the first)
Andeach Select returns a backend and no error while at least one backend is up
Background

Round-robin is the default in almost every load balancer because it is simple, fair, and stateless per request. Keep a single counter n that only ever goes up; each Select picks Available()[n % len] and increments n. The modulo is what makes it wrap - after the last backend, n % len rolls back to 0 and you are at the first again. The wrap is the whole point, so the spec pins it directly: the fourth pick returns to A.

Notice the selector chooses from Available, not the raw pool, so a down backend is skipped automatically - you will see that next lesson. One edge to leave for later: if every backend is down, Available is empty and the modulo divides by zero. Selecting from an empty pool is the explicit all-down error case near the end of the project; for now the spec keeps at least one backend up.

Make it work
type Selector interface{ Select() (*Backend, error) }
type RoundRobin struct { pool *Pool; n int }
func (r *RoundRobin) Select() (*Backend, error) {
avail := r.pool.Available()
// if avail is empty, that is the all-down case (a later lesson)
b := avail[r.n % len(avail)]
r.n++ // advance for next time
return b, nil
}
CheckpointDONE
Round-robin hands out backends in turn and wraps around cleanly. Commit and stop here.