net.openhft.chronicle.hash.replication.DefaultEventualConsistencyStrategy Maven / Gradle / Ivy
/*
* Copyright (C) 2012, 2016 higherfrequencytrading.com
* Copyright (C) 2016 Roman Leventov
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*/
package net.openhft.chronicle.hash.replication;
import net.openhft.chronicle.hash.ChronicleHash;
import net.openhft.chronicle.hash.ChronicleHashBuilder;
import net.openhft.chronicle.hash.ChronicleHashBuilderPrivateAPI;
import net.openhft.chronicle.map.replication.MapRemoteOperations;
import net.openhft.chronicle.set.replication.SetRemoteOperations;
import static net.openhft.chronicle.hash.replication.DefaultEventualConsistencyStrategy.AcceptanceDecision.ACCEPT;
import static net.openhft.chronicle.hash.replication.DefaultEventualConsistencyStrategy.AcceptanceDecision.DISCARD;
/**
* Specifies the default eventual consistency strategy for {@link
* ChronicleHashBuilderPrivateAPI#replication(byte) replicated} {@link ChronicleHash}es:
* last write wins. If two writes to a single entry occurred simultaneously on different
* nodes, the write on the node with lower identifier wins.
*
* @see MapRemoteOperations
* @see SetRemoteOperations
*/
public final class DefaultEventualConsistencyStrategy {
/**
* Returns the acceptance decision, should be made about the modification operation in the
* given {@code context}, aiming to modify the given {@code entry}. This method doesn't do any
* changes to {@code entry} nor {@code context} state. {@link MapRemoteOperations} and
* {@link SetRemoteOperations} method implementations should guide the result of calling this
* method to do something to actually apply the remote operation.
*
* @param entry the entry to be modified
* @param context the remote operation context
* @return if the remote operation should be accepted or discarded
*/
public static AcceptanceDecision decideOnRemoteModification(
ReplicableEntry entry, RemoteOperationContext context) {
long remoteTimestamp = context.remoteTimestamp();
long originTimestamp = entry.originTimestamp();
// Last write wins
if (remoteTimestamp > originTimestamp)
return ACCEPT;
if (remoteTimestamp < originTimestamp)
return DISCARD;
// remoteTimestamp == originTimestamp below
byte remoteIdentifier = context.remoteIdentifier();
byte originIdentifier = entry.originIdentifier();
// Lower identifier wins
if (remoteIdentifier < originIdentifier)
return ACCEPT;
if (remoteIdentifier > originIdentifier)
return DISCARD;
// remoteTimestamp == originTimestamp && remoteIdentifier == originIdentifier below
// This could be, only if a node with the origin identifier was lost, a new Chronicle Hash
// instance was started up, but with system time which for some reason is very late, so
// that it provides the same time, as the "old" node with this identifier, before it was
// lost. (This is almost a theoretical situation.) In this case, give advantage to fresh
// entry updates to the "new" node. Entries with the same id and timestamp, bootstrapped
// "back" from other nodes in the system, are discarded on this new node (this is the
// of the condition originIdentifier == currentNodeIdentifier). But those new updates
// should win on other nodes.
//
// Another case, in which we could have remoteTimestamp == originTimestamp &&
// remoteIdentifier == originIdentifier, is replication of barely the same entry, if an
// entry is bootstrapped "back" from remote node to it's origin node. In this case the
// following condition also plays right (the update is discarded, due to it's redundancy).
return originIdentifier == context.currentNodeIdentifier() ? DISCARD : ACCEPT;
}
private DefaultEventualConsistencyStrategy() {}
/**
* Decision, if {@link MapRemoteOperations remote modification operation} should be accepted
* or discarded. Used in {@link DefaultEventualConsistencyStrategy}.
*/
public enum AcceptanceDecision {
/**
* Acceptance decision -- the remote modification operation is applied to the local
* {@link ChronicleHash} state.
*/
ACCEPT,
/**
* Discard decision -- the remote modification operation is rejected.
*/
DISCARD
}
}