/**
* Copyright 2016 Netflix, Inc.
*
* Licensed 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 com.netflix.hystrix.metric.sample;
import com.hystrix.junit.HystrixRequestContextRule;
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.hystrix.HystrixCommandKey;
import com.netflix.hystrix.HystrixEventType;
import com.netflix.hystrix.metric.CommandStreamTest;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import rx.Observable;
import rx.Subscriber;
import rx.Subscription;
import rx.functions.Action0;
import rx.functions.Func1;
import rx.functions.Func2;
import rx.schedulers.Schedulers;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class HystrixUtilizationStreamTest extends CommandStreamTest {
@Rule
public HystrixRequestContextRule ctx = new HystrixRequestContextRule();
HystrixUtilizationStream stream;
private final static HystrixCommandGroupKey groupKey = HystrixCommandGroupKey.Factory.asKey("Util");
private final static HystrixCommandKey commandKey = HystrixCommandKey.Factory.asKey("Command");
@Before
public void init() {
stream = HystrixUtilizationStream.getNonSingletonInstanceOnlyUsedInUnitTests(10);
}
@Test
public void testStreamHasData() throws Exception {
final AtomicBoolean commandShowsUp = new AtomicBoolean(false);
final AtomicBoolean threadPoolShowsUp = new AtomicBoolean(false);
final CountDownLatch latch = new CountDownLatch(1);
final int NUM = 10;
for (int i = 0; i < 2; i++) {
HystrixCommand<Integer> cmd = Command.from(groupKey, commandKey, HystrixEventType.SUCCESS, 50);
cmd.observe();
}
stream.observe().take(NUM).subscribe(
new Subscriber<HystrixUtilization>() {
@Override
public void onCompleted() {
System.out.println(System.currentTimeMillis() + " : " + Thread.currentThread().getName() + " OnCompleted");
latch.countDown();
}
@Override
public void onError(Throwable e) {
System.out.println(System.currentTimeMillis() + " : " + Thread.currentThread().getName() + " OnError : " + e);
latch.countDown();
}
@Override
public void onNext(HystrixUtilization utilization) {
System.out.println(System.currentTimeMillis() + " : " + Thread.currentThread().getName() + " : Received data with : " + utilization.getCommandUtilizationMap().size() + " commands");
if (utilization.getCommandUtilizationMap().containsKey(commandKey)) {
commandShowsUp.set(true);
}
if (!utilization.getThreadPoolUtilizationMap().isEmpty()) {
threadPoolShowsUp.set(true);
}
}
});
assertTrue(latch.await(10000, TimeUnit.MILLISECONDS));
assertTrue(commandShowsUp.get());
assertTrue(threadPoolShowsUp.get());
}
@Test
public void testTwoSubscribersOneUnsubscribes() throws Exception {
final CountDownLatch latch1 = new CountDownLatch(1);
final CountDownLatch latch2 = new CountDownLatch(1);
final AtomicInteger payloads1 = new AtomicInteger(0);
final AtomicInteger payloads2 = new AtomicInteger(0);
Subscription s1 = stream
.observe()
.take(100)
.doOnUnsubscribe(new Action0() {
@Override
public void call() {
latch1.countDown();
}
})
.subscribe(new Subscriber<HystrixUtilization>() {
@Override
public void onCompleted() {
System.out.println(System.currentTimeMillis() + " : " + Thread.currentThread().getName() + " : Dashboard 1 OnCompleted");
latch1.countDown();
}
@Override
public void onError(Throwable e) {
System.out.println(System.currentTimeMillis() + " : " + Thread.currentThread().getName() + " : Dashboard 1 OnError : " + e);
latch1.countDown();
}
@Override
public void onNext(HystrixUtilization utilization) {
System.out.println(System.currentTimeMillis() + " : " + Thread.currentThread().getName() + " : Dashboard 1 OnNext : " + utilization);
payloads1.incrementAndGet();
}
});
Subscription s2 = stream
.observe()
.take(100)
.doOnUnsubscribe(new Action0() {
@Override
public void call() {
latch2.countDown();
}
})
.subscribe(new Subscriber<HystrixUtilization>() {
@Override
public void onCompleted() {
System.out.println(System.currentTimeMillis() + " : " + Thread.currentThread().getName() + " : Dashboard 2 OnCompleted");
latch2.countDown();
}
@Override
public void onError(Throwable e) {
System.out.println(System.currentTimeMillis() + " : " + Thread.currentThread().getName() + " : Dashboard 2 OnError : " + e);
latch2.countDown();
}
@Override
public void onNext(HystrixUtilization utilization) {
System.out.println(System.currentTimeMillis() + " : " + Thread.currentThread().getName() + " : Dashboard 2 OnNext : " + utilization);
payloads2.incrementAndGet();
}
});
//execute 1 command, then unsubscribe from first stream. then execute the rest
for (int i = 0; i < 50; i++) {
HystrixCommand<Integer> cmd = Command.from(groupKey, commandKey, HystrixEventType.SUCCESS, 50);
cmd.execute();
if (i == 1) {
s1.unsubscribe();
}
}
assertTrue(latch1.await(10000, TimeUnit.MILLISECONDS));
assertTrue(latch2.await(10000, TimeUnit.MILLISECONDS));
System.out.println("s1 got : " + payloads1.get() + ", s2 got : " + payloads2.get());
assertTrue("s1 got data", payloads1.get() > 0);
assertTrue("s2 got data", payloads2.get() > 0);
assertTrue("s1 got less data than s2", payloads2.get() > payloads1.get());
}
@Test
public void testTwoSubscribersBothUnsubscribe() throws Exception {
final CountDownLatch latch1 = new CountDownLatch(1);
final CountDownLatch latch2 = new CountDownLatch(1);
final AtomicInteger payloads1 = new AtomicInteger(0);
final AtomicInteger payloads2 = new AtomicInteger(0);
Subscription s1 = stream
.observe()
.take(100)
.doOnUnsubscribe(new Action0() {
@Override
public void call() {
latch1.countDown();
}
})
.subscribe(new Subscriber<HystrixUtilization>() {
@Override
public void onCompleted() {
System.out.println(System.currentTimeMillis() + " : " + Thread.currentThread().getName() + " : Dashboard 1 OnCompleted");
latch1.countDown();
}
@Override
public void onError(Throwable e) {
System.out.println(System.currentTimeMillis() + " : " + Thread.currentThread().getName() + " : Dashboard 1 OnError : " + e);
latch1.countDown();
}
@Override
public void onNext(HystrixUtilization utilization) {
System.out.println(System.currentTimeMillis() + " : " + Thread.currentThread().getName() + " : Dashboard 1 OnNext : " + utilization);
payloads1.incrementAndGet();
}
});
Subscription s2 = stream
.observe()
.take(100)
.doOnUnsubscribe(new Action0() {
@Override
public void call() {
latch2.countDown();
}
})
.subscribe(new Subscriber<HystrixUtilization>() {
@Override
public void onCompleted() {
System.out.println(System.currentTimeMillis() + " : " + Thread.currentThread().getName() + " : Dashboard 2 OnCompleted");
latch2.countDown();
}
@Override
public void onError(Throwable e) {
System.out.println(System.currentTimeMillis() + " : " + Thread.currentThread().getName() + " : Dashboard 2 OnError : " + e);
latch2.countDown();
}
@Override
public void onNext(HystrixUtilization utilization) {
System.out.println(System.currentTimeMillis() + " : " + Thread.currentThread().getName() + " : Dashboard 2 OnNext : " + utilization);
payloads2.incrementAndGet();
}
});
//execute 2 commands, then unsubscribe from both streams, then execute the rest
for (int i = 0; i < 10; i++) {
HystrixCommand<Integer> cmd = Command.from(groupKey, commandKey, HystrixEventType.SUCCESS, 50);
cmd.execute();
if (i == 2) {
s1.unsubscribe();
s2.unsubscribe();
}
}
assertFalse(stream.isSourceCurrentlySubscribed()); //both subscriptions have been cancelled - source should be too
assertTrue(latch1.await(10000, TimeUnit.MILLISECONDS));
assertTrue(latch2.await(10000, TimeUnit.MILLISECONDS));
System.out.println("s1 got : " + payloads1.get() + ", s2 got : " + payloads2.get());
assertTrue("s1 got data", payloads1.get() > 0);
assertTrue("s2 got data", payloads2.get() > 0);
}
@Test
public void testTwoSubscribersOneSlowOneFast() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
final AtomicBoolean foundError = new AtomicBoolean(false);
Observable<HystrixUtilization> fast = stream
.observe()
.observeOn(Schedulers.newThread());
Observable<HystrixUtilization> slow = stream
.observe()
.observeOn(Schedulers.newThread())
.map(new Func1<HystrixUtilization, HystrixUtilization>() {
@Override
public HystrixUtilization call(HystrixUtilization util) {
try {
Thread.sleep(100);
return util;
} catch (InterruptedException ex) {
return util;
}
}
});
Observable<Boolean> checkZippedEqual = Observable.zip(fast, slow, new Func2<HystrixUtilization, HystrixUtilization, Boolean>() {
@Override
public Boolean call(HystrixUtilization payload, HystrixUtilization payload2) {
return payload == payload2;
}
});
Subscription s1 = checkZippedEqual
.take(10000)
.subscribe(new Subscriber<Boolean>() {
@Override
public void onCompleted() {
System.out.println(System.currentTimeMillis() + " : " + Thread.currentThread().getName() + " : OnCompleted");
latch.countDown();
}
@Override
public void onError(Throwable e) {
System.out.println(System.currentTimeMillis() + " : " + Thread.currentThread().getName() + " : OnError : " + e);
e.printStackTrace();
foundError.set(true);
latch.countDown();
}
@Override
public void onNext(Boolean b) {
//System.out.println(System.currentTimeMillis() + " : " + Thread.currentThread().getName() + " : OnNext : " + b);
}
});
for (int i = 0; i < 50; i++) {
HystrixCommand<Integer> cmd = Command.from(groupKey, commandKey, HystrixEventType.SUCCESS, 50);
cmd.execute();
}
latch.await(10000, TimeUnit.MILLISECONDS);
assertFalse(foundError.get());
}
}