|
17 | 17 | */ |
18 | 18 | package org.apache.hadoop.hbase.chaos.monkies; |
19 | 19 |
|
| 20 | +import java.util.ArrayList; |
20 | 21 | import java.util.Arrays; |
21 | 22 | import java.util.Collection; |
22 | 23 | import java.util.Collections; |
|
30 | 31 | import org.apache.hadoop.hbase.IntegrationTestingUtility; |
31 | 32 | import org.apache.hadoop.hbase.chaos.policies.Policy; |
32 | 33 | import org.apache.hadoop.hbase.util.Pair; |
| 34 | +import org.slf4j.Logger; |
| 35 | +import org.slf4j.LoggerFactory; |
33 | 36 |
|
34 | 37 | import org.apache.hbase.thirdparty.com.google.common.util.concurrent.ThreadFactoryBuilder; |
35 | 38 |
|
36 | 39 | /** |
37 | 40 | * Chaos monkey that given multiple policies will run actions against the cluster. |
38 | 41 | */ |
39 | 42 | public class PolicyBasedChaosMonkey extends ChaosMonkey { |
| 43 | + private static final Logger LOG = LoggerFactory.getLogger(PolicyBasedChaosMonkey.class); |
40 | 44 |
|
41 | 45 | private static final long ONE_SEC = 1000; |
42 | 46 | private static final long ONE_MIN = 60 * ONE_SEC; |
@@ -116,13 +120,31 @@ public static <T> T selectWeightedRandomItem(List<Pair<T, Integer>> items) { |
116 | 120 |
|
117 | 121 | /** Selects and returns ceil(ratio * items.length) random items from the given array */ |
118 | 122 | public static <T> List<T> selectRandomItems(T[] items, float ratio) { |
119 | | - int selectedNumber = (int) Math.ceil(items.length * ratio); |
| 123 | + /* |
| 124 | + * N.b. `ratio` values are not validated. Be aware of excessive values and floating point |
| 125 | + * arithmetic rounding. Guard against negative input to Random#next() and exceeding boundaries |
| 126 | + * in call to List#subList. |
| 127 | + */ |
120 | 128 |
|
121 | | - List<T> originalItems = Arrays.asList(items); |
122 | | - Collections.shuffle(originalItems); |
| 129 | + // clamp ratio to [0.0,1.0] |
| 130 | + ratio = Math.max(Math.min(ratio, 1.0f), 0.0f); |
123 | 131 |
|
124 | | - int startIndex = ThreadLocalRandom.current().nextInt(items.length - selectedNumber); |
125 | | - return originalItems.subList(startIndex, startIndex + selectedNumber); |
| 132 | + final int selectedNumber = (int) Math.ceil(items.length * ratio); |
| 133 | + |
| 134 | + // shuffle a copy of the input, not the input. |
| 135 | + final List<T> shuffledItems = new ArrayList<>(items.length); |
| 136 | + shuffledItems.addAll(Arrays.asList(items)); |
| 137 | + Collections.shuffle(shuffledItems); |
| 138 | + |
| 139 | + if (selectedNumber >= items.length) { |
| 140 | + return shuffledItems; |
| 141 | + } |
| 142 | + |
| 143 | + // apply basic sanity check on sublist selection range. |
| 144 | + final int startIndex = |
| 145 | + Math.max(0, ThreadLocalRandom.current().nextInt(items.length - selectedNumber)); |
| 146 | + final int endIndex = Math.min(items.length, startIndex + selectedNumber); |
| 147 | + return shuffledItems.subList(startIndex, endIndex); |
126 | 148 | } |
127 | 149 |
|
128 | 150 | @Override |
@@ -151,7 +173,10 @@ public boolean isStopped() { |
151 | 173 |
|
152 | 174 | @Override |
153 | 175 | public void waitForStop() throws InterruptedException { |
154 | | - monkeyThreadPool.awaitTermination(1, TimeUnit.MINUTES); |
| 176 | + if (!monkeyThreadPool.awaitTermination(1, TimeUnit.MINUTES)) { |
| 177 | + LOG.warn("Some pool threads failed to terminate. Forcing. {}", monkeyThreadPool); |
| 178 | + monkeyThreadPool.shutdownNow(); |
| 179 | + } |
155 | 180 | } |
156 | 181 |
|
157 | 182 | @Override |
|
0 commit comments