ARTEMIS-684 Random is not equaly distributed among different VMs
This commit is contained in:
parent
2bff3d2b9b
commit
92c5d5cd50
|
@ -26,7 +26,11 @@ import org.apache.activemq.artemis.api.core.SimpleString;
|
|||
public class RandomUtil {
|
||||
// Constants -----------------------------------------------------
|
||||
|
||||
protected static final Random random = new Random(System.currentTimeMillis());
|
||||
protected static final Random random = new Random();
|
||||
|
||||
public static Random getRandom() {
|
||||
return random;
|
||||
}
|
||||
|
||||
// Attributes ----------------------------------------------------
|
||||
|
||||
|
@ -75,7 +79,7 @@ public class RandomUtil {
|
|||
}
|
||||
|
||||
public static int randomInterval(final int min, final int max) {
|
||||
return min + randomMax(max - min);
|
||||
return min + random.nextInt(max - min);
|
||||
}
|
||||
|
||||
public static int randomMax(final int max) {
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
*/
|
||||
package org.apache.activemq.artemis.api.core.client.loadbalance;
|
||||
|
||||
import org.apache.activemq.artemis.utils.Random;
|
||||
import org.apache.activemq.artemis.utils.RandomUtil;
|
||||
|
||||
/**
|
||||
* {@link RandomConnectionLoadBalancingPolicy#select(int)} returns a (pseudo) random integer between
|
||||
|
@ -24,8 +24,6 @@ import org.apache.activemq.artemis.utils.Random;
|
|||
*/
|
||||
public final class RandomConnectionLoadBalancingPolicy implements ConnectionLoadBalancingPolicy {
|
||||
|
||||
private final Random random = new Random();
|
||||
|
||||
/**
|
||||
* Returns a pseudo random number between {@code 0} (inclusive) and {@code max} exclusive.
|
||||
*
|
||||
|
@ -34,6 +32,6 @@ public final class RandomConnectionLoadBalancingPolicy implements ConnectionLoad
|
|||
*/
|
||||
@Override
|
||||
public int select(final int max) {
|
||||
return random.getRandom().nextInt(max);
|
||||
return RandomUtil.randomInterval(0, max);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,15 +16,13 @@
|
|||
*/
|
||||
package org.apache.activemq.artemis.api.core.client.loadbalance;
|
||||
|
||||
import org.apache.activemq.artemis.utils.Random;
|
||||
import org.apache.activemq.artemis.utils.RandomUtil;
|
||||
|
||||
/**
|
||||
* {@link RandomConnectionLoadBalancingPolicy#select(int)} chooses a the initial node randomly then subsequent requests return the same node
|
||||
*/
|
||||
public final class RandomStickyConnectionLoadBalancingPolicy implements ConnectionLoadBalancingPolicy {
|
||||
|
||||
private final Random random = new Random();
|
||||
|
||||
private int pos = -1;
|
||||
|
||||
/**
|
||||
|
@ -33,7 +31,7 @@ public final class RandomStickyConnectionLoadBalancingPolicy implements Connecti
|
|||
@Override
|
||||
public int select(final int max) {
|
||||
if (pos == -1) {
|
||||
pos = random.getRandom().nextInt(max);
|
||||
pos = RandomUtil.randomInterval(0, max);
|
||||
}
|
||||
|
||||
return pos;
|
||||
|
|
|
@ -18,7 +18,7 @@ package org.apache.activemq.artemis.api.core.client.loadbalance;
|
|||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.apache.activemq.artemis.utils.Random;
|
||||
import org.apache.activemq.artemis.utils.RandomUtil;
|
||||
|
||||
/**
|
||||
* RoundRobinConnectionLoadBalancingPolicy corresponds to a round-robin load-balancing policy.
|
||||
|
@ -31,8 +31,6 @@ public final class RoundRobinConnectionLoadBalancingPolicy implements Connection
|
|||
|
||||
private static final long serialVersionUID = 7511196010141439559L;
|
||||
|
||||
private final Random random = new Random();
|
||||
|
||||
private boolean first = true;
|
||||
|
||||
private int pos;
|
||||
|
@ -41,7 +39,7 @@ public final class RoundRobinConnectionLoadBalancingPolicy implements Connection
|
|||
public int select(final int max) {
|
||||
if (first) {
|
||||
// We start on a random one
|
||||
pos = random.getRandom().nextInt(max);
|
||||
pos = RandomUtil.randomInterval(0, max);
|
||||
|
||||
first = false;
|
||||
}
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
/*
|
||||
* 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.activemq.artemis.utils;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class Random implements Serializable {
|
||||
|
||||
private static int extraSeed;
|
||||
|
||||
private static final long serialVersionUID = 40335522290950498L;
|
||||
|
||||
private static synchronized long getSeed() {
|
||||
long seed = System.currentTimeMillis() + Random.extraSeed++;
|
||||
|
||||
return seed;
|
||||
}
|
||||
|
||||
private final java.util.Random random = new java.util.Random(Random.getSeed());
|
||||
|
||||
public java.util.Random getRandom() {
|
||||
return random;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
/**
|
||||
* 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.activemq.artemis.tests.unit.core.util;
|
||||
|
||||
|
||||
import java.util.HashSet;
|
||||
|
||||
import org.apache.activemq.artemis.tests.util.SpawnedVMSupport;
|
||||
import org.apache.activemq.artemis.utils.RandomUtil;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
/** This test will start many parallel VMs, to make sure each VM would generate a good distribution of random numbers */
|
||||
public class RandomUtilDistributionTest {
|
||||
public static void main(String[] arg) {
|
||||
|
||||
long start = Long.parseLong(arg[0]);
|
||||
|
||||
try {
|
||||
Thread.sleep((start - System.currentTimeMillis()) / 2);
|
||||
}
|
||||
catch (Exception e) {
|
||||
}
|
||||
while (System.currentTimeMillis() < start) {
|
||||
Thread.yield();
|
||||
}
|
||||
int value;
|
||||
value = RandomUtil.randomInterval(0, 255);
|
||||
System.exit(value);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDistribution() throws Exception {
|
||||
int numberOfStarts = 50;
|
||||
int iterations = 10;
|
||||
|
||||
int value = 0;
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
|
||||
int v = internalDistributionTest(numberOfStarts);
|
||||
|
||||
value += v;
|
||||
}
|
||||
|
||||
// I'm using an extra parenthesis here to avoid rounding problems.
|
||||
// Be careful removing it (make sure you know what you're doing in case you do so)
|
||||
int minimumExpected = (int)((iterations * numberOfStarts) * 0.80);
|
||||
|
||||
System.out.println("value=" + value + ", minimum expected = " + minimumExpected);
|
||||
Assert.assertTrue("The Random distribution is pretty bad. All tries have returned duplicated randoms. value=" + value + ", minimum expected = " + minimumExpected, value > minimumExpected);
|
||||
|
||||
}
|
||||
|
||||
private int internalDistributionTest(int numberOfTries) throws Exception {
|
||||
long timeStart = System.currentTimeMillis() + 5000;
|
||||
Process[] process = new Process[numberOfTries];
|
||||
int[] value = new int[numberOfTries];
|
||||
try {
|
||||
for (int i = 0; i < numberOfTries; i++) {
|
||||
process[i] = SpawnedVMSupport.spawnVM(RandomUtilDistributionTest.class.getName(), true, "" + timeStart);
|
||||
}
|
||||
|
||||
|
||||
HashSet<Integer> valueSet = new HashSet<>();
|
||||
|
||||
for (int i = 0; i < numberOfTries; i++) {
|
||||
value[i] = process[i].waitFor();
|
||||
Assert.assertTrue(value[i] >= 0);
|
||||
valueSet.add(process[i].exitValue());
|
||||
}
|
||||
|
||||
System.out.println("Generated " + valueSet.size() + " randoms out of " + numberOfTries + " tries");
|
||||
|
||||
return valueSet.size();
|
||||
|
||||
|
||||
}
|
||||
finally {
|
||||
for (Process p : process) {
|
||||
if (p != null) {
|
||||
p.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -32,7 +32,6 @@ import java.nio.ByteBuffer;
|
|||
import org.junit.Assert;
|
||||
|
||||
import org.apache.activemq.artemis.utils.DataConstants;
|
||||
import org.apache.activemq.artemis.utils.Random;
|
||||
import org.apache.activemq.artemis.utils.RandomUtil;
|
||||
import org.apache.activemq.artemis.utils.UTF8Util;
|
||||
|
||||
|
@ -44,8 +43,7 @@ public class UTF8Test extends ActiveMQTestBase {
|
|||
|
||||
byte[] bytes = new byte[20000];
|
||||
|
||||
Random random = new Random();
|
||||
random.getRandom().nextBytes(bytes);
|
||||
RandomUtil.getRandom().nextBytes(bytes);
|
||||
|
||||
String str = new String(bytes);
|
||||
|
||||
|
@ -59,12 +57,11 @@ public class UTF8Test extends ActiveMQTestBase {
|
|||
@Test
|
||||
public void testValidateUTFOnDataInput() throws Exception {
|
||||
for (int i = 0; i < 100; i++) {
|
||||
Random random = new Random();
|
||||
|
||||
// Random size between 15k and 20K
|
||||
byte[] bytes = new byte[15000 + RandomUtil.randomPositiveInt() % 5000];
|
||||
|
||||
random.getRandom().nextBytes(bytes);
|
||||
RandomUtil.getRandom().nextBytes(bytes);
|
||||
|
||||
String str = new String(bytes);
|
||||
|
||||
|
|
Loading…
Reference in New Issue