Skip to content

feat(bigtable): add AFE picker (Simple / LeastInFlight / LeastLatency) - #20204

Merged
sushanb merged 4 commits into
googleapis:mainfrom
sushanb:feat/bigtable-afe-picker
Jul 23, 2026
Merged

sushanb merged 4 commits into
googleapis:mainfrom
sushanb:feat/bigtable-afe-picker

Conversation

@sushanb

@sushanb sushanb commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

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 a sessionList producer, no lock ownership, no pool integration. The producer + wiring land in follow-up PRs.

  • afe_picker.goAfePicker interface + SimpleAfePicker (uniform random), LeastInFlightAfePicker (K-choice min NumOutstanding), LeastLatencyAfePicker (K-choice min e2e PeakEwma). Every PickAfe returns a PickDecision{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.goafeID int64 + afeSnapshot struct (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.

kChoiceMinCost implements 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.
  • CI green.

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.
@sushanb
sushanb requested review from a team as code owners July 23, 2026 18:59
@product-auto-label product-auto-label Bot added the api: bigtable Issues related to the Bigtable API. label Jul 23, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.