You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by nr...@apache.org on 2018/03/26 21:57:57 UTC

[geode] branch develop updated: GEODE-4808: Add performance tests for rebalance simulation (#1667)

This is an automated email from the ASF dual-hosted git repository.

nreich pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/geode.git


The following commit(s) were added to refs/heads/develop by this push:
     new 6d729d3  GEODE-4808: Add performance tests for rebalance simulation (#1667)
6d729d3 is described below

commit 6d729d3bb7d83d2d335a618d14ead89d4bc5f1ab
Author: Nick Reich <nr...@pivotal.io>
AuthorDate: Mon Mar 26 14:57:54 2018 -0700

    GEODE-4808: Add performance tests for rebalance simulation (#1667)
---
 .../RebalanceGrowingClusterBenchmark.java          |  71 ++++++++
 .../rebalance/RebalanceModelBuilder.java           | 188 +++++++++++++++++++++
 .../RebalanceOnAddingMemberBenchmark.java          |  73 ++++++++
 .../rebalance/RebalanceRegionBenchmark.java        |  73 ++++++++
 4 files changed, 405 insertions(+)

diff --git a/geode-core/src/jmh/java/org/apache/geode/cache/partitioned/rebalance/RebalanceGrowingClusterBenchmark.java b/geode-core/src/jmh/java/org/apache/geode/cache/partitioned/rebalance/RebalanceGrowingClusterBenchmark.java
new file mode 100644
index 0000000..5855c65
--- /dev/null
+++ b/geode-core/src/jmh/java/org/apache/geode/cache/partitioned/rebalance/RebalanceGrowingClusterBenchmark.java
@@ -0,0 +1,71 @@
+/*
+ * 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.geode.cache.partitioned.rebalance;
+
+import java.net.UnknownHostException;
+import java.util.concurrent.TimeUnit;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+
+import org.apache.geode.internal.cache.partitioned.rebalance.MoveBuckets;
+import org.apache.geode.internal.cache.partitioned.rebalance.RebalanceDirector;
+import org.apache.geode.internal.cache.partitioned.rebalance.model.PartitionedRegionLoadModel;
+
+/**
+ * This test simulates an existing partitioned region with evenly distributed numbers of buckets,
+ * all of which are exactly the same size and with no replicate copies. The number of members is
+ * then doubled and the region rebalanced across the new members. This represents a best-case
+ * scenario for rebalancing a cluster after greatly increasing the number of members.
+ */
+@State(Scope.Thread)
+@Fork(1)
+public class RebalanceGrowingClusterBenchmark {
+
+  @Param({"1", "2", "4", "8", "16", "32"})
+  public int startingMembers;
+
+  @Benchmark
+  @Measurement(time = 5, iterations = 10)
+  @Warmup(iterations = 5)
+  @BenchmarkMode(Mode.AverageTime)
+  @OutputTimeUnit(TimeUnit.MILLISECONDS)
+  public int doubleMembersAndRebalancedRegion() throws UnknownHostException {
+    int totalBuckets = startingMembers * 31;
+    RebalanceModelBuilder modelBuilder = new RebalanceModelBuilder(startingMembers, totalBuckets);
+    PartitionedRegionLoadModel model = modelBuilder.withNewMembers(startingMembers).createModel();
+    return doMoves(new MoveBuckets(), model);
+  }
+
+  private int doMoves(RebalanceDirector director, PartitionedRegionLoadModel model) {
+    int moveCount = 0;
+
+    model.initialize();
+    director.initialize(model);
+    while (director.nextStep()) {
+      moveCount++;
+    }
+
+    return moveCount;
+  }
+}
diff --git a/geode-core/src/jmh/java/org/apache/geode/cache/partitioned/rebalance/RebalanceModelBuilder.java b/geode-core/src/jmh/java/org/apache/geode/cache/partitioned/rebalance/RebalanceModelBuilder.java
new file mode 100644
index 0000000..95d731a
--- /dev/null
+++ b/geode-core/src/jmh/java/org/apache/geode/cache/partitioned/rebalance/RebalanceModelBuilder.java
@@ -0,0 +1,188 @@
+/*
+ * 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.geode.cache.partitioned.rebalance;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+
+import org.apache.geode.DataSerializer;
+import org.apache.geode.distributed.internal.membership.InternalDistributedMember;
+import org.apache.geode.internal.cache.partitioned.OfflineMemberDetails;
+import org.apache.geode.internal.cache.partitioned.PRLoad;
+import org.apache.geode.internal.cache.partitioned.PartitionMemberInfoImpl;
+import org.apache.geode.internal.cache.partitioned.rebalance.SimulatedBucketOperator;
+import org.apache.geode.internal.cache.partitioned.rebalance.model.AddressComparor;
+import org.apache.geode.internal.cache.partitioned.rebalance.model.PartitionedRegionLoadModel;
+import org.apache.geode.internal.cache.persistence.PersistentMemberID;
+
+class RebalanceModelBuilder {
+  private static final int DEFAULT_MEAN_BUCKET_SIZE = 100;
+
+  private final Random random = new Random();
+  private int members;
+  private int buckets;
+  private int newMembers;
+  private int bucketSizeDeviation;
+
+  RebalanceModelBuilder(int members, int buckets) {
+    this.members = members;
+    this.buckets = buckets;
+  }
+
+  RebalanceModelBuilder withNewMembers(int newMembers) {
+    this.newMembers = newMembers;
+    return this;
+  }
+
+  RebalanceModelBuilder withBucketSizeStandardDeviation(int deviation) {
+    this.bucketSizeDeviation = deviation;
+    return this;
+  }
+
+  PartitionedRegionLoadModel createModel() throws UnknownHostException {
+    SimulatedBucketOperator bucketOperator = new SimulatedBucketOperator();
+    PartitionedRegionLoadModel model = new PartitionedRegionLoadModel(bucketOperator, 0, buckets,
+        comparor, Collections.emptySet(), null);
+    double bucketsPerMember = (double) buckets / members;
+    int bucketOffset = 0;
+    List<PartitionMemberInfoImpl> members = new ArrayList<>();
+
+    for (int memberId = 0; memberId < this.members; memberId++) {
+      int bucketsOnMember = getBucketsOnMember(bucketsPerMember, memberId);
+      long[] loads = new long[this.buckets];
+      long[] primaryLoads = new long[this.buckets];
+      for (int bucketId = bucketOffset; bucketId < bucketOffset + bucketsOnMember; bucketId++) {
+        loads[bucketId] = getBucketSize(memberId);
+        primaryLoads[bucketId] = 1;
+      }
+
+      InternalDistributedMember member =
+          new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), memberId);
+      PartitionMemberInfoImpl memberInfo =
+          buildDetails(member, 500, Integer.MAX_VALUE, loads, primaryLoads);
+      members.add(memberInfo);
+
+      bucketOffset += bucketsOnMember;
+    }
+
+    members.addAll(getNewMembers());
+    model.addRegion("a", members, new FakeOfflineDetails(), true);
+    return model;
+  }
+
+  private int getBucketsOnMember(double bucketsPerMember, double memberId) {
+    if (memberId / this.members < (bucketsPerMember - (int) bucketsPerMember)) {
+      return (int) Math.ceil(bucketsPerMember);
+    } else {
+      return (int) Math.floor(bucketsPerMember);
+    }
+  }
+
+  private int getBucketSize(int memberId) {
+    int loadDeviation = (int) (random.nextGaussian() * bucketSizeDeviation);
+
+    if (memberId % 2 == 0) {
+      return DEFAULT_MEAN_BUCKET_SIZE + Math.abs(loadDeviation);
+    } else {
+      return DEFAULT_MEAN_BUCKET_SIZE - Math.abs(loadDeviation);
+    }
+  }
+
+  private List<PartitionMemberInfoImpl> getNewMembers() throws UnknownHostException {
+    List<PartitionMemberInfoImpl> members = new ArrayList<>();
+    for (int i = 0; i < newMembers; i++) {
+      InternalDistributedMember newMember =
+          new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), this.members + i);
+      PartitionMemberInfoImpl newMemberInfo =
+          buildDetails(newMember, 500, Integer.MAX_VALUE, new long[buckets], new long[buckets]);
+      members.add(newMemberInfo);
+    }
+    return members;
+  }
+
+
+  private PartitionMemberInfoImpl buildDetails(InternalDistributedMember id, float weight,
+      long localMaxMemory, long[] loads, long[] primaryLoads) {
+    PRLoad prLoad = new PRLoad(loads.length, weight);
+    int size = 0;
+    int primaryCount = 0;
+    int bucketCount = 0;
+    long[] bucketSizes = new long[loads.length];
+    for (int bucketId = 0; bucketId < loads.length; bucketId++) {
+      prLoad.addBucket(bucketId, loads[bucketId], primaryLoads[bucketId]);
+      bucketSizes[bucketId] = loads[bucketId];
+      size += bucketSizes[bucketId];
+      if (loads[bucketId] != 0) {
+        bucketCount++;
+      }
+      if (primaryLoads[bucketId] != 0) {
+        primaryCount++;
+      }
+    }
+    return new PartitionMemberInfoImpl(id, localMaxMemory, size, bucketCount, primaryCount, prLoad,
+        bucketSizes);
+  }
+
+  private static final AddressComparor comparor = new AddressComparor() {
+    @Override
+    public boolean enforceUniqueZones() {
+      return false;
+    }
+
+    @Override
+    public boolean areSameZone(InternalDistributedMember member1,
+        InternalDistributedMember member2) {
+      return false;
+    }
+  };
+
+  private static class FakeOfflineDetails implements OfflineMemberDetails {
+
+    private Set<PersistentMemberID> offlineMembers;
+
+    FakeOfflineDetails() {
+      this(Collections.emptySet());
+    }
+
+    FakeOfflineDetails(Set<PersistentMemberID> offlineMembers) {
+      this.offlineMembers = offlineMembers;
+    }
+
+    @Override
+    public Set<PersistentMemberID> getOfflineMembers(int bucketId) {
+      return this.offlineMembers;
+    }
+
+    @Override
+    public void fromData(DataInput in) throws IOException, ClassNotFoundException {
+      offlineMembers = DataSerializer.readObject(in);
+    }
+
+    @Override
+    public void toData(DataOutput out) throws IOException {
+      DataSerializer.writeObject(offlineMembers, out);
+
+    }
+
+  }
+}
diff --git a/geode-core/src/jmh/java/org/apache/geode/cache/partitioned/rebalance/RebalanceOnAddingMemberBenchmark.java b/geode-core/src/jmh/java/org/apache/geode/cache/partitioned/rebalance/RebalanceOnAddingMemberBenchmark.java
new file mode 100644
index 0000000..7fae3d4
--- /dev/null
+++ b/geode-core/src/jmh/java/org/apache/geode/cache/partitioned/rebalance/RebalanceOnAddingMemberBenchmark.java
@@ -0,0 +1,73 @@
+/*
+ * 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.geode.cache.partitioned.rebalance;
+
+import java.net.UnknownHostException;
+import java.util.concurrent.TimeUnit;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+
+import org.apache.geode.internal.cache.partitioned.rebalance.MoveBuckets;
+import org.apache.geode.internal.cache.partitioned.rebalance.RebalanceDirector;
+import org.apache.geode.internal.cache.partitioned.rebalance.model.PartitionedRegionLoadModel;
+
+/**
+ * This test simulates an existing partitioned region with evenly distributed numbers of buckets,
+ * all of which are exactly the same size and with no replicate copies. A single new member is added
+ * to the cluster and the region is then rebalanced. This represents a best-case scenario for
+ * rebalancing a cluster after the addition of a member.
+ */
+@State(Scope.Thread)
+@Fork(1)
+public class RebalanceOnAddingMemberBenchmark {
+
+  @Param({"4", "8", "16", "32", "64", "128"})
+  public int startingMembers;
+
+  @Param({"100", "200", "400", "800", "1600", "3200"})
+  public int totalBuckets;
+
+  @Benchmark
+  @Measurement(time = 5, iterations = 10)
+  @Warmup(iterations = 5)
+  @BenchmarkMode(Mode.AverageTime)
+  @OutputTimeUnit(TimeUnit.MILLISECONDS)
+  public int addNewMemberToBalancedRegion() throws UnknownHostException {
+    RebalanceModelBuilder modelBuilder = new RebalanceModelBuilder(startingMembers, totalBuckets);
+    PartitionedRegionLoadModel model = modelBuilder.withNewMembers(1).createModel();
+    return doMoves(new MoveBuckets(), model);
+  }
+
+  private int doMoves(RebalanceDirector director, PartitionedRegionLoadModel model) {
+    int moveCount = 0;
+
+    model.initialize();
+    director.initialize(model);
+    while (director.nextStep()) {
+      moveCount++;
+    }
+
+    return moveCount;
+  }
+}
diff --git a/geode-core/src/jmh/java/org/apache/geode/cache/partitioned/rebalance/RebalanceRegionBenchmark.java b/geode-core/src/jmh/java/org/apache/geode/cache/partitioned/rebalance/RebalanceRegionBenchmark.java
new file mode 100644
index 0000000..d9d2be7
--- /dev/null
+++ b/geode-core/src/jmh/java/org/apache/geode/cache/partitioned/rebalance/RebalanceRegionBenchmark.java
@@ -0,0 +1,73 @@
+/*
+ * 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.geode.cache.partitioned.rebalance;
+
+import java.net.UnknownHostException;
+import java.util.concurrent.TimeUnit;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+
+import org.apache.geode.internal.cache.partitioned.rebalance.MoveBuckets;
+import org.apache.geode.internal.cache.partitioned.rebalance.RebalanceDirector;
+import org.apache.geode.internal.cache.partitioned.rebalance.model.PartitionedRegionLoadModel;
+
+/**
+ * This test simulates a worst case scenario for rebalancing a region. Each bucket is of a
+ * different size, randomly determined, but defined by a Gaussian distribution. All buckets
+ * on a member are either larger or smaller than the average bucket size, which leads to a
+ * maximal number of bucket moves being required.
+ */
+@State(Scope.Thread)
+@Fork(1)
+public class RebalanceRegionBenchmark {
+  private static final int STARTING_MEMBERS = 32;
+  private static final int TOTAL_BUCKETS = 800;
+
+  @Param({"0", "5", "10", "15", "20"})
+  public int deviation;
+
+  @Benchmark
+  @Measurement(time = 5, iterations = 10)
+  @Warmup(iterations = 5)
+  @BenchmarkMode(Mode.AverageTime)
+  @OutputTimeUnit(TimeUnit.MILLISECONDS)
+  public int rebalance() throws UnknownHostException {
+    RebalanceModelBuilder modelBuilder = new RebalanceModelBuilder(STARTING_MEMBERS, TOTAL_BUCKETS);
+    PartitionedRegionLoadModel model =
+        modelBuilder.withBucketSizeStandardDeviation(deviation).createModel();
+    return doMoves(new MoveBuckets(), model);
+  }
+
+  private int doMoves(RebalanceDirector director, PartitionedRegionLoadModel model) {
+    int moveCount = 0;
+
+    model.initialize();
+    director.initialize(model);
+    while (director.nextStep()) {
+      moveCount++;
+    }
+
+    return moveCount;
+  }
+}

-- 
To stop receiving notification emails like this one, please contact
nreich@apache.org.