All Downloads are FREE. Search and download functionalities are using the official Maven repository.

netflix.ocelli.FailureDetectingInstance Maven / Gradle / Ivy

There is a newer version: 0.1.0-rc.2
Show newest version
package netflix.ocelli;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

import rx.Observable;
import rx.Subscriber;
import rx.functions.Action0;
import rx.functions.Func1;
import rx.functions.Func2;

/**
 * Instance whose up state is dictated by a connector and failure detector.
 * 
 * @author elandau
 *
 * @param 
 */
public class FailureDetectingInstance extends Instance {
    static class State {
        private AtomicBoolean connected = new AtomicBoolean(true);
        private AtomicInteger failureCounter = new AtomicInteger(-1);
        
        static Func2 Accum = new Func2() {
            @Override
            public State call(State current, Throwable error) {
                return current;
            }
        };
    }
    
    public FailureDetectingInstance(
            final T                          value, 
            final Func1> connector,
            final Observable      errorStream, 
            final Func1       backoffStrategy
            ) {
        super(value, new OnSubscribe() {
            
            Observable lifecycle = Observable
                    // Placeholder to trigger for connect attempt
                    .just(new Throwable("Not connected"))
                    .concatWith(errorStream)
                    .scan(new State(), State.Accum)
                    // Skip the first element since scan() duplicates the first State
                    .skip(1)
                    // Discard errors while trying to connect
                    .filter(new Func1() {
                        @Override
                        public Boolean call(State state) {
                            return state.connected.compareAndSet(true, false);
                        }
                    })
                    .switchMap(new Func1>() {
                        @Override
                        public Observable call(final State state) {
                            return Observable
                                // Carry the 'false' to the subscribers
                                .just(false)
                                // Concat with connection logic
                                .concatWith(Observable
                                        .just(state)
                                        // Connect with backoff if necessary
                                        .concatMap(new Func1>() {
                                            @Override
                                            public Observable call(State t1) {
                                                int counter = state.failureCounter.incrementAndGet();
                                                Observable connect = connector.call(value);
                                                if (counter > 0) {
                                                    connect = connect.delaySubscription(backoffStrategy.call(counter), TimeUnit.MILLISECONDS);
                                                }
                                                return connect;
                                            }
                                        })
                                        // Retry on all connect errors.  Backoff counter will increase for each connect attempt.
                                        .retry()
                                        // Reset state to connected
                                        .doOnCompleted(new Action0() {
                                            @Override
                                            public void call() {
                                                if (state.connected.compareAndSet(false, true)) {
                                                    state.failureCounter.set(0);
                                                }
                                            }
                                        })
                                        // Emit 'true' to indicate instance is up
                                        .cast(Boolean.class)
                                        .defaultIfEmpty(true));
                        }
                    })
                    .replay(1)
                    .refCount();
        
            @Override
            public void call(Subscriber s) {
                lifecycle.subscribe(s);
            }
        });
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy