Package com.fasterxml.clustermate.client.cluster

Source Code of com.fasterxml.clustermate.client.cluster.ClusterViewByClientImpl$NodePriorityComparator

package com.fasterxml.clustermate.client.cluster;

import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicReferenceArray;

import com.fasterxml.clustermate.api.EntryKeyConverter;
import com.fasterxml.clustermate.api.KeyHash;
import com.fasterxml.clustermate.api.KeySpace;
import com.fasterxml.clustermate.api.NodeState;
import com.fasterxml.clustermate.api.RequestPath;
import com.fasterxml.clustermate.api.RequestPathBuilder;
import com.fasterxml.clustermate.client.ClusterViewByClient;
import com.fasterxml.clustermate.client.NetworkClient;
import com.fasterxml.clustermate.client.NodesForKey;
import com.fasterxml.clustermate.client.cluster.ClusterServerNodeImpl;
import com.fasterxml.clustermate.client.impl.StoreClientConfig;
import com.fasterxml.storemate.shared.EntryKey;
import com.fasterxml.storemate.shared.IpAndPort;

/**
* Class that encapsulates view of the cluster, as whole, from
* client perspective.
*/
public class ClusterViewByClientImpl<K extends EntryKey>
    extends ClusterViewByClient<K>
{
  /**
   * Reference to the underlying network client, so that we can
   * construct paths for requests.
   */
  private final NetworkClient<K> _client;

  private final KeySpace _keyspace;
   
  private final EntryKeyConverter<K> _keyConverter;
   
  private final Map<IpAndPort, ClusterServerNodeImpl> _nodes = new LinkedHashMap<IpAndPort, ClusterServerNodeImpl>();

    /**
     * Since we will need to iterate over server node
     */
    private AtomicReference<ClusterServerNodeImpl[]> _states = new AtomicReference<ClusterServerNodeImpl[]>(
            new ClusterServerNodeImpl[0]);

    /**
     * Monotonically increasing counter we use for lazily constructing
     * and invalidating routing information, mapping from key hashes
     * to {@link NodesForKey} objects.
     */
    private final AtomicInteger _version = new AtomicInteger(1);

    private final AtomicReferenceArray<NodesForKey> _routing;

    private final EntryAccessors<K> _entryAccessors;

    /*
    /**********************************************************************
    /* Construction
    /**********************************************************************
     */
   
    /**
     * Path segments for the root path
     */
    private final String[] _rootPathSegments;

    public ClusterViewByClientImpl(StoreClientConfig<K,?> storeConfig,
            NetworkClient<K> client, KeySpace keyspace)
    {
        _keyspace = keyspace;
        _routing = new AtomicReferenceArray<NodesForKey>(keyspace.getLength());
        if (client == null) {
            _client = null;
            _keyConverter = null;
            _entryAccessors = null;
        } else {
            _client = client;
            _keyConverter = client.getKeyConverter();
            _entryAccessors = client.getEntryAccessors();
        }
        if (storeConfig == null) {
            _rootPathSegments = new String[0];
        } else {
            _rootPathSegments = storeConfig.getBasePath();
        }
    }
   
    public static <K extends EntryKey> ClusterViewByClientImpl<K> forTesting(KeySpace keyspace)
    {
        return new ClusterViewByClientImpl<K>(null, null, keyspace);
    }

    /*
    protected String[] _splitPath(String base)
    {
        base = base.trim();
        if (base.startsWith("/")) {
            base = base.substring(1);
        }
        if (base.endsWith("/")) {
            base = base.substring(0, base.length()-1);
        }
        if (base.length() == 0) {
            return new String[0];
        }
        return base.split("/");
    }
    */
   
    /*
    /**********************************************************************
    /* Accessors
    /**********************************************************************
     */

    @Override
    public int getServerCount() {
        return _nodes.size();
    }

    @Override
    public boolean isFullyAvailable() {
        return getCoverage() == _keyspace.getLength();
    }

    @Override
    public int getCoverage() {
        return _getCoverage(_states());
    }

    // separate method for testing:
    protected int _getCoverage(ClusterServerNodeImpl[] states)
    {
        BitSet slices = new BitSet(_keyspace.getLength());
        for (ClusterServerNodeImpl state : states) {
            state.getTotalRange().fill(slices);
        }
        return slices.cardinality();
    }

    @Override
    public NodesForKey getNodesFor(K key)
    {
        int fullHash = _keyConverter.routingHashFor(key);
        KeyHash hash = new KeyHash(fullHash, _keyspace.getLength());
        int currVersion = _version.get();
        int modulo = hash.getModuloHash();
        NodesForKey nodes = _routing.get(modulo);
        // fast (and common) case: pre-calculated, valid info exists:
        if (nodes != null && nodes.version() == currVersion) {
            return nodes;
        }
        NodesForKey newNodes = _calculateNodes(currVersion, hash);
        _routing.compareAndSet(modulo, nodes, newNodes);
        return newNodes;
    }
   
    /*
    /**********************************************************************
    /* Updating state
    /**********************************************************************
     */
   
    /**
     * Method called to add information directly related to node that served
     * the request.
     */
    public synchronized void updateDirectState(IpAndPort byNode, NodeState stateInfo,
            long requestTime, long responseTime,
            long clusterInfoVersion)
    {
        ClusterServerNodeImpl localState = _nodes.get(byNode);
        if (localState == null) { // new info
            localState = new ClusterServerNodeImpl(_rootPathFor(byNode),
                byNode, stateInfo.getRangeActive(), stateInfo.getRangePassive(),
                    _entryAccessors);
            _addNode(byNode, localState);
        }
        boolean needInvalidate = localState.updateRanges(stateInfo.getRangeActive(),
                stateInfo.getRangePassive());
        if (localState.updateDisabled(stateInfo.isDisabled())) {
            needInvalidate = true;
        }
        if (needInvalidate) {
            invalidateRouting();
        }
        localState.setLastRequestSent(requestTime);
        localState.setLastResponseReceived(responseTime);
        localState.setLastNodeUpdateFetched(stateInfo.getLastUpdated());
        localState.setLastClusterUpdateFetched(clusterInfoVersion);
    }
   
    /**
     * Method called to add information obtained indirectly; i.e. "gossip".
     */
    public synchronized void updateIndirectState(IpAndPort byNode, NodeState stateInfo)
    {
        // First: ensure references are properly resolved (eliminate "localhost" if need be):
        IpAndPort ip = stateInfo.getAddress();
        if (ip.isLocalReference()) {
            ip = byNode.withPort(ip.getPort());
        }
        final long nodeInfoTimestamp = stateInfo.getLastUpdated();
        // otherwise pretty simple:
        ClusterServerNodeImpl state = _nodes.get(ip);
        if (state == null) { // new info
            state = new ClusterServerNodeImpl(_rootPathFor(ip),
                ip, stateInfo.getRangeActive(), stateInfo.getRangePassive(),
                    _entryAccessors);
            _addNode(ip, state);
        } else {
            // quick check to ensure info we get is newer: if not, skip
            if (nodeInfoTimestamp <= state.getLastNodeUpdateFetched()) {
                return;
            }
        }
        state.setLastNodeUpdateFetched(nodeInfoTimestamp);
        boolean needInvalidate = state.updateRanges(stateInfo.getRangeActive(),
                stateInfo.getRangePassive());
        if (state.updateDisabled(stateInfo.isDisabled())) {
            needInvalidate = true;
        }
        if (needInvalidate) {
            invalidateRouting();
        }
    }
   
    /*
    /**********************************************************************
    /* Internal methods
    /**********************************************************************
     */

    protected RequestPath _rootPathFor(IpAndPort serverAddress)
    {
        RequestPathBuilder builder = _client.pathBuilder(serverAddress);
        for (String component : _rootPathSegments) {
            builder = builder.addPathSegment(component);
        }
        return builder.build();
    }
   
    private void _addNode(IpAndPort key, ClusterServerNodeImpl state)
    {
        _nodes.put(key, state);
        _states.set(_nodes.values().toArray(new ClusterServerNodeImpl[_nodes.size()]));
    }
   
    private ClusterServerNodeImpl[] _states() {
        return _states.get();
    }

    /**
     * Method called when server node state information changes in a way that
     * may affect routing of requests.
     */
    private void invalidateRouting()
    {
        _version.addAndGet(1);
    }

    /**
     * Helper method that actually calculates routing information for specific
     * part of key space.
     */
    protected NodesForKey _calculateNodes(int version, KeyHash keyHash) {
        return _calculateNodes(version, keyHash, _states());
    }

    // separate method for testing
    protected NodesForKey _calculateNodes(int version, KeyHash keyHash,
            ClusterServerNodeImpl[] allNodes)
    {
        final int allCount = allNodes.length;
        // First: simply collect all applicable nodes:
        ArrayList<ClusterServerNodeImpl> appl = new ArrayList<ClusterServerNodeImpl>();
        for (int i = 0; i < allCount; ++i) {
            ClusterServerNodeImpl state = allNodes[i];
            if (state.getTotalRange().contains(keyHash)) {
                appl.add(state);
            }
        }
        return _sortNodes(version, keyHash, appl);
    }

    protected NodesForKey _sortNodes(int version, KeyHash keyHash,
            Collection<ClusterServerNodeImpl> appl)
    {
        // edge case: no matching
        if (appl.isEmpty()) {
            return NodesForKey.empty(version);
        }
        // otherwise need to sort
        ClusterServerNodeImpl[] matching = appl.toArray(new ClusterServerNodeImpl[appl.size()]);
        Arrays.sort(matching, 0, appl.size(), new NodePriorityComparator(keyHash));
        return new NodesForKey(version, matching);
    }
       
    /*
    /**********************************************************************
    /* Helper classes
    /**********************************************************************
     */

    /**
     * Comparator that orders server in decreasing priority, that is, starts with
     * the closest enabled match, ending with disabled entries.
     */
    private final static class NodePriorityComparator implements Comparator<ClusterServerNodeImpl>
    {
        private final KeyHash _keyHash;

        public NodePriorityComparator(KeyHash keyHash) {
            _keyHash = keyHash;
        }
       
        @Override
        public int compare(ClusterServerNodeImpl node1, ClusterServerNodeImpl node2)
        {
            return node1.calculateSortingDistance(_keyHash) - node2.calculateSortingDistance(_keyHash);
        }
    }
}
TOP

Related Classes of com.fasterxml.clustermate.client.cluster.ClusterViewByClientImpl$NodePriorityComparator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.