-
Notifications
You must be signed in to change notification settings - Fork 3.8k
Add BroadcastDistributionRule #4077
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
a8c9d27
c6af428
1b7c096
6066fc7
05a7d63
9013378
a47a743
1c0a859
f65e590
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,116 @@ | ||
| /* | ||
| * Licensed to Metamarkets Group Inc. (Metamarkets) under one | ||
| * or more contributor license agreements. See the NOTICE file | ||
| * distributed with this work for additional information | ||
| * regarding copyright ownership. Metamarkets 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 io.druid.server.coordinator.rules; | ||
|
|
||
| import com.metamx.emitter.EmittingLogger; | ||
| import io.druid.server.coordinator.CoordinatorStats; | ||
| import io.druid.server.coordinator.DruidCoordinator; | ||
| import io.druid.server.coordinator.DruidCoordinatorRuntimeParams; | ||
| import io.druid.server.coordinator.ServerHolder; | ||
| import io.druid.timeline.DataSegment; | ||
|
|
||
| import java.util.HashSet; | ||
| import java.util.List; | ||
| import java.util.Set; | ||
|
|
||
| public abstract class BroadcastDistributionRule implements Rule | ||
| { | ||
| private static final EmittingLogger log = new EmittingLogger(BroadcastDistributionRule.class); | ||
|
|
||
| @Override | ||
| public CoordinatorStats run( | ||
| DruidCoordinator coordinator, DruidCoordinatorRuntimeParams params, DataSegment segment | ||
| ) | ||
| { | ||
| // Find servers which holds the segments of co-located data source | ||
| final Set<ServerHolder> loadServerHolders = new HashSet<>(); | ||
| final Set<ServerHolder> dropServerHolders = new HashSet<>(); | ||
| final List<String> colocatedDataSources = getColocatedDataSources(); | ||
| if (colocatedDataSources == null || colocatedDataSources.isEmpty()) { | ||
| loadServerHolders.addAll(params.getDruidCluster().getAllServers()); | ||
| } else { | ||
| params.getDruidCluster().getAllServers().forEach( | ||
| eachHolder -> { | ||
| if (colocatedDataSources.stream() | ||
| .anyMatch(source -> eachHolder.getServer().getDataSource(source) != null)) { | ||
| loadServerHolders.add(eachHolder); | ||
| } else if (eachHolder.isServingSegment(segment)) { | ||
| if (!eachHolder.getPeon().getSegmentsToDrop().contains(segment)) { | ||
| dropServerHolders.add(eachHolder); | ||
| } | ||
| } | ||
| } | ||
| ); | ||
| } | ||
|
|
||
| final CoordinatorStats stats = new CoordinatorStats(); | ||
|
|
||
| return stats.accumulate(assign(loadServerHolders, segment)) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. im wondering if the broadcast rules should label their segments a little differently, or at least be able to distinguish from actual Druid segments
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It'll definitely make reading the logs easier
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think broadcasted segments should be regarded as same as other segments. Every types of queries can be queried on broadcasted segments, but the broadcast information is required by only joins. The information of which sources are broadcasted or not is required by brokers and historicals (and realtimes in the future). Brokers decides a given query should be distributed to which nodes, and this is determined by selecting the nodes holding the segments of non-broadcasted tables. Historicals, as proposed in #4032, join broadcasted segments first and then subsequently join the result with non-broadcasted segments in parallel. I'm currently thinking that brokers are able to figure out which tables are broadcasted by looking at BroadcastDistributionRules, and they will add this information to QueryContext for historicals. If this works, broadcast segments can be regarded as normal. What do you think?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jihoonson Okay that makes sense to me. Can broadcast segments be created dynamically?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe possible in the future when we support the feature of broadcasting tables on the fly, but not yet. |
||
| .accumulate(drop(dropServerHolders, segment)); | ||
| } | ||
|
|
||
| private CoordinatorStats assign( | ||
| final Set<ServerHolder> serverHolders, | ||
| final DataSegment segment | ||
| ) | ||
| { | ||
| final CoordinatorStats stats = new CoordinatorStats(); | ||
| stats.addToGlobalStat(LoadRule.ASSIGNED_COUNT, 0); | ||
|
|
||
| for (ServerHolder holder : serverHolders) { | ||
| if (segment.getSize() > holder.getAvailableSize()) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that like CostBalancerStrategy, this should check if
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks! I added it. |
||
| log.makeAlert("Failed to broadcast segment for [%s]", segment.getDataSource()) | ||
| .addData("segmentId", segment.getIdentifier()) | ||
| .addData("segmentSize", segment.getSize()) | ||
| .addData("hostName", holder.getServer().getHost()) | ||
| .addData("availableSize", holder.getAvailableSize()) | ||
| .emit(); | ||
| } else { | ||
| if (!holder.isLoadingSegment(segment)) { | ||
| holder.getPeon().loadSegment( | ||
| segment, | ||
| null | ||
| ); | ||
|
|
||
| stats.addToGlobalStat(LoadRule.ASSIGNED_COUNT, 1); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return stats; | ||
| } | ||
|
|
||
| private CoordinatorStats drop( | ||
| final Set<ServerHolder> serverHolders, | ||
| final DataSegment segment | ||
| ) | ||
| { | ||
| CoordinatorStats stats = new CoordinatorStats(); | ||
|
|
||
| for (ServerHolder holder : serverHolders) { | ||
| holder.getPeon().dropSegment(segment, null); | ||
| stats.addToGlobalStat(LoadRule.DROPPED_COUNT, 1); | ||
| } | ||
|
|
||
| return stats; | ||
| } | ||
|
|
||
| public abstract List<String> getColocatedDataSources(); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,91 @@ | ||
| /* | ||
| * Licensed to Metamarkets Group Inc. (Metamarkets) under one | ||
| * or more contributor license agreements. See the NOTICE file | ||
| * distributed with this work for additional information | ||
| * regarding copyright ownership. Metamarkets 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 io.druid.server.coordinator.rules; | ||
|
|
||
| import com.fasterxml.jackson.annotation.JsonCreator; | ||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||
| import io.druid.timeline.DataSegment; | ||
| import org.joda.time.DateTime; | ||
| import org.joda.time.Interval; | ||
|
|
||
| import java.util.List; | ||
| import java.util.Objects; | ||
|
|
||
| public class ForeverBroadcastDistributionRule extends BroadcastDistributionRule | ||
| { | ||
| static final String TYPE = "broadcastForever"; | ||
|
|
||
| private final List<String> colocatedDataSources; | ||
|
|
||
| @JsonCreator | ||
| public ForeverBroadcastDistributionRule( | ||
| @JsonProperty("colocatedDataSources") List<String> colocatedDataSources | ||
| ) | ||
| { | ||
| this.colocatedDataSources = colocatedDataSources; | ||
| } | ||
|
|
||
| @Override | ||
| @JsonProperty | ||
| public String getType() | ||
| { | ||
| return TYPE; | ||
| } | ||
|
|
||
| @Override | ||
| @JsonProperty | ||
| public List<String> getColocatedDataSources() | ||
| { | ||
| return colocatedDataSources; | ||
| } | ||
|
|
||
| @Override | ||
| public boolean appliesTo(DataSegment segment, DateTime referenceTimestamp) | ||
| { | ||
| return true; | ||
| } | ||
|
|
||
| @Override | ||
| public boolean appliesTo(Interval interval, DateTime referenceTimestamp) | ||
| { | ||
| return true; | ||
| } | ||
|
|
||
| @Override | ||
| public boolean equals(Object o) | ||
| { | ||
| if (this == o) { | ||
| return true; | ||
| } | ||
|
|
||
| if (o == null || o.getClass() != getClass()) { | ||
| return false; | ||
| } | ||
|
|
||
| ForeverBroadcastDistributionRule that = (ForeverBroadcastDistributionRule) o; | ||
| return Objects.equals(colocatedDataSources, that.colocatedDataSources); | ||
| } | ||
|
|
||
| @Override | ||
| public int hashCode() | ||
| { | ||
| return Objects.hash(getType(), colocatedDataSources); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should skip holders that already have the segment in their drop lists (
peon.getSegmentsToDrop()), like DruidCoordinator.moveSegment and DruidCoordinatorCleanupUnneeded.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! I added it.