com.fasterxml.clustermate.client.cluster.ClusterServerNodeImpl Maven / Gradle / Ivy
Show all versions of clustermate-client Show documentation
package com.fasterxml.clustermate.client.cluster;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import com.fasterxml.clustermate.api.EntryKey;
import com.fasterxml.clustermate.api.KeyHash;
import com.fasterxml.clustermate.api.KeyRange;
import com.fasterxml.clustermate.api.RequestPath;
import com.fasterxml.clustermate.api.RequestPathBuilder;
import com.fasterxml.clustermate.client.ClusterServerNode;
import com.fasterxml.clustermate.client.EntryAccessors;
import com.fasterxml.clustermate.client.NetworkClient;
import com.fasterxml.clustermate.client.call.ContentDeleter;
import com.fasterxml.clustermate.client.call.ContentGetter;
import com.fasterxml.clustermate.client.call.ContentHeader;
import com.fasterxml.clustermate.client.call.ContentPutter;
import com.fasterxml.clustermate.client.call.EntryLister;
import com.fasterxml.storemate.shared.IpAndPort;
/**
* Value class used by {@link NetworkClient} to keep track of state
* of a single cluster node.
* Instances are mutable to a degree, and properly synchronized to allow
* thread-safe use.
*/
public class ClusterServerNodeImpl
implements ClusterServerNode
{
/**
* Address (ip number and port) used for communicating with the
* node. Note that this is resolved end point in case server
* node has been configured to use "localhost" (resolution can
* be done since client has remote address for starting points)
*/
private final IpAndPort _address;
/**
* Reference to the root path of the server node this object represents;
* used for constructing references to item entry points and node
* status accessor.
*/
private final RequestPath _pathBase;
private KeyRange _activeRange;
private KeyRange _passiveRange;
private KeyRange _totalRange;
protected final AtomicBoolean _disabled = new AtomicBoolean(false);
/**
* Time when last request was sent specifically for this server node
* (i.e. not updated when we get indirect updates)
*/
private final AtomicLong _lastRequestSent = new AtomicLong(0L);
/**
* Time when last request was sent specifically from this server node
* (i.e. not updated when we get indirect updates)
*/
private final AtomicLong _lastResponseReceived = new AtomicLong(0L);
/**
* Timestamp of last update for information regarding this node; regardless
* of whether directly or indirectly.
*/
private long _lastNodeUpdateFetched = 0L;
/**
* Timestamp of last version of cluster update from this server node
* (i.e. not applicable for indirect updates)
*/
private long _lastClusterUpdateFetched = 0L;
/**
* Timestamp of last version of cluster update that this server node
* might have; received indirectly via one of GET, PUT or DELETE
* operations.
*/
private final AtomicLong _lastClusterUpdateAvailable = new AtomicLong(1L);
/*
/**********************************************************************
/* Entry accessor handling
/**********************************************************************
*/
protected final ContentPutter> _entryPutter;
protected final ContentGetter> _entryGetter;
protected final ContentHeader> _entryHeader;
protected final ContentDeleter> _entryDeleter;
protected final EntryLister> _entryLister;
/*
/**********************************************************************
/* Instance creation
/**********************************************************************
*/
public ClusterServerNodeImpl(RequestPath pathBase,
IpAndPort address, KeyRange activeRange, KeyRange passiveRange,
EntryAccessors> entryAccessors)
{
_pathBase = pathBase;
_address = address;
_activeRange = activeRange;
_passiveRange = passiveRange;
_totalRange = _activeRange.union(_passiveRange);
_entryPutter = entryAccessors.entryPutter(this);
_entryGetter = entryAccessors.entryGetter(this);
_entryHeader = entryAccessors.entryHeader(this);
_entryDeleter = entryAccessors.entryDeleter(this);
_entryLister = entryAccessors.entryLister(this);
}
// only for test usage
private ClusterServerNodeImpl(RequestPath pathBase,
IpAndPort address, KeyRange activeRange, KeyRange passiveRange)
{
_pathBase = pathBase;
_address = address;
_activeRange = activeRange;
_passiveRange = passiveRange;
_totalRange = _activeRange.union(_passiveRange);
_entryPutter = null;
_entryGetter = null;
_entryHeader = null;
_entryDeleter = null;
_entryLister = null;
}
protected static ClusterServerNodeImpl forTesting(KeyRange range) {
return forTesting(range, range);
}
protected static ClusterServerNodeImpl forTesting(KeyRange rangeActive, KeyRange rangePassive) {
return new ClusterServerNodeImpl(null, // client not needed for kinds of tests
new IpAndPort("localhost:"+rangeActive.getStart()),
rangeActive, rangePassive);
}
/*
/**********************************************************************
/* Mutations
/**********************************************************************
*/
/**
* @return True if ranges changed
*/
public boolean updateRanges(KeyRange activeRange, KeyRange passiveRange)
{
if (_activeRange.equals(activeRange) && _passiveRange.equals(passiveRange)) {
return false;
}
_activeRange = activeRange;
_passiveRange = passiveRange;
_totalRange = activeRange.union(passiveRange);
return true;
}
/**
* @return True if state changed
*/
public boolean updateDisabled(boolean state) {
boolean old = _disabled.getAndSet(state);
return (old != state);
}
/**
* Method called to update cluster info timestamps, based on header
* received during regular CRUD operations.
*
* @return True if state changed
*/
public boolean updateLastClusterUpdateAvailable(long requestTime, long responseTime,
long timestamp)
{
if (_lastResponseReceived.get() < responseTime) {
_lastRequestSent.set(requestTime);
_lastResponseReceived.set(responseTime);
long old = _lastClusterUpdateAvailable.getAndSet(timestamp);
return (old != timestamp);
}
return false;
}
public void setLastRequestSent(long timestamp) {
_lastRequestSent.set(timestamp);
}
public void setLastResponseReceived(long timestamp) {
_lastResponseReceived.set(timestamp);
}
public void setLastNodeUpdateFetched(long timestamp) {
_lastNodeUpdateFetched = timestamp;
}
public void setLastClusterUpdateFetched(long timestamp) {
_lastClusterUpdateFetched = timestamp;
}
public void setLastClusterUpdateAvailable(long timestamp) {
_lastClusterUpdateAvailable.set(timestamp);
}
/*
/**********************************************************************
/* ReadOnlyServerNodeState implementation (public accessors)
/**********************************************************************
*/
@Override
public IpAndPort getAddress() { return _address; }
@Override
public KeyRange getActiveRange() { return _activeRange; }
@Override
public KeyRange getPassiveRange() { return _passiveRange; }
@Override
public KeyRange getTotalRange() { return _totalRange; }
@Override
public boolean isDisabled() { return _disabled.get(); }
@Override
public long getLastRequestSent() { return _lastRequestSent.get(); }
@Override
public long getLastResponseReceived() { return _lastResponseReceived.get(); }
@Override
public long getLastNodeUpdateFetched() { return _lastNodeUpdateFetched; }
@Override
public long getLastClusterUpdateFetched() { return _lastClusterUpdateFetched; }
@Override
public long getLastClusterUpdateAvailable() { return _lastClusterUpdateAvailable.get(); }
/*
/**********************************************************************
/* Advanced accessors
/**********************************************************************
*/
/**
* Method for calculating distance metrics to use for sorting server nodes
* based on distance. In addition to basic clock-wise distance from start
* point we also consider disabled nodes to be further away than any
* enabled nodes; this is done by adding full length of key space to basic
* clock-wise distance. *
*/
public int calculateSortingDistance(KeyHash keyHash)
{
/* Note: while we include things based on passive range, distance
* should be based on active range; this to make passive range
* more useful (can catch up with larger passive range; then enable
* larger range once caught up)
*/
KeyRange range = getActiveRange();
int distance = range.clockwiseDistance(keyHash);
if (isDisabled()) {
distance += range.getKeyspace().getLength();
}
return distance;
}
/*
/**********************************************************************
/* Call accessors, paths etc
/**********************************************************************
*/
/*
@SuppressWarnings("unchecked")
@Override
public P resourceEndpoint() {
return (P) _pathBase.builder();
}
*/
@SuppressWarnings("unchecked")
@Override
public
P rootPath() {
return (P) _pathBase.builder();
}
@SuppressWarnings("unchecked")
@Override
public ContentPutter entryPutter() {
return (ContentPutter) _entryPutter;
}
@SuppressWarnings("unchecked")
@Override
public ContentGetter entryGetter() {
return (ContentGetter) _entryGetter;
}
@SuppressWarnings("unchecked")
@Override
public ContentHeader entryHeader() {
return (ContentHeader) _entryHeader;
}
@SuppressWarnings("unchecked")
@Override
public ContentDeleter entryDeleter() {
return (ContentDeleter) _entryDeleter;
}
@SuppressWarnings("unchecked")
@Override
public EntryLister entryLister() {
return (EntryLister) _entryLister;
}
}