package netflix.ocelli.retry;
import netflix.ocelli.LoadBalancer;
import netflix.ocelli.functions.Metrics;
import netflix.ocelli.loadbalancer.RoundRobinLoadBalancer;
import netflix.ocelli.retrys.BackupRequestRetryStrategy;
import netflix.ocelli.util.RxUtil;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import rx.Observable;
import rx.functions.Action1;
import rx.functions.Func1;
import rx.schedulers.TestScheduler;
import rx.subjects.BehaviorSubject;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
@Ignore
// Test needs to be updated to tracks clients not as Observables since they cannot be
// effectively compared when cached internally
public class BackupRequestStrategyTest {
private final Func1<Observable<Integer>, Observable<String>> Operation = new Func1<Observable<Integer>, Observable<String>>() {
@Override
public Observable<String> call(Observable<Integer> t1) {
return t1.map(new Func1<Integer, String>() {
@Override
public String call(Integer t1) {
return t1.toString();
}
});
}
};
private final TestScheduler scheduler = new TestScheduler();
private final BackupRequestRetryStrategy<String> strategy = BackupRequestRetryStrategy.<String>builder()
.withScheduler(scheduler)
.withTimeoutMetric(Metrics.memoize(1000L))
.build();
@Test
public void firstSucceedsFast() {
BehaviorSubject<List<Observable<Integer>>> subject = BehaviorSubject.create(Arrays.asList(
Observable.just(1),
Observable.just(2),
Observable.just(3)));
LoadBalancer<Observable<Integer>> lb = LoadBalancer.fromSnapshotSource(subject).build(RoundRobinLoadBalancer.<Observable<Integer>>create(-1));
final AtomicInteger lbCounter = new AtomicInteger();
final AtomicReference<String> result = new AtomicReference<String>();
lb .toObservable()
.doOnNext(RxUtil.increment(lbCounter))
.flatMap(Operation)
.compose(strategy)
.doOnNext(RxUtil.set(result))
.subscribe();
scheduler.advanceTimeBy(2, TimeUnit.SECONDS);
scheduler.advanceTimeBy(1, TimeUnit.SECONDS);
Assert.assertEquals("1", result.get());
Assert.assertEquals(1, lbCounter.get());
}
@Test
public void firstNeverSecondSucceeds() {
BehaviorSubject<List<Observable<Integer>>> subject = BehaviorSubject.create(Arrays.asList(
Observable.<Integer>never(),
Observable.just(2),
Observable.just(3)));
LoadBalancer<Observable<Integer>> lb = LoadBalancer.fromSnapshotSource(subject).build(RoundRobinLoadBalancer.<Observable<Integer>>create(-1));
final AtomicInteger lbCounter = new AtomicInteger();
final AtomicReference<String> result = new AtomicReference<String>();
lb .toObservable()
.doOnNext(RxUtil.increment(lbCounter))
.flatMap(Operation)
.compose(strategy)
.doOnNext(RxUtil.set(result))
.subscribe();
scheduler.advanceTimeBy(2, TimeUnit.SECONDS);
scheduler.advanceTimeBy(1, TimeUnit.SECONDS);
Assert.assertEquals("2", result.get());
Assert.assertEquals(2, lbCounter.get());
}
@Test
public void firstFailsSecondSucceeds() {
BehaviorSubject<List<Observable<Integer>>> subject = BehaviorSubject.create(Arrays.asList(
Observable.<Integer>error(new Exception("1")),
Observable.just(2),
Observable.just(3)));
LoadBalancer<Observable<Integer>> lb = LoadBalancer.fromSnapshotSource(subject).build(RoundRobinLoadBalancer.<Observable<Integer>>create(-1));
final AtomicInteger lbCounter = new AtomicInteger();
final AtomicReference<String> result = new AtomicReference<String>();
lb .toObservable()
.doOnNext(RxUtil.increment(lbCounter))
.flatMap(Operation)
.compose(strategy)
.doOnNext(RxUtil.set(result))
.subscribe();
scheduler.advanceTimeBy(2, TimeUnit.SECONDS);
scheduler.advanceTimeBy(1, TimeUnit.SECONDS);
Assert.assertEquals("2", result.get());
Assert.assertEquals(2, lbCounter.get());
}
@Test
public void bothDelayed() {
BehaviorSubject<List<Observable<Integer>>> subject = BehaviorSubject.create(Arrays.asList(
Observable.just(1).delaySubscription(2, TimeUnit.SECONDS, scheduler),
Observable.just(2).delaySubscription(2, TimeUnit.SECONDS, scheduler),
Observable.just(3)));
LoadBalancer<Observable<Integer>> lb = LoadBalancer.fromSnapshotSource(subject).build(RoundRobinLoadBalancer.<Observable<Integer>>create(-1));
final AtomicInteger lbCounter = new AtomicInteger();
final AtomicReference<String> result = new AtomicReference<String>();
lb .toObservable()
.doOnNext(RxUtil.increment(lbCounter))
.flatMap(Operation)
.compose(strategy)
.doOnNext(RxUtil.set(result))
.subscribe();
scheduler.advanceTimeBy(1, TimeUnit.SECONDS);
scheduler.advanceTimeBy(1, TimeUnit.SECONDS);
scheduler.advanceTimeBy(3, TimeUnit.SECONDS);
Assert.assertEquals(2, lbCounter.get());
Assert.assertEquals("1", result.get());
}
@Test
public void bothFailed() {
BehaviorSubject<List<Observable<Integer>>> subject = BehaviorSubject.create(Arrays.asList(
Observable.<Integer>error(new Exception("1")),
Observable.<Integer>error(new Exception("2")),
Observable.just(3)));
LoadBalancer<Observable<Integer>> lb = LoadBalancer.fromSnapshotSource(subject).build(RoundRobinLoadBalancer.<Observable<Integer>>create(-1));
final AtomicInteger lbCounter = new AtomicInteger();
final AtomicReference<String> result = new AtomicReference<String>();
final AtomicBoolean failed = new AtomicBoolean(false);
lb .toObservable()
.doOnNext(RxUtil.increment(lbCounter))
.flatMap(Operation)
.compose(strategy)
.doOnNext(RxUtil.set(result))
.doOnError(new Action1<Throwable>() {
@Override
public void call(Throwable t1) {
failed.set(true);
}
})
.subscribe();
scheduler.advanceTimeBy(1, TimeUnit.SECONDS);
scheduler.advanceTimeBy(1, TimeUnit.SECONDS);
scheduler.advanceTimeBy(3, TimeUnit.SECONDS);
Assert.assertEquals(2, lbCounter.get());
Assert.assertTrue(failed.get());
}
@Test
public void firstSucceedsSecondFailsAfterBackupStarted() {
BehaviorSubject<List<Observable<Integer>>> subject = BehaviorSubject.create(Arrays.asList(
Observable.just(1).delaySubscription(2, TimeUnit.SECONDS, scheduler),
Observable.<Integer>error(new Exception("2")),
Observable.just(3)));
LoadBalancer<Observable<Integer>> lb = LoadBalancer.fromSnapshotSource(subject).build(RoundRobinLoadBalancer.<Observable<Integer>>create(-1));
final AtomicInteger lbCounter = new AtomicInteger();
final AtomicReference<String> result = new AtomicReference<String>();
lb .toObservable()
.doOnNext(RxUtil.increment(lbCounter))
.flatMap(Operation)
.compose(strategy)
.doOnNext(RxUtil.set(result))
.subscribe();
scheduler.advanceTimeBy(1, TimeUnit.SECONDS);
scheduler.advanceTimeBy(1, TimeUnit.SECONDS);
scheduler.advanceTimeBy(1, TimeUnit.SECONDS);
Assert.assertEquals("1", result.get());
Assert.assertEquals(2, lbCounter.get());
}
}