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

netflix.ocelli.topologies.RingTopology Maven / Gradle / Ivy

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

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import netflix.ocelli.Instance;
import netflix.ocelli.MutableInstance;
import rx.Observable;
import rx.Observable.Transformer;
import rx.functions.Action0;
import rx.functions.Func0;
import rx.functions.Func1;

/**
 * The ring topology uses consistent hashing to arrange all hosts in a predictable ring 
 * topology such that each client instance will be located in a uniformly distributed
 * fashion around the ring.  The client will then target the next N hosts after it's location.
 * 
 * This type of topology ensures that each client instance communicates with a subset of 
 * hosts in such a manner that the overall load shall be evenly distributed.
 * 
 * @author elandau
 *
 * @param 
 * @param 
 */

public class RingTopology, T> implements Transformer, Instance> {

    private final Instance localMember;
    private final Func1 countFunc;
    private final Comparator> comparator;
    private final Func1 keyFunc;
    
    public RingTopology(final K localKey, final Func1 keyFunc, Func1 countFunc) {
        this.localMember = MutableInstance.from((T)null);
        this.countFunc = countFunc;
        this.keyFunc = keyFunc;
        this.comparator = new Comparator>() {
            @Override
            public int compare(Instance o1, Instance o2) {
                K k1 = o1.getValue() == null ? localKey : keyFunc.call(o1.getValue());
                K k2 = o2.getValue() == null ? localKey : keyFunc.call(o2.getValue());
                return k1.compareTo(k2);
            }
        };
    }
    
    @Override
    public Observable> call(Observable> o) {
        return o.flatMap(new Func1, Observable>>() {
            final List> ring = new ArrayList>();
            Map> members = new HashMap>();
          
            {
                ring.add(localMember);
            }

            @Override
            public Observable> call(final Instance member) {
                ring.add(member);
                return update().concatWith(member.flatMap(
                        new Func1>>() {
                            @Override
                            public Observable> call(Boolean t) {
                                return Observable.empty();
                            }
                        },
                        new Func1>>() {
                            @Override
                            public Observable> call(Throwable t1) {
                                ring.remove(t1);
                                return update();
                            }
                        },
                        new Func0>>() {
                            @Override
                            public Observable> call() {
                                ring.remove(member);
                                return update();
                            }
                        }));
            }
            
            private Observable> update() {
                Collections.sort(ring, comparator);

                // -1 to account for the current instance
                int count = Math.min(ring.size() - 1, countFunc.call(ring.size() - 1));
                List> toAdd = new ArrayList>();
                List> toRemove = new ArrayList>();
                
                int pos = Collections.binarySearch(ring, localMember, comparator) + 1;
                Map> newMembers = new HashMap>();
                for (int i = 0; i < count; i++) {
                    Instance member = ring.get((pos + i) % ring.size());
                    MutableInstance existing = members.remove(keyFunc.call(member.getValue()));
                    if (existing == null) {
                        MutableInstance newMember = MutableInstance.from(member.getValue());
                        newMembers.put(keyFunc.call(member.getValue()), newMember);
                        toAdd.add(newMember);
                    } 
                    else {
                        newMembers.put(keyFunc.call(member.getValue()), existing);
                    }
                }

                for (MutableInstance member : members.values()) {
                    toRemove.add(member);
                }

                members = newMembers;
                
                return response(toAdd, toRemove);
            }
            
            private Observable> response(List> toAdd, final List> toRemove) {
                return Observable.from(toAdd).doOnCompleted(new Action0() {
                    @Override
                    public void call() {
                        for (MutableInstance member : toRemove) {
                            member.close();
                        }
                    }
                });
            }
        });
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy