Skip to content
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

[COST] add MigrateTimeCost #1665

Open
wants to merge 38 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
5b5d00f
add PartitionMigrateTimeCost and revise MetricSensor#fetch
qoo332001 Apr 17, 2023
05b838e
fix issues
qoo332001 Apr 21, 2023
1b1a18a
fix conflict
qoo332001 Apr 21, 2023
43b1a5f
fix bug
qoo332001 Apr 21, 2023
749b63b
fix issues
qoo332001 Apr 23, 2023
f39ad51
fix issues
qoo332001 Apr 29, 2023
262a407
update docs
qoo332001 Apr 29, 2023
33967dd
fix issues
qoo332001 Apr 30, 2023
81439fe
fix conflict
qoo332001 Apr 30, 2023
e4e5981
fix issue
qoo332001 May 1, 2023
0bd88b1
Merge branch 'main' of https://github.com/skiptests/astraea into addP…
qoo332001 May 1, 2023
efb3216
fix issues
qoo332001 May 4, 2023
7452921
update
qoo332001 May 9, 2023
23e7f3f
fix conflict
qoo332001 May 9, 2023
ea28b65
fix issue
qoo332001 May 10, 2023
c74cf56
fix conflict
qoo332001 May 10, 2023
1ad7892
fix issues
qoo332001 May 11, 2023
60a62be
Merge branch 'main' of https://github.com/skiptests/astraea into addP…
qoo332001 May 11, 2023
e4ac803
update docs
qoo332001 May 15, 2023
311a48e
fix conflict
qoo332001 May 15, 2023
f4e1297
fix issues
May 16, 2023
1eca3ea
Merge branch 'main' of github.com:skiptests/astraea into addPartition…
May 16, 2023
9257e72
move brokerMaxRate() to MigrationCost
May 16, 2023
c27fe29
fix conflict
qoo332001 May 18, 2023
6df0a15
fix conflict
qoo332001 May 18, 2023
848f4e9
fix tests
qoo332001 May 18, 2023
e3dd72e
fix issue
qoo332001 May 22, 2023
38031ee
Merge branch 'main' of https://github.com/skiptests/astraea into addP…
qoo332001 May 22, 2023
dbae033
merge main
qoo332001 May 22, 2023
ebbc82b
remove reduant code
qoo332001 May 22, 2023
85ce98d
fix bugs
qoo332001 May 25, 2023
00a6fc7
fix bugs
qoo332001 May 25, 2023
5298769
fix issues
qoo332001 May 27, 2023
3731482
fix bugs
qoo332001 May 27, 2023
c04bdb8
fix conflict
qoo332001 May 29, 2023
a7e2515
Merge branch 'main' of https://github.com/skiptests/astraea into addP…
qoo332001 May 30, 2023
3e006c6
fix bug and merge main
qoo332001 May 30, 2023
bd59c89
fix issue
qoo332001 May 30, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion app/src/main/java/org/astraea/app/web/BalancerHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,8 @@ private PlanExecutionProgress progress(String taskId) {
solution ->
new PlanReport(
changes.apply(solution),
MigrationCost.migrationCosts(contextCluster, solution.proposal())))
MigrationCost.migrationCosts(
contextCluster, solution.proposal(), solution.clusterBean())))
.orElse(null);
var phase = balancerConsole.taskPhase(taskId).orElseThrow();
return new PlanExecutionProgress(
Expand Down
3 changes: 2 additions & 1 deletion app/src/main/java/org/astraea/app/web/SensorHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ public class SensorHandler implements Handler {
private static final Set<String> DEFAULT_COSTS =
Set.of(
"org.astraea.common.cost.ReplicaLeaderCost",
"org.astraea.common.cost.NetworkIngressCost");
"org.astraea.common.cost.NetworkIngressCost",
"org.astraea.common.cost.PartitionMigrateTimeCost");
qoo332001 marked this conversation as resolved.
Show resolved Hide resolved

SensorHandler(Sensors sensors) {
this.sensors = sensors;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ void testBeans() {
Assertions.assertInstanceOf(
SensorHandler.Response.class,
defaultCostHandler.get(Channel.EMPTY).toCompletableFuture().join());
Assertions.assertEquals(2, defaultCostResponse.costs.size());
Assertions.assertEquals(3, defaultCostResponse.costs.size());

var changedCostResponse =
Assertions.assertInstanceOf(
Expand Down
46 changes: 7 additions & 39 deletions common/src/main/java/org/astraea/common/balancer/Balancer.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.astraea.common.balancer.algorithms.GreedyBalancer;
import org.astraea.common.balancer.algorithms.SingleStepBalancer;
import org.astraea.common.cost.ClusterCost;
import org.astraea.common.metrics.ClusterBean;

public interface Balancer {

Expand All @@ -32,45 +33,12 @@ public interface Balancer {
*/
Optional<Plan> offer(AlgorithmConfig config);

class Plan {
private final ClusterInfo initialClusterInfo;
private final ClusterCost initialClusterCost;

private final ClusterInfo proposal;
private final ClusterCost proposalClusterCost;

public Plan(
ClusterInfo initialClusterInfo,
ClusterCost initialClusterCost,
ClusterInfo proposal,
ClusterCost proposalClusterCost) {
this.initialClusterInfo = initialClusterInfo;
this.initialClusterCost = initialClusterCost;
this.proposal = proposal;
this.proposalClusterCost = proposalClusterCost;
}

public ClusterInfo initialClusterInfo() {
return initialClusterInfo;
}

/**
* The {@link ClusterCost} score of the original {@link ClusterInfo} when this plan is start
qoo332001 marked this conversation as resolved.
Show resolved Hide resolved
* generating.
*/
public ClusterCost initialClusterCost() {
return initialClusterCost;
}

public ClusterInfo proposal() {
return proposal;
}

/** The {@link ClusterCost} score of the proposed new allocation. */
public ClusterCost proposalClusterCost() {
return proposalClusterCost;
}
}
record Plan(
qoo332001 marked this conversation as resolved.
Show resolved Hide resolved
ClusterBean clusterBean,
ClusterInfo initialClusterInfo,
ClusterCost initialClusterCost,
ClusterInfo proposal,
ClusterCost proposalClusterCost) {}

/** The official implementation of {@link Balancer}. */
enum Official implements EnumInfo {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public class BalancerProblemFormat {
"org.astraea.common.cost.RecordSizeCost",
"org.astraea.common.cost.ReplicaNumberCost",
"org.astraea.common.cost.ReplicaLeaderSizeCost",
"org.astraea.common.cost.PartitionMigrateTimeCost",
"org.astraea.common.cost.BrokerDiskSpaceCost");

public AlgorithmConfig parse() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ public Optional<Plan> offer(AlgorithmConfig config) {
.map(
newAllocation ->
new Plan(
config.clusterBean(),
config.clusterInfo(),
initialCost,
newAllocation,
Expand Down Expand Up @@ -238,6 +239,7 @@ public Optional<Plan> offer(AlgorithmConfig config) {
.overflow()) {
return Optional.of(
new Plan(
config.clusterBean(),
config.clusterInfo(),
config.clusterCostFunction().clusterCost(config.clusterInfo(), clusterBean),
currentClusterInfo,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ public Optional<Plan> offer(AlgorithmConfig config) {
.map(
newAllocation ->
new Plan(
config.clusterBean(),
config.clusterInfo(),
currentCost,
newAllocation,
Expand All @@ -139,6 +140,7 @@ public Optional<Plan> offer(AlgorithmConfig config) {
.overflow()) {
return Optional.of(
new Plan(
config.clusterBean(),
config.clusterInfo(),
config.clusterCostFunction().clusterCost(config.clusterInfo(), clusterBean),
currentClusterInfo,
Expand Down
75 changes: 73 additions & 2 deletions common/src/main/java/org/astraea/common/cost/MigrationCost.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,26 +18,31 @@

import java.util.List;
import java.util.Map;
import java.util.OptionalDouble;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.astraea.common.admin.ClusterInfo;
import org.astraea.common.admin.NodeInfo;
import org.astraea.common.admin.Replica;
import org.astraea.common.metrics.ClusterBean;
import org.astraea.common.metrics.HasBeanObject;
import org.astraea.common.metrics.broker.HasMaxRate;

public class MigrationCost {

public final String name;

public final Map<Integer, Long> brokerCosts;
public static final String TO_SYNC_BYTES = "record size to sync (bytes)";
public static final String TO_FETCH_BYTES = "record size to fetch (bytes)";
public static final String REPLICA_LEADERS_TO_ADDED = "leader number to add";
public static final String REPLICA_LEADERS_TO_REMOVE = "leader number to remove";
public static final String CHANGED_REPLICAS = "changed replicas";
public static final String PARTITION_MIGRATED_TIME = "partition migrated time";
qoo332001 marked this conversation as resolved.
Show resolved Hide resolved

public static List<MigrationCost> migrationCosts(ClusterInfo before, ClusterInfo after) {
public static List<MigrationCost> migrationCosts(
ClusterInfo before, ClusterInfo after, ClusterBean clusterBean) {
var migrateInBytes = recordSizeToSync(before, after);
var migrateOutBytes = recordSizeToFetch(before, after);
var migrateReplicaNum = replicaNumChanged(before, after);
Expand All @@ -46,6 +51,9 @@ public static List<MigrationCost> migrationCosts(ClusterInfo before, ClusterInfo
return List.of(
new MigrationCost(TO_SYNC_BYTES, migrateInBytes),
new MigrationCost(TO_FETCH_BYTES, migrateOutBytes),
new MigrationCost(CHANGED_REPLICAS, migrateReplicaNum),
new MigrationCost(
PARTITION_MIGRATED_TIME, brokerMigrationSecond(before, after, clusterBean)),
new MigrationCost(REPLICA_LEADERS_TO_ADDED, migrateInLeader),
new MigrationCost(REPLICA_LEADERS_TO_REMOVE, migrateOutLeader),
new MigrationCost(CHANGED_REPLICAS, migrateReplicaNum));
Expand Down Expand Up @@ -76,6 +84,69 @@ static Map<Integer, Long> replicaLeaderToRemove(ClusterInfo before, ClusterInfo
return migratedChanged(before, after, false, Replica::isLeader, ignore -> 1L);
}

/**
* @param before the ClusterInfo before migrated replicas
* @param after the ClusterInfo after migrated replicas
* @param clusterBean cluster metrics
* @return estimated migrated time required by all brokers (seconds)
*/
public static Map<Integer, Long> brokerMigrationSecond(
ClusterInfo before, ClusterInfo after, ClusterBean clusterBean) {
var brokerInRate =
before.nodes().stream()
.collect(
Collectors.toMap(
NodeInfo::id,
nodeInfo ->
brokerMaxRate(
nodeInfo.id(),
clusterBean,
PartitionMigrateTimeCost.MaxReplicationInRateBean.class)));
var brokerOutRate =
before.nodes().stream()
.collect(
Collectors.toMap(
NodeInfo::id,
nodeInfo ->
brokerMaxRate(
nodeInfo.id(),
clusterBean,
PartitionMigrateTimeCost.MaxReplicationOutRateBean.class)));
var brokerMigrateInSecond =
MigrationCost.recordSizeToFetch(before, after).entrySet().stream()
.collect(
Collectors.toMap(
Map.Entry::getKey,
brokerSize ->
brokerSize.getValue() / brokerInRate.get(brokerSize.getKey()).orElse(0)));
var brokerMigrateOutSecond =
MigrationCost.recordSizeToSync(before, after).entrySet().stream()
.collect(
Collectors.toMap(
Map.Entry::getKey,
brokerSize ->
brokerSize.getValue() / brokerOutRate.get(brokerSize.getKey()).orElse(0)));
return Stream.concat(before.nodes().stream(), after.nodes().stream())
.map(NodeInfo::id)
.distinct()
.collect(
Collectors.toMap(
nodeId -> nodeId,
nodeId ->
(long)
Math.max(
brokerMigrateInSecond.get(nodeId),
brokerMigrateOutSecond.get(nodeId))));
}

static OptionalDouble brokerMaxRate(
qoo332001 marked this conversation as resolved.
Show resolved Hide resolved
int identity, ClusterBean clusterBean, Class<? extends HasBeanObject> statisticMetrics) {
return clusterBean
.brokerMetrics(identity, statisticMetrics)
.mapToDouble(b -> ((HasMaxRate) b).maxRate())
.max();
}

/**
* @param before the ClusterInfo before migrated replicas
* @param after the ClusterInfo after migrated replicas
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*
* 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.astraea.common.cost;

import static org.astraea.common.cost.MigrationCost.brokerMaxRate;

import java.time.Duration;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import org.astraea.common.Configuration;
import org.astraea.common.admin.ClusterInfo;
import org.astraea.common.metrics.BeanObject;
import org.astraea.common.metrics.ClusterBean;
import org.astraea.common.metrics.Sensor;
import org.astraea.common.metrics.broker.HasMaxRate;
import org.astraea.common.metrics.broker.ServerMetrics;
import org.astraea.common.metrics.collector.MetricSensor;
import org.astraea.common.metrics.stats.Max;

/** MoveCost: more max write rate change -> higher migrate cost. */
public class PartitionMigrateTimeCost implements HasMoveCost {
private static final String REPLICATION_IN_RATE = "replication_in_rate";
private static final String REPLICATION_OUT_RATE = "replication_out_rate";
static final String MAX_MIGRATE_TIME_KEY = "max.migrated.time.limit";
public static final String STATISTICS_RATE_KEY = "statistics.rate.key";
qoo332001 marked this conversation as resolved.
Show resolved Hide resolved

// metrics windows size
private final Duration maxMigrateTime;

public PartitionMigrateTimeCost(Configuration config) {
this.maxMigrateTime =
config.duration(MAX_MIGRATE_TIME_KEY).orElse(Duration.ofSeconds(Long.MAX_VALUE));
}

@Override
public MetricSensor metricSensor() {
return (client, clusterBean) -> {
var oldInRate =
brokerMaxRate(
client.identity(),
clusterBean,
PartitionMigrateTimeCost.MaxReplicationInRateBean.class);
var oldOutRate =
brokerMaxRate(
client.identity(),
clusterBean,
PartitionMigrateTimeCost.MaxReplicationOutRateBean.class);
var newInMetrics = ServerMetrics.BrokerTopic.REPLICATION_BYTES_IN_PER_SEC.fetch(client);
var newOutMetrics = ServerMetrics.BrokerTopic.REPLICATION_BYTES_OUT_PER_SEC.fetch(client);
var current = Duration.ofMillis(System.currentTimeMillis());
var maxRateSensor =
Sensor.builder()
.addStats(
Map.of(
REPLICATION_IN_RATE, Max.<Double>of(),
REPLICATION_OUT_RATE, Max.of()))
.build();
maxRateSensor.record(REPLICATION_IN_RATE, newInMetrics.oneMinuteRate());
qoo332001 marked this conversation as resolved.
Show resolved Hide resolved
maxRateSensor.record(REPLICATION_OUT_RATE, newOutMetrics.oneMinuteRate());
var inRate = maxRateSensor.measure(REPLICATION_IN_RATE);
var outRate = maxRateSensor.measure(REPLICATION_OUT_RATE);
return List.of(
new MaxReplicationInRateBean(
() ->
new BeanObject(
newInMetrics.beanObject().domainName(),
newInMetrics.beanObject().properties(),
Map.of(STATISTICS_RATE_KEY, Math.max(oldInRate.orElse(0), inRate)),
current.toMillis())),
new MaxReplicationOutRateBean(
() ->
new BeanObject(
newOutMetrics.beanObject().domainName(),
newOutMetrics.beanObject().properties(),
Map.of(STATISTICS_RATE_KEY, Math.max(oldOutRate.orElse(0), outRate)),
current.toMillis())));
};
}

@Override
public MoveCost moveCost(ClusterInfo before, ClusterInfo after, ClusterBean clusterBean) {
var brokerMigrateSecond = MigrationCost.brokerMigrationSecond(before, after, clusterBean);
var planMigrateSecond =
brokerMigrateSecond.values().stream()
.max(Comparator.comparing(Function.identity()))
.orElse(Long.MAX_VALUE);
return () -> planMigrateSecond > this.maxMigrateTime.getSeconds();
}

public record MaxReplicationInRateBean(HasMaxRate hasMaxRate) implements HasMaxRate {
@Override
public BeanObject beanObject() {
return hasMaxRate.beanObject();
}
}

public record MaxReplicationOutRateBean(HasMaxRate hasMaxRate) implements HasMaxRate {
@Override
public BeanObject beanObject() {
return hasMaxRate.beanObject();
}
}
}
8 changes: 8 additions & 0 deletions common/src/main/java/org/astraea/common/metrics/Sensor.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ static Builder<Long> builder() {
/** Record the new get data */
void record(V value);

/** Record the new get data for given `metricName` */
void record(String statName, V value);

/**
* Get the statistic by the given `metricName`.
*
Expand Down Expand Up @@ -69,6 +72,11 @@ public synchronized void record(V value) {
stats.values().forEach(stat -> stat.record(value));
}

@Override
public void record(String statName, V value) {
stats.get(statName).record(value);
}

@Override
public V measure(String statName) {
return stats.get(statName).measure();
Expand Down
Loading