fix: skip experiment weight destinations with empty ServiceName - #4891
Conversation
During experiment teardown the experiment's template statuses can momentarily carry an empty ServiceName while the template weight still applies. calculateWeightDestinationsFromExperiment copied the empty ServiceName into a WeightDestination, which traffic routers render as a route destination with an empty host (e.g. an Istio VirtualService route to 'host: ""' carrying the arm's weight). Envoy cannot route such destinations, so that share of traffic is black-holed until the VirtualService is manually restored. Skip template statuses with an empty ServiceName and log a warning instead, and add a regression test covering the teardown race. Signed-off-by: Tanmay Agrawal <agrawaltanmay98@gmail.com>
5e1f736 to
e04a5ab
Compare
|
|
Adding the exact upstream code path that opens the window, for reviewers — both halves are in-tree: Producer side (experiments controller) —
ec.templateServices[templateName] = nil
templateStatus.ServiceName = ""
templateStatus.PodTemplateHash = ""Because the Consumer side (rollout controller) — Since Degradation with the guard is the safe direction: for the reconciles inside the window the arm simply receives no traffic share (identical to the post-teardown steady state), and the next reconcile with a repopulated status converges normally — no ordering between the two controllers is introduced. |



Fixes #4890
During experiment teardown the experiment's template statuses can momentarily carry an empty
serviceNamewhile the template weight from the step spec still applies.calculateWeightDestinationsFromExperimentcopied the empty ServiceName into aWeightDestination, which the Istio reconciler renders as a VirtualService route destination with an empty host carrying the arm's weight. Envoy cannot route such destinations, so that traffic share is black-holed (503s), and the entries are never cleaned up — recovery requires manually restoring the VirtualService. I hit this in a production environment I operate, during normal experiment teardown.This change skips template statuses with an empty ServiceName (logging a warning) so a route destination without a host can never be emitted, and adds a regression test simulating the teardown race.
The controller's own event stream shows the poisoned state this prevents:
Alternatives considered
IsTerminatinginstead of checking the ServiceName: terminating is only one of several ways the status can carry a blank ServiceName (Service-creation failure at experiment start, templateFailed/Error, external status writes — theexperimentsCRD has no status subresource). The empty-name check enforces the invariant itself — never emit a destination without a host — rather than one of its causes.PhaseleavesRunning; acting on it would abort healthy releases. Lifecycle decisions stay with experiment phase + analysis results.Checklist: