Repository navigation
Conversation
xiangfu0
left a comment
There was a problem hiding this comment.
Thanks for adding these observability metrics. The fail-open-eligible transition logic looks correct and is nicely covered by testFailOpenEligibleSegmentsMeter. I verified the change compiles (JDK 25) and the global/table flags on each meter match the emit method used. I only have a few minor/nit suggestions inline — no blocking issues.
| create("FAIL_OPEN_ELIGIBLE_REQUESTS", "failOpenEligibleRequests", true); | ||
|
|
||
| // Tracks segments that become eligible for fail-open routing. | ||
| public static final BrokerMeter FAIL_OPEN_ELIGIBLE_SEGMENTS = |
There was a problem hiding this comment.
Minor: most recently added meters here carry a /// Javadoc explaining exactly when they are emitted. These two have non-obvious semantics worth documenting the same way: FAIL_OPEN_ELIGIBLE_SEGMENTS is a transition counter (it fires once when a segment enters the unavailable-with-candidates state, not per refresh), while FAIL_OPEN_ELIGIBLE_REQUESTS fires once per query that touches such a segment. Spelling that out will save operators from misreading the rates.
| public static final BrokerMeter ROUTING_TABLE_UPDATE_FAILURE = | ||
| create("ROUTING_TABLE_UPDATE_FAILURE", "failures", false); | ||
|
|
||
| // Panic routing metrics |
There was a problem hiding this comment.
Minor/nit: the phrase "Panic routing" doesn't appear anywhere else in the codebase — the rest of the code calls this "fail-open". Using the same term consistently here would make it easier to grep and reason about.
|
|
||
| public SegmentStates(Map<String, List<SegmentInstanceCandidate>> instanceCandidatesMap, Set<String> servingInstances, | ||
| Set<String> unavailableSegments) { | ||
| this(instanceCandidatesMap, servingInstances, unavailableSegments, unavailableSegments); |
There was a problem hiding this comment.
Minor: this public constructor silently defaults segmentsWithoutCandidates to the full unavailableSegments set, which means any SegmentStates built this way treats every unavailable segment as having no candidates (so it never counts as fail-open eligible). That's a safe/conservative default, but it's surprising — a short comment explaining the intent would help future callers, since production now always uses the 4-arg form.
xiangfu0
left a comment
There was a problem hiding this comment.
Thanks for adding these observability metrics. The fail-open-eligible transition logic looks correct and is nicely covered by testFailOpenEligibleSegmentsMeter. I verified the change compiles (JDK 25) and the global/table flags on each meter match the emit method used. I only have a few minor/nit suggestions inline — no blocking issues.
xiangfu0
left a comment
There was a problem hiding this comment.
Approving — only minor/nit comments (doc wording and a clarifying comment on the new constructor default). I verified the change compiles on JDK 25 and the full InstanceSelectorTest suite passes, including the new testFailOpenEligibleSegmentsMeter. The transition-counting logic for FAIL_OPEN_ELIGIBLE_SEGMENTS and the per-request FAIL_OPEN_ELIGIBLE_REQUESTS metric both look correct, and the global/table meter flags match their emit methods.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #19891 +/- ##
============================================
+ Coverage 69.26% 69.43% +0.16%
Complexity 1491 1491
============================================
Files 3543 3545 +2
Lines 233237 233910 +673
Branches 37277 37441 +164
============================================
+ Hits 161560 162410 +850
+ Misses 59184 58956 -228
- Partials 12493 12544 +51
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
@xiangfu0 I've updated the comments could I get a re-approval? |
xiangfu0
left a comment
There was a problem hiding this comment.
Re-reviewed after the latest push (d8c22d7). This revision addresses all three of my earlier minor comments:
- Both meters now carry
///Javadoc spelling out the emission semantics (FAIL_OPEN_ELIGIBLE_REQUESTS is per-query/global, FAIL_OPEN_ELIGIBLE_SEGMENTS is a transition counter scoped to a table). - The inconsistent "Panic routing" comment is gone.
- The 3-arg SegmentStates constructor now documents its conservative default (treating all unavailable segments as candidate-less).
The routing logic and tests are unchanged from the version I verified earlier. I re-confirmed this revision compiles on JDK 25 and the full InstanceSelectorTest suite passes. No remaining issues — approving.
|
Thanks for the review! CI is green could someone merge this? I don't have contributor access |
Jackie-Jiang
left a comment
There was a problem hiding this comment.
Reviewed d8c22d7 across the four changed files and the surrounding routing paths. I found four metric-accuracy issues, detailed inline, plus a simplification suggestion: store the fail-open-eligible segments directly so transition detection and request classification use the same explicit state.
All 13 reported CI checks passed for this revision. This was a source review; I did not run local tests.
| if (candidates.isEmpty()) { | ||
| segmentsWithoutCandidates.add(segment); |
There was a problem hiding this comment.
[P2] Distinguish failure-detector exclusions from administrative disables.
A nonempty candidate list does not establish the eligibility described in the PR's testing section. The supplied enabled-instance set excludes servers with HELIX_ENABLED=false, QUERIES_DISABLED=true, or shutdown in progress, as well as failure-detector exclusions. If all replicas are administratively disabled while IS/EV remain ONLINE, both new meters count them as fail-open eligible. Please carry the exclusion reason into this classification and require at least one candidate that is still administratively enabled but excluded by the failure detector. A routing-manager test contrasting these cases would cover the distinction.
| if (!segmentsWithoutCandidates.contains(segment) && !wasAlreadyUnavailableWithCandidates) { | ||
| _brokerMetrics.addMeteredTableValue(_tableNameWithType, BrokerMeter.FAIL_OPEN_ELIGIBLE_SEGMENTS, 1); |
There was a problem hiding this comment.
[P2] Emit segment transitions only from the main table selector.
BaseBrokerRoutingManager creates a separate selector for each configured table sampler, using the same table name and BrokerMetrics, and updates all of them on instance changes. A segment included in N samplers therefore increments this meter N+1 times for one transition. The main routing entry should own table-level emission, following the existing replica-health reporting approach. An overlapping-sampler test would catch this. The transition count can also be accumulated during the existing old-segment loop and emitted once, avoiding the extra pass and repeated meter calls.
| if (failOpenEligible) { | ||
| _brokerMetrics.addMeteredGlobalValue(BrokerMeter.FAIL_OPEN_ELIGIBLE_REQUESTS, 1); |
There was a problem hiding this comment.
[P2] Count eligible requests before subclass selection can throw.
The protected select(...) call at line 596 runs before this increment. For multiStageReplicaGroup, an old segment whose ONLINE IS/EV candidates are all excluded has no enabled candidate list, and MultiStageReplicaGroupSelector.assign() throws before reaching this meter. The segment meter increments, but affected requests are omitted. Classify and record eligibility from the captured snapshot before invoking subclass selection, preserving the existing exception behavior. The new test's selectorType provider does not include this selector.
| } | ||
| } | ||
| if (failOpenEligible) { | ||
| _brokerMetrics.addMeteredGlobalValue(BrokerMeter.FAIL_OPEN_ELIGIBLE_REQUESTS, 1); |
There was a problem hiding this comment.
[P2] Deduplicate request counts across scans of the same physical table.
This increments once per select invocation, while the meter documents once per query per physical table. Both MSE planners route separate leaf scans independently. A non-partitioned self-join with distinct leaves that both touch an eligible segment can therefore increment twice for the same request and physical table. Aggregate eligibility in request-scoped state and emit once per physical table; a self-join regression with distinct leaf filters would cover this.
| private final Map<String, List<SegmentInstanceCandidate>> _instanceCandidatesMap; | ||
| private final Set<String> _servingInstances; | ||
| private final Set<String> _unavailableSegments; | ||
| private final Set<String> _segmentsWithoutCandidates; |
There was a problem hiding this comment.
Simplification: store _failOpenEligibleSegments directly here. Both consumers currently reconstruct eligibility as unavailable minus candidate-less, which introduces compound conditions and negation. An explicit eligible set lets transition detection use !previousEligibleSegments.contains(segment) and request classification use eligibleSegments.contains(segment). The existing three-argument constructor can default it to Set.of(), preserving its conservative behavior. Keep the set in this same published snapshot so routing and eligibility stay consistent.
PR flow
Added fail‑open eligibility metrics: segment state refresh tracks newly ineligible segments; query selection counts requests touching any such segment.
AI-generated · Green: added · Yellow: modified · Red: removed · Gray: existing
Diff evidence
Background: Panic Routing
Panic routing is when the broker finds no available server for one or more segments in a query. Today the broker gives up on those segments: it marks them unavailable and returns partial results without them.
Fail-open routing handles this differently. If a segment still has candidate servers, but they're all disabled or marked unhealthy, the broker sends the request to one of them anyway. A disabled server often still has the segment loaded and can answer, so serving from it is better than dropping the segment from the result.
This PR doesn't turn on fail-open routing. It adds metrics that show how often panic routing happens and how much of it fail-open could fix.
Will be followed up with implementation PRs
Summary
Adds two broker meters that show how often fail-open routing would help, without changing any routing behavior.
The change splits unavailable segments into two kinds:
Changes
BrokerMeter: two new meters:FAIL_OPEN_ELIGIBLE_REQUESTS(global): counts queries that touch at least one segment that is unavailable but has candidates.FAIL_OPEN_ELIGIBLE_SEGMENTS(per table): counts each time a segment moves into that state. Unchanged refreshes and recoveries don't count.SegmentStates: now also stores which unavailable segments have no candidates.BaseInstanceSelector: records that state duringrefreshSegmentStates()and updates both meters.InstanceSelectorTest: covers the full lifecycle for every selector type: no candidates → disabled candidate → unchanged refresh → recovery → disabled again.Motivation
Today every unavailable segment is reported the same way (
NO_SERVING_HOST_FOR_SEGMENT), so you can't tell which ones fail-open could actually serve. These meters show how much traffic fail-open would affect, and which tables, before we turn it on.Diagram
flowchart TD subgraph Refresh["refreshSegmentStates()"] A[Old segment] --> B{Any enabled<br/>candidates?} B -- yes --> C[Routable] B -- no --> D{Any candidates<br/>at all?} D -- no --> E[Unavailable, without candidates] D -- yes --> F[Unavailable, with candidates] F --> G{Already in this<br/>state before?} G -- no --> H[["FAIL_OPEN_ELIGIBLE_SEGMENTS += 1"]] G -- yes --> I[No-op] end subgraph Select["select() — per query"] Q[Query segments] --> R{Touches a segment that's<br/>unavailable but has candidates?} R -- yes --> S[["FAIL_OPEN_ELIGIBLE_REQUESTS += 1"]] R -- no --> T[No-op] end Refresh -.->|SegmentStates| SelectTesting
In e2e testing we saw the metrics being emitted. To achieve this we injected packet loss on the query ports for servers. This caused the brokers to mark the servers unavailable, and over a short period of time all servers were marked as unavailable for segments
The different colors in the following image are different tables that are emitted as containing no server hosts for segments (existing metric)

Fail-open eligible segments during the same time period. These are tables who would be considered to be fail-open eligible. A segment is fail-open eligible when none of its replicas are routable, but at least one replica is

ONLINEorCONSUMINGin both the ideal state and the external view, and sits on a server that the broker's failure detector excluded from routing (as opposed to a server an operator disabled or that is shutting down).Fail-open eligible requests during the same time period:
