1818import static org .junit .Assert .assertEquals ;
1919import static org .junit .Assert .fail ;
2020
21+ import java .util .Random ;
22+ import java .util .concurrent .CountDownLatch ;
23+ import java .util .concurrent .ExecutorService ;
24+ import java .util .concurrent .Executors ;
25+ import java .util .concurrent .Future ;
26+ import java .util .concurrent .TimeUnit ;
2127import java .util .concurrent .atomic .AtomicInteger ;
2228
29+ import org .junit .AfterClass ;
2330import org .junit .Assert ;
31+ import org .junit .BeforeClass ;
2432import org .junit .Test ;
2533
2634import com .netflix .hystrix .strategy .properties .HystrixProperty ;
@@ -33,6 +41,23 @@ public class HystrixRollingPercentileTest {
3341 private static final HystrixProperty <Integer > numberOfBuckets = HystrixProperty .Factory .asProperty (12 ); // 12 buckets at 5000ms each
3442 private static final HystrixProperty <Boolean > enabled = HystrixProperty .Factory .asProperty (true );
3543
44+ private static ExecutorService threadPool ;
45+
46+ @ BeforeClass
47+ public static void setUp () {
48+ threadPool = Executors .newFixedThreadPool (10 );
49+ }
50+
51+ @ AfterClass
52+ public static void tearDown () {
53+ threadPool .shutdown ();
54+ try {
55+ threadPool .awaitTermination (10 , TimeUnit .SECONDS );
56+ } catch (InterruptedException ie ) {
57+ System .out .println ("Thread pool never terminated in HystrixRollingPercentileTest" );
58+ }
59+ }
60+
3661 @ Test
3762 public void testRolling () {
3863 MockedTime time = new MockedTime ();
@@ -78,7 +103,7 @@ public void testRolling() {
78103 time .increment (6000 );
79104
80105 // the rolling version should have the same data as creating a snapshot like this
81- PercentileSnapshot ps = new PercentileSnapshot (1000 , 1000 , 1000 , 2000 , 1000 , 500 , 200 , 200 , 1600 , 200 , 1600 , 1600 );
106+ PercentileSnapshot ps = new PercentileSnapshot (System . currentTimeMillis (), 1000 , 1000 , 1000 , 2000 , 1000 , 500 , 200 , 200 , 1600 , 200 , 1600 , 1600 );
82107
83108 assertEquals (ps .getPercentile (0.15 ), p .getPercentile (0.15 ));
84109 assertEquals (ps .getPercentile (0.50 ), p .getPercentile (0.50 ));
@@ -201,36 +226,36 @@ public void testSampleDataOverTime2() {
201226 }
202227
203228 public PercentileSnapshot getPercentileForValues (int ... values ) {
204- return new PercentileSnapshot (values );
229+ return new PercentileSnapshot (System . currentTimeMillis (), values );
205230 }
206231
207232 @ Test
208233 public void testPercentileAlgorithm_Median1 () {
209- PercentileSnapshot list = new PercentileSnapshot (100 , 100 , 100 , 100 , 200 , 200 , 200 , 300 , 300 , 300 , 300 );
234+ PercentileSnapshot list = new PercentileSnapshot (System . currentTimeMillis (), 100 , 100 , 100 , 100 , 200 , 200 , 200 , 300 , 300 , 300 , 300 );
210235 Assert .assertEquals (200 , list .getPercentile (50 ));
211236 }
212237
213238 @ Test
214239 public void testPercentileAlgorithm_Median2 () {
215- PercentileSnapshot list = new PercentileSnapshot (100 , 100 , 100 , 100 , 100 , 100 , 100 , 100 , 100 , 100 , 500 );
240+ PercentileSnapshot list = new PercentileSnapshot (System . currentTimeMillis (), 100 , 100 , 100 , 100 , 100 , 100 , 100 , 100 , 100 , 100 , 500 );
216241 Assert .assertEquals (100 , list .getPercentile (50 ));
217242 }
218243
219244 @ Test
220245 public void testPercentileAlgorithm_Median3 () {
221- PercentileSnapshot list = new PercentileSnapshot (50 , 75 , 100 , 125 , 160 , 170 , 180 , 200 , 210 , 300 , 500 );
246+ PercentileSnapshot list = new PercentileSnapshot (System . currentTimeMillis (), 50 , 75 , 100 , 125 , 160 , 170 , 180 , 200 , 210 , 300 , 500 );
222247 Assert .assertEquals (170 , list .getPercentile (50 ));
223248 }
224249
225250 @ Test
226251 public void testPercentileAlgorithm_Median4 () {
227- PercentileSnapshot list = new PercentileSnapshot (300 , 75 , 125 , 500 , 100 , 160 , 180 , 200 , 210 , 50 , 170 );
252+ PercentileSnapshot list = new PercentileSnapshot (System . currentTimeMillis (), 300 , 75 , 125 , 500 , 100 , 160 , 180 , 200 , 210 , 50 , 170 );
228253 Assert .assertEquals (170 , list .getPercentile (50 ));
229254 }
230255
231256 @ Test
232257 public void testPercentileAlgorithm_Extremes () {
233- PercentileSnapshot p = new PercentileSnapshot (2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 800 , 768 , 657 , 700 , 867 );
258+ PercentileSnapshot p = new PercentileSnapshot (System . currentTimeMillis (), 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 800 , 768 , 657 , 700 , 867 );
234259
235260 System .out .println ("0.01: " + p .getPercentile (0.01 ));
236261 System .out .println ("10th: " + p .getPercentile (10 ));
@@ -317,6 +342,65 @@ public void increment(int millis) {
317342
318343 }
319344
345+ @ Test
346+ public void testThreadSafety () {
347+ final MockedTime time = new MockedTime ();
348+ final HystrixRollingPercentile p = new HystrixRollingPercentile (time , HystrixProperty .Factory .asProperty (100 ), HystrixProperty .Factory .asProperty (25 ), HystrixProperty .Factory .asProperty (true ));
349+
350+ final int NUM_THREADS = 1000 ;
351+ final int NUM_ITERATIONS = 1000000 ;
352+
353+ final CountDownLatch latch = new CountDownLatch (NUM_THREADS );
354+
355+ final AtomicInteger aggregateMetrics = new AtomicInteger (); //same as a blackhole
356+
357+ final Random r = new Random ();
358+
359+ Future <?> metricsPoller = threadPool .submit (new Runnable () {
360+ @ Override
361+ public void run () {
362+ while (!Thread .currentThread ().isInterrupted ()) {
363+ aggregateMetrics .addAndGet (p .getMean () + p .getPercentile (10 ) + p .getPercentile (50 ) + p .getPercentile (90 ));
364+ //System.out.println("AGGREGATE : " + p.getPercentile(10) + " : " + p.getPercentile(50) + " : " + p.getPercentile(90));
365+ }
366+ }
367+ });
368+
369+ for (int i = 0 ; i < NUM_THREADS ; i ++) {
370+ final int threadId = i ;
371+ threadPool .submit (new Runnable () {
372+ @ Override
373+ public void run () {
374+ for (int j = 1 ; j < NUM_ITERATIONS / NUM_THREADS + 1 ; j ++) {
375+ int nextInt = r .nextInt (100 );
376+ p .addValue (nextInt );
377+ if (threadId == 0 ) {
378+ time .increment (1 );
379+ }
380+ }
381+ latch .countDown ();
382+ }
383+ });
384+ }
385+
386+ try {
387+ latch .await (100 , TimeUnit .SECONDS );
388+ metricsPoller .cancel (true );
389+ } catch (InterruptedException ex ) {
390+ fail ("Timeout on all threads writing percentiles" );
391+ }
392+
393+ aggregateMetrics .addAndGet (p .getMean () + p .getPercentile (10 ) + p .getPercentile (50 ) + p .getPercentile (90 ));
394+ System .out .println (p .getMean () + " : " + p .getPercentile (50 ) + " : " + p .getPercentile (75 ) + " : " + p .getPercentile (90 ) + " : " + p .getPercentile (95 ) + " : " + p .getPercentile (99 ));
395+ }
396+
397+ @ Test
398+ public void testThreadSafetyMulti () {
399+ for (int i = 0 ; i < 100 ; i ++) {
400+ testThreadSafety ();
401+ }
402+ }
403+
320404 /* sub-class to avoid 65k limit of a single class */
321405 private static class SampleDataHolder1 {
322406 /*
0 commit comments