mirror of https://github.com/apache/cassandra
Gossip doesn't converge due to race condition when updating EndpointStates multiple fields
patch by David Capwell, Matt Byrd; reviewed by Blake Eggleston, Brandon Williams for CASSANDRA-20659
This commit is contained in:
parent
422b8a6cbd
commit
c736d22cf8
|
|
@ -1,4 +1,5 @@
|
||||||
4.0.18
|
4.0.18
|
||||||
|
* Gossip doesn't converge due to race condition when updating EndpointStates multiple fields (CASSANDRA-20659)
|
||||||
* Handle sstable metadata stats file getting a new mtime after compaction has finished (CASSANDRA-18119)
|
* Handle sstable metadata stats file getting a new mtime after compaction has finished (CASSANDRA-18119)
|
||||||
* Honor MAX_PARALLEL_TRANSFERS correctly (CASSANDRA-20532)
|
* Honor MAX_PARALLEL_TRANSFERS correctly (CASSANDRA-20532)
|
||||||
* Updating a column with a new TTL but same expiration time is non-deterministic and causes repair mismatches. (CASSANDRA-20561)
|
* Updating a column with a new TTL but same expiration time is non-deterministic and causes repair mismatches. (CASSANDRA-20561)
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,9 @@ import java.util.stream.Collectors;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
|
||||||
|
import com.google.common.base.Function;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
|
@ -37,16 +40,25 @@ import org.apache.cassandra.utils.CassandraVersion;
|
||||||
* This abstraction represents both the HeartBeatState and the ApplicationState in an EndpointState
|
* This abstraction represents both the HeartBeatState and the ApplicationState in an EndpointState
|
||||||
* instance. Any state for a given endpoint can be retrieved from this instance.
|
* instance. Any state for a given endpoint can be retrieved from this instance.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
public class EndpointState
|
public class EndpointState
|
||||||
{
|
{
|
||||||
protected static final Logger logger = LoggerFactory.getLogger(EndpointState.class);
|
protected static final Logger logger = LoggerFactory.getLogger(EndpointState.class);
|
||||||
|
|
||||||
public final static IVersionedSerializer<EndpointState> serializer = new EndpointStateSerializer();
|
public final static IVersionedSerializer<EndpointState> serializer = new EndpointStateSerializer();
|
||||||
|
|
||||||
private volatile HeartBeatState hbState;
|
private static class View
|
||||||
private final AtomicReference<Map<ApplicationState, VersionedValue>> applicationState;
|
{
|
||||||
|
final HeartBeatState hbState;
|
||||||
|
final Map<ApplicationState, VersionedValue> applicationState;
|
||||||
|
|
||||||
|
private View(HeartBeatState hbState, Map<ApplicationState, VersionedValue> applicationState)
|
||||||
|
{
|
||||||
|
this.hbState = hbState;
|
||||||
|
this.applicationState = applicationState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final AtomicReference<View> ref;
|
||||||
|
|
||||||
/* fields below do not get serialized */
|
/* fields below do not get serialized */
|
||||||
private volatile long updateTimestamp;
|
private volatile long updateTimestamp;
|
||||||
|
|
@ -54,46 +66,79 @@ public class EndpointState
|
||||||
|
|
||||||
public EndpointState(HeartBeatState initialHbState)
|
public EndpointState(HeartBeatState initialHbState)
|
||||||
{
|
{
|
||||||
this(initialHbState, new EnumMap<ApplicationState, VersionedValue>(ApplicationState.class));
|
this(initialHbState, new EnumMap<>(ApplicationState.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
public EndpointState(EndpointState other)
|
public EndpointState(EndpointState other)
|
||||||
{
|
{
|
||||||
this(new HeartBeatState(other.hbState), new EnumMap<>(other.applicationState.get()));
|
ref = new AtomicReference<>(other.ref.get());
|
||||||
}
|
|
||||||
|
|
||||||
EndpointState(HeartBeatState initialHbState, Map<ApplicationState, VersionedValue> states)
|
|
||||||
{
|
|
||||||
hbState = initialHbState;
|
|
||||||
applicationState = new AtomicReference<Map<ApplicationState, VersionedValue>>(new EnumMap<>(states));
|
|
||||||
updateTimestamp = System.nanoTime();
|
updateTimestamp = System.nanoTime();
|
||||||
isAlive = true;
|
isAlive = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
HeartBeatState getHeartBeatState()
|
@VisibleForTesting
|
||||||
|
public EndpointState(HeartBeatState initialHbState, Map<ApplicationState, VersionedValue> states)
|
||||||
{
|
{
|
||||||
return hbState;
|
ref = new AtomicReference<>(new View(initialHbState, new EnumMap<>(states)));
|
||||||
|
updateTimestamp = System.nanoTime();
|
||||||
|
isAlive = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setHeartBeatState(HeartBeatState newHbState)
|
@VisibleForTesting
|
||||||
|
public HeartBeatState getHeartBeatState()
|
||||||
{
|
{
|
||||||
updateTimestamp();
|
return ref.get().hbState;
|
||||||
hbState = newHbState;
|
}
|
||||||
|
|
||||||
|
public void updateHeartBeat()
|
||||||
|
{
|
||||||
|
updateHeartBeat(HeartBeatState::updateHeartBeat);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void forceNewerGenerationUnsafe()
|
||||||
|
{
|
||||||
|
updateHeartBeat(HeartBeatState::forceNewerGenerationUnsafe);
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public void forceHighestPossibleVersionUnsafe()
|
||||||
|
{
|
||||||
|
updateHeartBeat(HeartBeatState::forceHighestPossibleVersionUnsafe);
|
||||||
|
}
|
||||||
|
|
||||||
|
void unsafeSetEmptyHeartBeatState()
|
||||||
|
{
|
||||||
|
updateHeartBeat(ignore -> HeartBeatState.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateHeartBeat(Function<HeartBeatState, HeartBeatState> fn)
|
||||||
|
{
|
||||||
|
HeartBeatState previous = null;
|
||||||
|
HeartBeatState update = null;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
View view = ref.get();
|
||||||
|
if (previous == null || view.hbState != previous) // if this races with updating states then can avoid bumping versions
|
||||||
|
update = fn.apply(view.hbState);
|
||||||
|
if (ref.compareAndSet(view, new View(update, view.applicationState)))
|
||||||
|
return;
|
||||||
|
previous = view.hbState;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public VersionedValue getApplicationState(ApplicationState key)
|
public VersionedValue getApplicationState(ApplicationState key)
|
||||||
{
|
{
|
||||||
return applicationState.get().get(key);
|
return ref.get().applicationState.get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean containsApplicationState(ApplicationState key)
|
public boolean containsApplicationState(ApplicationState key)
|
||||||
{
|
{
|
||||||
return applicationState.get().containsKey(key);
|
return ref.get().applicationState.containsKey(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<Map.Entry<ApplicationState, VersionedValue>> states()
|
public Set<Map.Entry<ApplicationState, VersionedValue>> states()
|
||||||
{
|
{
|
||||||
return applicationState.get().entrySet();
|
return ref.get().applicationState.entrySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addApplicationState(ApplicationState key, VersionedValue value)
|
public void addApplicationState(ApplicationState key, VersionedValue value)
|
||||||
|
|
@ -107,17 +152,27 @@ public class EndpointState
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addApplicationStates(Set<Map.Entry<ApplicationState, VersionedValue>> values)
|
public void addApplicationStates(Set<Map.Entry<ApplicationState, VersionedValue>> values)
|
||||||
|
{
|
||||||
|
addApplicationStates(values, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addApplicationStates(Set<Map.Entry<ApplicationState, VersionedValue>> values, @Nullable HeartBeatState hbState)
|
||||||
{
|
{
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
Map<ApplicationState, VersionedValue> orig = applicationState.get();
|
View view = this.ref.get();
|
||||||
|
Map<ApplicationState, VersionedValue> orig = view.applicationState;
|
||||||
Map<ApplicationState, VersionedValue> copy = new EnumMap<>(orig);
|
Map<ApplicationState, VersionedValue> copy = new EnumMap<>(orig);
|
||||||
|
|
||||||
for (Map.Entry<ApplicationState, VersionedValue> value : values)
|
for (Map.Entry<ApplicationState, VersionedValue> value : values)
|
||||||
copy.put(value.getKey(), value.getValue());
|
copy.put(value.getKey(), value.getValue());
|
||||||
|
|
||||||
if (applicationState.compareAndSet(orig, copy))
|
if (this.ref.compareAndSet(view, new View(hbState == null ? view.hbState : hbState, copy)))
|
||||||
|
{
|
||||||
|
if (hbState != null)
|
||||||
|
updateTimestamp();
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -125,18 +180,19 @@ public class EndpointState
|
||||||
{
|
{
|
||||||
while (hasLegacyFields())
|
while (hasLegacyFields())
|
||||||
{
|
{
|
||||||
Map<ApplicationState, VersionedValue> orig = applicationState.get();
|
View view = ref.get();
|
||||||
|
Map<ApplicationState, VersionedValue> orig = view.applicationState;
|
||||||
Map<ApplicationState, VersionedValue> updatedStates = filterMajorVersion3LegacyApplicationStates(orig);
|
Map<ApplicationState, VersionedValue> updatedStates = filterMajorVersion3LegacyApplicationStates(orig);
|
||||||
// avoid updating if no state is removed
|
// avoid updating if no state is removed
|
||||||
if (orig.size() == updatedStates.size()
|
if (orig.size() == updatedStates.size()
|
||||||
|| applicationState.compareAndSet(orig, updatedStates))
|
|| ref.compareAndSet(view, new View(view.hbState, updatedStates)))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean hasLegacyFields()
|
private boolean hasLegacyFields()
|
||||||
{
|
{
|
||||||
Set<ApplicationState> statesPresent = applicationState.get().keySet();
|
Set<ApplicationState> statesPresent = ref.get().applicationState.keySet();
|
||||||
if (statesPresent.isEmpty())
|
if (statesPresent.isEmpty())
|
||||||
return false;
|
return false;
|
||||||
return (statesPresent.contains(ApplicationState.STATUS) && statesPresent.contains(ApplicationState.STATUS_WITH_PORT))
|
return (statesPresent.contains(ApplicationState.STATUS) && statesPresent.contains(ApplicationState.STATUS_WITH_PORT))
|
||||||
|
|
@ -193,7 +249,7 @@ public class EndpointState
|
||||||
|
|
||||||
public boolean isStateEmpty()
|
public boolean isStateEmpty()
|
||||||
{
|
{
|
||||||
return applicationState.get().isEmpty();
|
return ref.get().applicationState.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -201,8 +257,10 @@ public class EndpointState
|
||||||
*/
|
*/
|
||||||
public boolean isEmptyWithoutStatus()
|
public boolean isEmptyWithoutStatus()
|
||||||
{
|
{
|
||||||
Map<ApplicationState, VersionedValue> state = applicationState.get();
|
View view = ref.get();
|
||||||
return hbState.isEmpty() && !(state.containsKey(ApplicationState.STATUS_WITH_PORT) || state.containsKey(ApplicationState.STATUS));
|
Map<ApplicationState, VersionedValue> state = view.applicationState;
|
||||||
|
boolean hasStatus = state.containsKey(ApplicationState.STATUS_WITH_PORT) || state.containsKey(ApplicationState.STATUS);
|
||||||
|
return view.hbState.isEmpty() && !hasStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isRpcReady()
|
public boolean isRpcReady()
|
||||||
|
|
@ -253,7 +311,8 @@ public class EndpointState
|
||||||
|
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
return "EndpointState: HeartBeatState = " + hbState + ", AppStateMap = " + applicationState.get();
|
View view = ref.get();
|
||||||
|
return "EndpointState: HeartBeatState = " + view.hbState + ", AppStateMap = " + view.applicationState;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -310,7 +310,7 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean
|
||||||
taskLock.lock();
|
taskLock.lock();
|
||||||
|
|
||||||
/* Update the local heartbeat counter. */
|
/* Update the local heartbeat counter. */
|
||||||
endpointStateMap.get(FBUtilities.getBroadcastAddressAndPort()).getHeartBeatState().updateHeartBeat();
|
endpointStateMap.get(FBUtilities.getBroadcastAddressAndPort()).updateHeartBeat();
|
||||||
if (logger.isTraceEnabled())
|
if (logger.isTraceEnabled())
|
||||||
logger.trace("My heartbeat is now {}", endpointStateMap.get(FBUtilities.getBroadcastAddressAndPort()).getHeartBeatState().getHeartBeatVersion());
|
logger.trace("My heartbeat is now {}", endpointStateMap.get(FBUtilities.getBroadcastAddressAndPort()).getHeartBeatState().getHeartBeatVersion());
|
||||||
final List<GossipDigest> gDigests = new ArrayList<>();
|
final List<GossipDigest> gDigests = new ArrayList<>();
|
||||||
|
|
@ -598,7 +598,7 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean
|
||||||
epState.addApplicationState(ApplicationState.STATUS_WITH_PORT, shutdown);
|
epState.addApplicationState(ApplicationState.STATUS_WITH_PORT, shutdown);
|
||||||
epState.addApplicationState(ApplicationState.STATUS, StorageService.instance.valueFactory.shutdown(true));
|
epState.addApplicationState(ApplicationState.STATUS, StorageService.instance.valueFactory.shutdown(true));
|
||||||
epState.addApplicationState(ApplicationState.RPC_READY, StorageService.instance.valueFactory.rpcReady(false));
|
epState.addApplicationState(ApplicationState.RPC_READY, StorageService.instance.valueFactory.rpcReady(false));
|
||||||
epState.getHeartBeatState().forceHighestPossibleVersionUnsafe();
|
epState.forceHighestPossibleVersionUnsafe();
|
||||||
markDead(endpoint, epState);
|
markDead(endpoint, epState);
|
||||||
FailureDetector.instance.forceConviction(endpoint);
|
FailureDetector.instance.forceConviction(endpoint);
|
||||||
GossiperDiagnostics.markedAsShutdown(this, endpoint);
|
GossiperDiagnostics.markedAsShutdown(this, endpoint);
|
||||||
|
|
@ -778,7 +778,7 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean
|
||||||
// update the other node's generation to mimic it as if it had changed it itself
|
// update the other node's generation to mimic it as if it had changed it itself
|
||||||
logger.info("Advertising removal for {}", endpoint);
|
logger.info("Advertising removal for {}", endpoint);
|
||||||
epState.updateTimestamp(); // make sure we don't evict it too soon
|
epState.updateTimestamp(); // make sure we don't evict it too soon
|
||||||
epState.getHeartBeatState().forceNewerGenerationUnsafe();
|
epState.forceNewerGenerationUnsafe();
|
||||||
Map<ApplicationState, VersionedValue> states = new EnumMap<>(ApplicationState.class);
|
Map<ApplicationState, VersionedValue> states = new EnumMap<>(ApplicationState.class);
|
||||||
states.put(ApplicationState.STATUS_WITH_PORT, StorageService.instance.valueFactory.removingNonlocal(hostId));
|
states.put(ApplicationState.STATUS_WITH_PORT, StorageService.instance.valueFactory.removingNonlocal(hostId));
|
||||||
states.put(ApplicationState.STATUS, StorageService.instance.valueFactory.removingNonlocal(hostId));
|
states.put(ApplicationState.STATUS, StorageService.instance.valueFactory.removingNonlocal(hostId));
|
||||||
|
|
@ -798,7 +798,7 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean
|
||||||
{
|
{
|
||||||
EndpointState epState = endpointStateMap.get(endpoint);
|
EndpointState epState = endpointStateMap.get(endpoint);
|
||||||
epState.updateTimestamp(); // make sure we don't evict it too soon
|
epState.updateTimestamp(); // make sure we don't evict it too soon
|
||||||
epState.getHeartBeatState().forceNewerGenerationUnsafe();
|
epState.forceNewerGenerationUnsafe();
|
||||||
long expireTime = computeExpireTime();
|
long expireTime = computeExpireTime();
|
||||||
epState.addApplicationState(ApplicationState.STATUS_WITH_PORT, StorageService.instance.valueFactory.removedNonlocal(hostId, expireTime));
|
epState.addApplicationState(ApplicationState.STATUS_WITH_PORT, StorageService.instance.valueFactory.removedNonlocal(hostId, expireTime));
|
||||||
epState.addApplicationState(ApplicationState.STATUS, StorageService.instance.valueFactory.removedNonlocal(hostId, expireTime));
|
epState.addApplicationState(ApplicationState.STATUS, StorageService.instance.valueFactory.removedNonlocal(hostId, expireTime));
|
||||||
|
|
@ -849,7 +849,7 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean
|
||||||
else if (newState.getHeartBeatState().getHeartBeatVersion() != heartbeat)
|
else if (newState.getHeartBeatState().getHeartBeatVersion() != heartbeat)
|
||||||
throw new RuntimeException("Endpoint still alive: " + endpoint + " heartbeat changed while trying to assassinate it");
|
throw new RuntimeException("Endpoint still alive: " + endpoint + " heartbeat changed while trying to assassinate it");
|
||||||
epState.updateTimestamp(); // make sure we don't evict it too soon
|
epState.updateTimestamp(); // make sure we don't evict it too soon
|
||||||
epState.getHeartBeatState().forceNewerGenerationUnsafe();
|
epState.forceNewerGenerationUnsafe();
|
||||||
}
|
}
|
||||||
|
|
||||||
Collection<Token> tokens = null;
|
Collection<Token> tokens = null;
|
||||||
|
|
@ -1580,15 +1580,7 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean
|
||||||
// don't assert here, since if the node restarts the version will go back to zero
|
// don't assert here, since if the node restarts the version will go back to zero
|
||||||
int oldVersion = localState.getHeartBeatState().getHeartBeatVersion();
|
int oldVersion = localState.getHeartBeatState().getHeartBeatVersion();
|
||||||
|
|
||||||
localState.setHeartBeatState(remoteState.getHeartBeatState());
|
Set<Entry<ApplicationState, VersionedValue>> updatedStates = remoteState.states().stream().filter(entry -> {
|
||||||
if (logger.isTraceEnabled())
|
|
||||||
logger.trace("Updating heartbeat state version to {} from {} for {} ...", localState.getHeartBeatState().getHeartBeatVersion(), oldVersion, addr);
|
|
||||||
|
|
||||||
Set<Entry<ApplicationState, VersionedValue>> remoteStates = remoteState.states();
|
|
||||||
assert remoteState.getHeartBeatState().getGeneration() == localState.getHeartBeatState().getGeneration();
|
|
||||||
|
|
||||||
|
|
||||||
Set<Entry<ApplicationState, VersionedValue>> updatedStates = remoteStates.stream().filter(entry -> {
|
|
||||||
// filter out the states that are already up to date (has the same or higher version)
|
// filter out the states that are already up to date (has the same or higher version)
|
||||||
VersionedValue local = localState.getApplicationState(entry.getKey());
|
VersionedValue local = localState.getApplicationState(entry.getKey());
|
||||||
return (local == null || local.version < entry.getValue().version);
|
return (local == null || local.version < entry.getValue().version);
|
||||||
|
|
@ -1601,7 +1593,9 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean
|
||||||
logger.trace("Updating {} state version to {} for {}", entry.getKey().toString(), entry.getValue().version, addr);
|
logger.trace("Updating {} state version to {} for {}", entry.getKey().toString(), entry.getValue().version, addr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
localState.addApplicationStates(updatedStates);
|
localState.addApplicationStates(updatedStates, remoteState.getHeartBeatState());
|
||||||
|
if (logger.isTraceEnabled())
|
||||||
|
logger.trace("Updating heartbeat state version to {} from {} for {} ...", localState.getHeartBeatState().getHeartBeatVersion(), oldVersion, addr);
|
||||||
|
|
||||||
// get rid of legacy fields once the cluster is not in mixed mode
|
// get rid of legacy fields once the cluster is not in mixed mode
|
||||||
if (!hasMajorVersion3OrUnknownNodes())
|
if (!hasMajorVersion3OrUnknownNodes())
|
||||||
|
|
@ -1983,7 +1977,7 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean
|
||||||
public void forceNewerGeneration()
|
public void forceNewerGeneration()
|
||||||
{
|
{
|
||||||
EndpointState epstate = endpointStateMap.get(FBUtilities.getBroadcastAddressAndPort());
|
EndpointState epstate = endpointStateMap.get(FBUtilities.getBroadcastAddressAndPort());
|
||||||
epstate.getHeartBeatState().forceNewerGenerationUnsafe();
|
epstate.forceNewerGenerationUnsafe();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -2004,7 +1998,7 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean
|
||||||
if (epState != null)
|
if (epState != null)
|
||||||
{
|
{
|
||||||
logger.debug("not replacing a previous epState for {}, but reusing it: {}", ep, epState);
|
logger.debug("not replacing a previous epState for {}, but reusing it: {}", ep, epState);
|
||||||
epState.setHeartBeatState(HeartBeatState.empty());
|
epState.unsafeSetEmptyHeartBeatState();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -33,8 +33,8 @@ public class HeartBeatState
|
||||||
|
|
||||||
public static final IVersionedSerializer<HeartBeatState> serializer = new HeartBeatStateSerializer();
|
public static final IVersionedSerializer<HeartBeatState> serializer = new HeartBeatStateSerializer();
|
||||||
|
|
||||||
private volatile int generation;
|
private final int generation;
|
||||||
private volatile int version;
|
private final int version;
|
||||||
|
|
||||||
HeartBeatState(int gen)
|
HeartBeatState(int gen)
|
||||||
{
|
{
|
||||||
|
|
@ -67,29 +67,29 @@ public class HeartBeatState
|
||||||
return version == EMPTY_VERSION;
|
return version == EMPTY_VERSION;
|
||||||
}
|
}
|
||||||
|
|
||||||
int getGeneration()
|
public int getGeneration()
|
||||||
{
|
{
|
||||||
return generation;
|
return generation;
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateHeartBeat()
|
HeartBeatState updateHeartBeat()
|
||||||
{
|
{
|
||||||
version = VersionGenerator.getNextVersion();
|
return new HeartBeatState(generation, VersionGenerator.getNextVersion());
|
||||||
}
|
}
|
||||||
|
|
||||||
int getHeartBeatVersion()
|
public int getHeartBeatVersion()
|
||||||
{
|
{
|
||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
|
|
||||||
void forceNewerGenerationUnsafe()
|
HeartBeatState forceNewerGenerationUnsafe()
|
||||||
{
|
{
|
||||||
generation += 1;
|
return new HeartBeatState(generation + 1, version);
|
||||||
}
|
}
|
||||||
|
|
||||||
void forceHighestPossibleVersionUnsafe()
|
HeartBeatState forceHighestPossibleVersionUnsafe()
|
||||||
{
|
{
|
||||||
version = Integer.MAX_VALUE;
|
return new HeartBeatState(generation, Integer.MAX_VALUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString()
|
public String toString()
|
||||||
|
|
|
||||||
|
|
@ -304,7 +304,7 @@ public class GossiperTest
|
||||||
proposedRemoteState = new EndpointState(proposedRemoteHeartBeat);
|
proposedRemoteState = new EndpointState(proposedRemoteHeartBeat);
|
||||||
|
|
||||||
// Bump the heartbeat version and use the same TOKENS state
|
// Bump the heartbeat version and use the same TOKENS state
|
||||||
proposedRemoteHeartBeat.updateHeartBeat();
|
proposedRemoteState.updateHeartBeat();
|
||||||
proposedRemoteState.addApplicationState(ApplicationState.TOKENS, tokensValue);
|
proposedRemoteState.addApplicationState(ApplicationState.TOKENS, tokensValue);
|
||||||
|
|
||||||
// The following state change should only update heartbeat without updating the TOKENS state
|
// The following state change should only update heartbeat without updating the TOKENS state
|
||||||
|
|
|
||||||
|
|
@ -130,7 +130,7 @@ public class SerializationsTest extends AbstractSerializationsTester
|
||||||
private static List<GossipDigest> Digests = new ArrayList<GossipDigest>();
|
private static List<GossipDigest> Digests = new ArrayList<GossipDigest>();
|
||||||
|
|
||||||
{
|
{
|
||||||
HeartbeatSt.updateHeartBeat();
|
EndpointSt.updateHeartBeat();
|
||||||
EndpointSt.addApplicationState(ApplicationState.LOAD, vv0);
|
EndpointSt.addApplicationState(ApplicationState.LOAD, vv0);
|
||||||
EndpointSt.addApplicationState(ApplicationState.STATUS_WITH_PORT, vv1);
|
EndpointSt.addApplicationState(ApplicationState.STATUS_WITH_PORT, vv1);
|
||||||
for (int i = 0; i < 100; i++)
|
for (int i = 0; i < 100; i++)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue