Previously, a state machine invalidation could have raced against an
in-flight routing attempt: if the invalidation occured after the routing
attempt had already completed its nil-check (choosing not to compile a
new state machine) but before the state machine was atomically loaded to
perform routing, the routing goroutine would begin to panic from
dereferencing nil.
The meat of this change is that we now return the state machine that we
compiled (while still holding the lock), and we only ever interact with
the state machine through atomic pointer loads.
Swap out the naive "try all the routes in order" router with a "compile
a trie down to bytecode" router. It's a ton faster, while providing all
the same semantics.
See the documentation at the top of web/fast_router.go for more.