feat(bigtable): add AFE picker (Simple / LeastInFlight / LeastLatency) - #20204
Merged
Merged
Conversation
Pure-function picker interface over []afeSnapshot, plus three implementations: - SimpleAfePicker: uniform-random pick. - LeastInFlightAfePicker: K-choice min-cost by NumOutstanding. - LeastLatencyAfePicker: K-choice min-cost by per-AFE e2e PeakEwma. Every PickAfe call returns a PickDecision (sampled candidates + winner + reason tag) so operators can trace picker reasoning through the debug surface without re-running the pick. kChoiceMinCost implements partial-Fisher-Yates in place over the caller's slice. Callers must pass a throwaway slice; production call sites will produce one via the follow-up sessionList snapshot method, which allocates a fresh copy per call. afe_types.go carries the two consumer-facing types (afeID, afeSnapshot). The producer of []afeSnapshot (sessionList) lands in a follow-up PR; splitting the picker out here lets it ship and be reviewed as a pure function over snapshot slices.
Contributor
There was a problem hiding this comment.
Code Review
This pull request introduces the AfePicker interface and its implementations (SimpleAfePicker, LeastInFlightAfePicker, and LeastLatencyAfePicker) to support Application Front End (AFE) selection strategies, along with associated unit tests and type definitions. Feedback on the changes highlights a contradiction in kChoiceMinCost where a non-positive RandomSubsetSize overrides the documented 'consider all candidates' behavior to a default of 2. Additionally, it is recommended to document the in-place mutation of the ready slice in the AfePicker interface, and to address potential heap allocation overhead on the hot path caused by allocating the Candidates slice on every pick.
Summary
Adds the AFE (Application Front End) picker interface and three implementations, plus the two consumer-facing types they operate on. Pure function over
[]afeSnapshot— no ties to asessionListproducer, no lock ownership, no pool integration. The producer + wiring land in follow-up PRs.afe_picker.go—AfePickerinterface +SimpleAfePicker(uniform random),LeastInFlightAfePicker(K-choice min NumOutstanding),LeastLatencyAfePicker(K-choice min e2e PeakEwma). EveryPickAfereturns aPickDecision{Candidates, Winner, Reason}so debug surfaces can trace picker reasoning without re-running the pick.afe_picker_test.go— 10 unit tests: empty-input handling per picker, uniform-random distribution smoke, min-cost correctness, K-choice sub-sampling, in-place mutation of caller's slice, and picker-name identity.afe_types.go—afeID int64+afeSnapshotstruct (5 fields, 3 used by the picker today). Split into its own file so the picker ships as a pure function over snapshot slices; the producer (sessionList) lands in a follow-up PR.kChoiceMinCostimplements partial-Fisher-Yates over the caller's slice in place — a defensive copy per pick cost ~4µs at steady-state QPS, so callers own the slice's lifetime.Test plan
go test ./bigtable/internal/transport/ -run "AfePicker|KChoice|Decision" -count=1 -short→ 10/10 pass locally.go build ./bigtable/internal/transport/clean.