gortiz commented on code in PR #19568:
URL: https://github.com/apache/pinot/pull/19568#discussion_r4063612448
##########
pinot-query-runtime/src/main/java/org/apache/pinot/query/service/dispatch/QueryDispatcher.java:
##########
@@ -633,8 +641,9 @@ private <E> void execute(long requestId,
Set<DispatchablePlanFragment> stagePlan
ByteString protoRequestMetadata =
QueryPlanSerDeUtils.toProtoProperties(requestMetadata);
// Submit the query plan to all servers in parallel
+ boolean protoSegmentList =
QueryOptionsUtils.isProtoSegmentList(queryOptions,
_protoSegmentList.isEnabled());
BlockingQueue<AsyncResponse<E>> dispatchCallbacks = dispatch(sendRequest,
serverInstancesOut, deadline,
- serverInstance -> createRequest(serverInstance, stageInfos,
protoRequestMetadata));
+ serverInstance -> createRequest(serverInstance, stageInfos,
protoRequestMetadata, protoSegmentList));
Review Comment:
The encode moved into the dispatch loop, not just off the compile executor,
and I think that costs tail latency on the default path.
`dispatch()` just below runs `R request =
requestBuilder.apply(serverInstance); ... sendRequest.send(...)` in a single
loop on the calling thread. Before this PR the segment lists were already
encoded at plan time, so this was a tight send loop. Now every iteration does
that server's encode first.
With the numbers from the description -- 60k segments at ~16ms for the
legacy JSON encode, which is the **default** path -- the last server in a wide
fan-out starts its leaf stage materially later than the first, purely because
encode time is interleaved with sends. Total CPU is unchanged, but the query
finishes when the slowest server finishes.
Easy to keep: build all the requests in one pass over `serverInstancesOut`,
then run the send loop. That preserves the "off the compile executor" win,
which is the actual motivation, without introducing dispatch skew. It also
makes the benchmark table true for the default configuration rather than only
for the opt-in one.
Same pattern in `submitWithStream` (around line 369), which also builds and
sends in one loop.
Worth measuring on a wide fan-out against a large table before and after --
the benchmark in the description is a single leaf worker, so it cannot show
this either way.
(Unrelated, and good: reading the flag once per request rather than per
server is the right granularity -- all servers of one query agree on the
encoding even if an operator flips the cluster config mid-dispatch.)
##########
pinot-query-runtime/src/main/java/org/apache/pinot/query/service/dispatch/ProtoSegmentListPredicate.java:
##########
@@ -0,0 +1,109 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.pinot.query.service.dispatch;
+
+import java.util.Map;
+import java.util.Set;
+import javax.annotation.concurrent.ThreadSafe;
+import org.apache.pinot.spi.config.provider.PinotClusterConfigChangeListener;
+import org.apache.pinot.spi.env.PinotConfiguration;
+import org.apache.pinot.spi.utils.CommonConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/// The cluster-level default for encoding leaf-stage segment lists as native
protobuf fields of the worker metadata,
+/// read by [QueryDispatcher] on every request that does not carry an explicit
+/// [CommonConstants.Broker.Request.QueryOptionKey#PROTO_SEGMENT_LIST]
override.
+///
+/// The value is seeded from the static broker configuration and can then be
changed through cluster config, on the
+/// same key, without restarting the brokers. That matters because the setting
is only safe once every server of the
+/// cluster understands the proto fields: an older server finds no segments
under them, concludes the worker is not a
+/// leaf-stage worker and fails its leaf stage. Operators therefore want to
turn it on at the exact moment a rolling
+/// upgrade completes, and to turn it back off immediately if it misbehaves,
neither of which should cost a broker
+/// restart. Cluster config wins over the static seed because
[org.apache.pinot.common.config
+/// .DefaultClusterConfigChangeHandler] replays the current cluster config to
a listener as soon as it is registered;
+/// clearing the key from cluster config falls back to
[CommonConstants.Broker#DEFAULT_MSE_PROTO_SEGMENT_LIST], not to
+/// the static seed.
+///
+/// Thread-safety: `_enabled` is `volatile`, so [#isEnabled()] stays lock-free
on the request path. [#onChange] is
+/// `synchronized` only so that the `previous -> new` pair in its log line
cannot interleave with another delivery. It
+/// does *not* order deliveries: the change handler invokes listeners outside
its own lock, so a delivery computed
+/// from an older snapshot can still be applied after a newer one and leave a
stale value until the next
+/// cluster-config change.
+@ThreadSafe
+public class ProtoSegmentListPredicate implements
PinotClusterConfigChangeListener {
+ private static final Logger LOGGER =
LoggerFactory.getLogger(ProtoSegmentListPredicate.class);
+ private static final String KEY =
CommonConstants.Broker.CONFIG_OF_MSE_PROTO_SEGMENT_LIST;
+ private static final String ENABLE_WARNING =
+ "Every server of the cluster must already run a version that understands
the proto segment list fields. "
+ + "Leaf stages routed to an older server will fail. Set it back to
false to revert.";
+
+ private volatile boolean _enabled;
+
+ public ProtoSegmentListPredicate(boolean enabled) {
+ _enabled = enabled;
+ }
+
+ /// Seeds the value from the static broker configuration. NOTE: the Helix
manager is not necessarily connected when
+ /// this is called, so a cluster-config override is applied later through
[#onChange].
+ public static ProtoSegmentListPredicate create(PinotConfiguration
brokerConf) {
Review Comment:
There is prior art for this in the same module, and I think it removes most
of this PR's operational surface.
`pinot-query-runtime/src/main/java/org/apache/pinot/query/runtime/SendStatsPredicate.java`
handles the same class of problem -- MSE sends something a pre-1.4 peer cannot
handle -- and given the shared `*Predicate` name I assume it is where this
class came from. But it took the opposite half of the design:
> `SendStatsPredicate.Safe` implements `InstanceConfigChangeListener`, reads
`CommonConstants.Helix.Instance.PINOT_VERSION_KEY` off each instance's
`InstanceConfig` (line 228), and keeps `_problematicVersionsById`. It enables
itself the moment the last old instance disappears. Modes are `ALWAYS` / `SAFE`
/ `NEVER`, and `SAFE` needs no human.
The property is already there and already published:
`HelixHelper.updatePinotVersion` is called from `BaseServerStarter` (line 529),
`BaseBrokerStarter` (line 889), `BaseControllerStarter` and
`BaseMinionStarter`. Nothing new has to be written or watched.
Shape of the change, reusing this config key rather than adding one:
```java
// CommonConstants.Broker
CONFIG_OF_MSE_PROTO_SEGMENT_LIST = "pinot.broker.mse.proto.segment.list" //
unchanged
DEFAULT_MSE_PROTO_SEGMENT_LIST = "SAFE" //
was: boolean false
enum Mode { NEVER, SAFE, ALWAYS }
NEVER - always legacy JSON. The kill switch.
SAFE - proto while no server reports a version older than the one that
added
fields 4/5. Default. Turns itself on when the rollout finishes.
ALWAYS - proto unconditionally. Escape hatch for clusters that do not
publish
versions, for forks carrying the change on a different version
string,
and for tests.
```
Registration gets simpler, not harder: make this a `ClusterChangeHandler`
and add it to `_instanceConfigChangeHandlers` in `BaseBrokerStarter` (line
778), next to `_routingManager` and `_queryQuotaManager`. That is an existing
extension point fed by `_clusterChangeMediator` on
`ChangeType.INSTANCE_CONFIG`, so the bespoke registration added in this PR, the
comment defending its position, and
`testRegistrationBeforeFirstDeliveryStillLetsClusterConfigWin` all go away.
What it removes: the four-step enablement runbook, the "verify the version
of every `Server_*` instance" precondition, the canary query, the two curl
commands, the rollback procedure, and the window in which a mis-timed flip
silently turns leaf workers into intermediate workers. The per-query
`protoSegmentList` option stays as the override. It is also more responsive
than a cluster config, because it reacts to the rollout itself rather than to
an operator noticing the rollout finished.
Two honest weaknesses, both survivable:
1. `SendStatsPredicate.isProblematicVersion()` treats any version `!=
PinotVersion.VERSION` as problematic, including a newer one, so a heterogeneous
cluster stays on the legacy encoding. Strictly correct here would be "server
version >= the release that added fields 4/5", which means comparing Pinot
version strings (`1.4.0-SNAPSHOT`, `UNKNOWN`, fork versions) -- parsing nobody
wants to own. I would copy the conservative equality: it is over-cautious, it
self-heals exactly when the fleet becomes homogeneous, and that is precisely
the moment this PR's runbook tells a human to flip the flag. `ALWAYS` covers
whoever cannot wait.
2. A missing or `UNKNOWN` version must read as old, i.e. legacy encoding.
Fail closed, same as the unparseable-boolean handling below, which is already
right.
Most of `ProtoSegmentListPredicateTest` survives in shape -- swap the
`DefaultClusterConfigChangeHandler` deliveries for instance-config ones.
Was this approach considered and rejected? If so it would be worth saying
why in the description.
##########
pinot-query-planner/src/main/java/org/apache/pinot/query/routing/QueryPlanSerDeUtils.java:
##########
@@ -54,15 +73,55 @@ private static StageMetadata
fromProtoStageMetadata(Worker.StageMetadata protoSt
return new StageMetadata(protoStageMetadata.getStageId(),
workerMetadataList, customProperties);
}
- private static WorkerMetadata fromProtoWorkerMetadata(Worker.WorkerMetadata
protoWorkerMetadata)
+ @VisibleForTesting
+ static WorkerMetadata fromProtoWorkerMetadata(Worker.WorkerMetadata
protoWorkerMetadata)
throws InvalidProtocolBufferException {
Map<Integer, ByteString> protoMailboxInfosMap =
protoWorkerMetadata.getMailboxInfosMap();
Map<Integer, MailboxInfos> mailboxInfosMap =
Maps.newHashMapWithExpectedSize(protoMailboxInfosMap.size());
for (Map.Entry<Integer, ByteString> entry :
protoMailboxInfosMap.entrySet()) {
mailboxInfosMap.put(entry.getKey(),
fromProtoMailboxInfos(entry.getValue()));
}
- return new WorkerMetadata(protoWorkerMetadata.getWorkedId(),
mailboxInfosMap,
- protoWorkerMetadata.getCustomPropertyMap());
+ // A broker using the legacy encoding ships the segment maps as JSON
custom properties. Decode them once here and
+ // drop the raw strings so that the metadata never carries two copies of
the same segments.
+ Map<String, String> customProperties =
protoWorkerMetadata.getCustomPropertyMap();
+ String tableSegmentsJson =
customProperties.get(WorkerMetadata.TABLE_SEGMENTS_MAP_KEY);
+ String logicalTableSegmentsJson =
customProperties.get(WorkerMetadata.LOGICAL_TABLE_SEGMENTS_MAP_KEY);
+ if (tableSegmentsJson != null || logicalTableSegmentsJson != null) {
+ customProperties = new HashMap<>(customProperties);
+ customProperties.remove(WorkerMetadata.TABLE_SEGMENTS_MAP_KEY);
+ customProperties.remove(WorkerMetadata.LOGICAL_TABLE_SEGMENTS_MAP_KEY);
+ }
+ WorkerMetadata workerMetadata =
+ new WorkerMetadata(protoWorkerMetadata.getWorkedId(), mailboxInfosMap,
customProperties);
+ if (protoWorkerMetadata.hasTableSegmentsMap()) {
Review Comment:
Decoding proto first and JSON second is the right direction and is what
makes the rollout order broker-agnostic. Only the reverse case -- old server,
new broker with the encoding on -- breaks, and that is the documented
precondition.
One thing that would make that case much less painful, and is worth doing
whatever happens to the enablement mechanism: when neither
`hasTableSegmentsMap()` nor the JSON key is present, this worker is treated as
an intermediate-stage worker. That is exactly what an old server does when it
receives the new encoding, and it is silent -- a leaf stage that scans nothing,
not an error.
Since the server can see the stage plan, a `WARN` when a stage plan whose
root contains a `TableScanNode` arrives with no segment map would turn the one
dangerous misconfiguration into something greppable, at no cost on the happy
path.
##########
pinot-query-planner/src/test/java/org/apache/pinot/query/routing/QueryPlanSerDeUtilsTest.java:
##########
@@ -0,0 +1,173 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.pinot.query.routing;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javax.annotation.Nullable;
+import org.apache.pinot.common.proto.Worker;
+import org.apache.pinot.spi.utils.JsonUtils;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+
+/// Tests the two wire encodings of the leaf-stage segment maps in
[QueryPlanSerDeUtils]: the native proto fields and
+/// the legacy JSON custom properties, including a server decoding what a
pre-proto broker sends.
+public class QueryPlanSerDeUtilsTest {
+ private static final Map<String, String> CUSTOM_PROPERTIES = Map.of("foo",
"bar");
+ private static final Map<String, List<String>> TABLE_SEGMENTS_MAP =
+ Map.of("OFFLINE", List.of("seg_0", "seg_1"), "REALTIME",
List.of("seg__0__0__20240101T0000Z"));
+ private static final Map<String, List<String>> LOGICAL_TABLE_SEGMENTS_MAP =
+ Map.of("t1_OFFLINE", List.of("t1_seg_0"), "t2_REALTIME",
List.of("t2_seg_0", "t2_seg_1"));
+
+ @DataProvider
+ public static Object[][] encodings() {
+ return new Object[][]{{true}, {false}};
+ }
+
+ @Test(dataProvider = "encodings")
+ public void testLeafWorkerRoundTrip(boolean protoSegmentList)
+ throws Exception {
+ WorkerMetadata workerMetadata = leafWorker(TABLE_SEGMENTS_MAP, null);
+
+ Worker.WorkerMetadata proto = toProto(workerMetadata, protoSegmentList);
+ assertEquals(proto.hasTableSegmentsMap(), protoSegmentList);
+ assertFalse(proto.hasLogicalTableSegmentsMap());
+
assertEquals(proto.getCustomPropertyMap().containsKey(WorkerMetadata.TABLE_SEGMENTS_MAP_KEY),
!protoSegmentList);
+
assertFalse(proto.getCustomPropertyMap().containsKey(WorkerMetadata.LOGICAL_TABLE_SEGMENTS_MAP_KEY));
+ assertEquals(proto.getCustomPropertyMap().get("foo"), "bar");
+
+ WorkerMetadata decoded =
QueryPlanSerDeUtils.fromProtoWorkerMetadata(proto);
+ assertEquals(decoded.getWorkerId(), 3);
+ assertEquals(decoded.getTableSegmentsMap(), TABLE_SEGMENTS_MAP);
+ assertNull(decoded.getLogicalTableSegmentsMap());
+ assertTrue(decoded.isLeafStageWorker());
+ // The JSON is never surfaced as a custom property of the decoded
metadata, whichever encoding was used.
+ assertEquals(decoded.getCustomProperties(), CUSTOM_PROPERTIES);
+ MailboxInfo mailboxInfo =
decoded.getMailboxInfosMap().get(2).getMailboxInfos().get(0);
+ assertEquals(mailboxInfo.getHostname(), "localhost");
+ assertEquals(mailboxInfo.getPort(), 1234);
+ assertEquals(mailboxInfo.getWorkerIds(), List.of(0, 1));
+ }
+
+ @Test(dataProvider = "encodings")
+ public void testLogicalTableLeafWorkerRoundTrip(boolean protoSegmentList)
+ throws Exception {
+ WorkerMetadata workerMetadata = leafWorker(null,
LOGICAL_TABLE_SEGMENTS_MAP);
+
+ Worker.WorkerMetadata proto = toProto(workerMetadata, protoSegmentList);
+ assertFalse(proto.hasTableSegmentsMap());
+ assertEquals(proto.hasLogicalTableSegmentsMap(), protoSegmentList);
+
assertEquals(proto.getCustomPropertyMap().containsKey(WorkerMetadata.LOGICAL_TABLE_SEGMENTS_MAP_KEY),
+ !protoSegmentList);
+
+ WorkerMetadata decoded =
QueryPlanSerDeUtils.fromProtoWorkerMetadata(proto);
+ assertNull(decoded.getTableSegmentsMap());
+ assertEquals(decoded.getLogicalTableSegmentsMap(),
LOGICAL_TABLE_SEGMENTS_MAP);
+ assertTrue(decoded.isLeafStageWorker());
+ assertEquals(decoded.getCustomProperties(), CUSTOM_PROPERTIES);
+ }
+
+ @Test(dataProvider = "encodings")
+ public void testEmptySegmentListStillMarksLeafWorker(boolean
protoSegmentList)
+ throws Exception {
+ // A padded worker of a partitioned table scans no segment but must still
run the leaf stage.
+ Map<String, List<String>> emptySegments = Map.of("OFFLINE", new
ArrayList<>());
Review Comment:
This case does not quite test what its name suggests: `Map.of("OFFLINE", new
ArrayList<>())` is a *non-empty* `SegmentsMap` holding an empty `SegmentList`.
The presence bit keeps `isLeafStageWorker()` true either way, so the test
passes, but it does not exercise the interesting shape: a segments map with
zero entries, which encodes to `SegmentsMap.getDefaultInstance()`. That is
where proto3's explicit presence for singular message fields is doing the work,
and it is the one case where a naive reading would expect the field to vanish
from the wire entirely.
Since the whole leaf/intermediate distinction now rides on that presence
bit, a `Map.of()` case is worth adding to pin the semantics.
##########
pinot-query-planner/src/main/java/org/apache/pinot/query/routing/QueryPlanSerDeUtils.java:
##########
@@ -54,15 +73,55 @@ private static StageMetadata
fromProtoStageMetadata(Worker.StageMetadata protoSt
return new StageMetadata(protoStageMetadata.getStageId(),
workerMetadataList, customProperties);
}
- private static WorkerMetadata fromProtoWorkerMetadata(Worker.WorkerMetadata
protoWorkerMetadata)
+ @VisibleForTesting
+ static WorkerMetadata fromProtoWorkerMetadata(Worker.WorkerMetadata
protoWorkerMetadata)
throws InvalidProtocolBufferException {
Map<Integer, ByteString> protoMailboxInfosMap =
protoWorkerMetadata.getMailboxInfosMap();
Map<Integer, MailboxInfos> mailboxInfosMap =
Maps.newHashMapWithExpectedSize(protoMailboxInfosMap.size());
for (Map.Entry<Integer, ByteString> entry :
protoMailboxInfosMap.entrySet()) {
mailboxInfosMap.put(entry.getKey(),
fromProtoMailboxInfos(entry.getValue()));
}
- return new WorkerMetadata(protoWorkerMetadata.getWorkedId(),
mailboxInfosMap,
- protoWorkerMetadata.getCustomPropertyMap());
+ // A broker using the legacy encoding ships the segment maps as JSON
custom properties. Decode them once here and
+ // drop the raw strings so that the metadata never carries two copies of
the same segments.
+ Map<String, String> customProperties =
protoWorkerMetadata.getCustomPropertyMap();
+ String tableSegmentsJson =
customProperties.get(WorkerMetadata.TABLE_SEGMENTS_MAP_KEY);
+ String logicalTableSegmentsJson =
customProperties.get(WorkerMetadata.LOGICAL_TABLE_SEGMENTS_MAP_KEY);
+ if (tableSegmentsJson != null || logicalTableSegmentsJson != null) {
+ customProperties = new HashMap<>(customProperties);
Review Comment:
The custom-property map handed to `WorkerMetadata` ends up mutable or
immutable depending on which encoding the broker used.
When either JSON key is present it becomes a `HashMap`; otherwise it stays
the proto's immutable map view. So on a server, whether
`workerMetadata.getCustomProperties().put(...)` throws depends on the encoding
of the request that produced it -- a difference that would only ever surface in
production, under the new encoding, on whatever path decides to write to it.
Either always copy, or always wrap in an unmodifiable view. The copy is
cheap next to what this method already does.
##########
pinot-query-runtime/src/test/java/org/apache/pinot/query/service/server/QueryServerTest.java:
##########
@@ -235,13 +235,26 @@ public void onCompleted() {
@Test(dataProvider = "testSql")
public void testWorkerAcceptsWorkerRequestCorrect(String sql)
throws Exception {
+ testWorkerAcceptsWorkerRequestCorrect(sql, false);
+ }
+
+ /// Same as [#testWorkerAcceptsWorkerRequestCorrect(String)] with the
leaf-stage segment lists shipped as native
+ /// proto fields instead of the legacy JSON custom property.
+ @Test(dataProvider = "testSql")
+ public void testWorkerAcceptsProtoSegmentListRequestCorrect(String sql)
Review Comment:
Good to have both encodings covered here, but note what this still does not
reach: it builds the request by calling `toProtoWorkerMetadataList` directly,
so it covers serde plus server decode while bypassing `QueryDispatcher`, the
query option and the live config -- that is, everything that decides *which*
encoding is used. The two halves the rollout depends on are never tested
together.
For a change gated on an operational procedure I would want one integration
test running a real query with `SET protoSegmentList = true` and asserting the
same result as without it, plus the same for a logical table, since
`logicalTableSegmentsMap` is keyed differently and only
`ServerPlanRequestUtils.constructLogicalTableServerQueryRequests` reads it.
##########
pinot-query-planner/src/main/java/org/apache/pinot/query/planner/physical/DispatchablePlanContext.java:
##########
@@ -198,11 +198,19 @@ public Map<Integer, DispatchablePlanFragment>
constructDispatchablePlanFragmentM
QueryServerInstance queryServerInstance = serverEntry.getValue();
serverInstanceToWorkerIdsMap.computeIfAbsent(queryServerInstance, k ->
new ArrayList<>()).add(workerId);
WorkerMetadata workerMetadata = new WorkerMetadata(workerId,
workerIdToMailboxesMap.get(workerId));
+ // A leaf-stage worker is identified by carrying a (possibly empty)
segment map, so every worker of a
+ // scanning stage has to be present in the map. Fail loudly here
instead of letting the worker decay
+ // into an intermediate-stage worker on the server.
if (workerIdToSegmentsMap != null) {
-
workerMetadata.setTableSegmentsMap(workerIdToSegmentsMap.get(workerId));
+ Map<String, List<String>> segmentsMap =
workerIdToSegmentsMap.get(workerId);
+ Preconditions.checkNotNull(segmentsMap, "Missing segments map for
worker id: %s", workerId);
Review Comment:
This is a behavior change independent of the encoding work, and I think it
should be called out in the description (and ideally be its own commit).
Before this PR a null here went to `JsonUtils.objectToString(null)`, i.e.
the literal string `"null"` as the custom property, which made
`isLeafStageWorker()` true while `getTableSegmentsMap()` returned null. On the
server that lands in `ServerPlanRequestUtils.constructServerQueryRequests`,
whose only guard is `assert tableSegmentsMap != null` -- disabled in production
-- so it became an NPE deep in leaf-stage compilation. Failing at plan time
with the worker id is strictly better, so no objection to the change itself.
I checked the sites that populate `workerIdToSegmentsMap` (`WorkerManager`
around lines 449, 668, 959 and 1436, plus `PlanFragmentAndMailboxAssignment` in
the V2 planner) and they all iterate `workerIdToServerInstanceMap`'s key set,
so every worker gets an entry and this should never fire. That reassurance is
worth writing down somewhere more durable than a review thread.
The reason to split it: a bisect of a segment-map failure should not land on
"switch to proto encoding".
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]