package org.infinispan.remoting.inboundhandler;
import static org.infinispan.remoting.inboundhandler.DeliverOrder.NONE;
import java.util.Collection;
import org.infinispan.commands.CommandInvocationId;
import org.infinispan.commands.CommandsFactory;
import org.infinispan.commands.ReplicableCommand;
import org.infinispan.commands.remote.CacheRpcCommand;
import org.infinispan.commands.remote.SingleRpcCommand;
import org.infinispan.commands.write.BackupAckCommand;
import org.infinispan.commands.write.BackupMultiKeyAckCommand;
import org.infinispan.commands.write.BackupPutMapRpcCommand;
import org.infinispan.commands.write.BackupWriteRpcCommand;
import org.infinispan.commands.write.ExceptionAckCommand;
import org.infinispan.configuration.cache.Configuration;
import org.infinispan.distribution.TriangleOrderManager;
import org.infinispan.factories.annotations.Inject;
import org.infinispan.factories.annotations.Start;
import org.infinispan.interceptors.locking.ClusteringDependentLogic;
import org.infinispan.remoting.inboundhandler.action.Action;
import org.infinispan.remoting.inboundhandler.action.ActionState;
import org.infinispan.remoting.inboundhandler.action.ActionStatus;
import org.infinispan.remoting.inboundhandler.action.DefaultReadyAction;
import org.infinispan.remoting.inboundhandler.action.LockAction;
import org.infinispan.remoting.inboundhandler.action.ReadyAction;
import org.infinispan.remoting.inboundhandler.action.TriangleOrderAction;
import org.infinispan.remoting.rpc.RpcManager;
import org.infinispan.remoting.transport.Address;
import org.infinispan.statetransfer.StateRequestCommand;
import org.infinispan.util.concurrent.BlockingRunnable;
import org.infinispan.util.concurrent.BlockingTaskAwareExecutorService;
import org.infinispan.util.concurrent.CommandAckCollector;
import org.infinispan.util.concurrent.locks.LockListener;
import org.infinispan.util.concurrent.locks.LockManager;
import org.infinispan.util.concurrent.locks.LockState;
import org.infinispan.util.concurrent.locks.RemoteLockCommand;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;
/**
* A {@link PerCacheInboundInvocationHandler} implementation for non-transactional and distributed caches that uses the
* triangle algorithm.
*
* @author Pedro Ruivo
* @since 9.0
*/
public class TrianglePerCacheInboundInvocationHandler extends BasePerCacheInboundInvocationHandler implements
LockListener, Action {
private static final Log log = LogFactory.getLog(TrianglePerCacheInboundInvocationHandler.class);
private static final boolean trace = log.isTraceEnabled();
private LockManager lockManager;
private ClusteringDependentLogic clusteringDependentLogic;
private long lockTimeout;
private TriangleOrderManager triangleOrderManager;
private RpcManager rpcManager;
private CommandAckCollector commandAckCollector;
private CommandsFactory commandsFactory;
private Address localAddress;
@Inject
public void inject(LockManager lockManager,
ClusteringDependentLogic clusteringDependentLogic,
Configuration configuration, TriangleOrderManager anotherTriangleOrderManager, RpcManager rpcManager,
CommandAckCollector commandAckCollector, CommandsFactory commandsFactory) {
this.lockManager = lockManager;
this.clusteringDependentLogic = clusteringDependentLogic;
lockTimeout = configuration.locking().lockAcquisitionTimeout();
this.triangleOrderManager = anotherTriangleOrderManager;
this.rpcManager = rpcManager;
this.commandAckCollector = commandAckCollector;
this.commandsFactory = commandsFactory;
}
@Start
public void start() {
localAddress = rpcManager.getAddress();
}
@Override
public void handle(CacheRpcCommand command, Reply reply, DeliverOrder order) {
if (order == DeliverOrder.TOTAL) {
unexpectedDeliverMode(command, order);
}
try {
switch (command.getCommandId()) {
case SingleRpcCommand.COMMAND_ID:
handleSingleRpcCommand((SingleRpcCommand) command, reply, order);
return;
case BackupWriteRpcCommand.COMMAND_ID:
handleBackupWriteRpcCommand((BackupWriteRpcCommand) command);
return;
case BackupPutMapRpcCommand.COMMAND_ID:
handleBackupPutMapRpcCommand((BackupPutMapRpcCommand) command);
return;
case BackupAckCommand.COMMAND_ID:
handleBackupAckCommand((BackupAckCommand) command);
return;
case BackupMultiKeyAckCommand.COMMAND_ID:
handleBackupMultiKeyAckCommand((BackupMultiKeyAckCommand) command);
return;
case ExceptionAckCommand.COMMAND_ID:
handleExceptionAck((ExceptionAckCommand) command);
return;
case StateRequestCommand.COMMAND_ID:
handleStateRequestCommand((StateRequestCommand) command, reply, order);
return;
default:
handleDefaultCommand(command, reply, order);
}
} catch (Throwable throwable) {
reply.reply(exceptionHandlingCommand(command, throwable));
}
}
//lock listener interface
@Override
public void onEvent(LockState state) {
remoteCommandsExecutor.checkForReadyTasks();
}
//action interface
@Override
public ActionStatus check(ActionState state) {
return isCommandSentBeforeFirstTopology(state.getCommandTopologyId()) ?
ActionStatus.CANCELED :
ActionStatus.READY;
}
public TriangleOrderManager getTriangleOrderManager() {
return triangleOrderManager;
}
public BlockingTaskAwareExecutorService getRemoteExecutor() {
return remoteCommandsExecutor;
}
public ClusteringDependentLogic getClusteringDependentLogic() {
return clusteringDependentLogic;
}
@Override
public void onFinally(ActionState state) {
//no-op
//needed for ConditionalOperationPrimaryOwnerFailTest
//it mocks this class and when Action.onFinally is invoked, it doesn't behave well with the default implementation
//in the interface.
}
@Override
protected Log getLog() {
return log;
}
@Override
protected boolean isTraceEnabled() {
return trace;
}
private void handleStateRequestCommand(StateRequestCommand command, Reply reply, DeliverOrder order) {
if (executeOnExecutorService(order, command)) {
BlockingRunnable runnable = createDefaultRunnable(command, reply, extractCommandTopologyId(command),
TopologyMode.READY_TOPOLOGY, order.preserveOrder());
remoteCommandsExecutor.execute(runnable);
} else {
BlockingRunnable runnable = createDefaultRunnable(command, reply, extractCommandTopologyId(command),
TopologyMode.WAIT_TOPOLOGY, order.preserveOrder());
runnable.run();
}
}
private void handleDefaultCommand(CacheRpcCommand command, Reply reply, DeliverOrder order) {
if (executeOnExecutorService(order, command)) {
BlockingRunnable runnable = createDefaultRunnable(command, reply, extractCommandTopologyId(command),
TopologyMode.READY_TX_DATA, order.preserveOrder());
remoteCommandsExecutor.execute(runnable);
} else {
BlockingRunnable runnable = createDefaultRunnable(command, reply, extractCommandTopologyId(command),
TopologyMode.WAIT_TX_DATA, order.preserveOrder());
runnable.run();
}
}
private void handleBackupPutMapRpcCommand(BackupPutMapRpcCommand command) {
final int topologyId = command.getTopologyId();
ReadyAction readyAction = createTriangleOrderAction(command, topologyId, command.getSequence(),
command.getMap().keySet().iterator().next());
BlockingRunnable runnable = createBackupPutMapRunnable(command, topologyId, readyAction);
remoteCommandsExecutor.execute(runnable);
}
private void handleBackupWriteRpcCommand(BackupWriteRpcCommand command) {
final int topologyId = command.getTopologyId();
ReadyAction readyAction = createTriangleOrderAction(command, topologyId, command.getSequence(), command.getKey());
BlockingRunnable runnable = createBackupWriteRpcRunnable(command, topologyId, readyAction);
remoteCommandsExecutor.execute(runnable);
}
private void handleExceptionAck(ExceptionAckCommand command) {
command.ack();
}
private void handleBackupMultiKeyAckCommand(BackupMultiKeyAckCommand command) {
command.ack();
}
private void handleBackupAckCommand(BackupAckCommand command) {
command.ack();
}
private void handleSingleRpcCommand(SingleRpcCommand command, Reply reply, DeliverOrder order) {
if (executeOnExecutorService(order, command)) {
int commandTopologyId = extractCommandTopologyId(command);
BlockingRunnable runnable = createReadyActionRunnable(command, reply, commandTopologyId, order.preserveOrder(),
createReadyAction(commandTopologyId, command));
remoteCommandsExecutor.execute(runnable);
} else {
createDefaultRunnable(command, reply, extractCommandTopologyId(command), TopologyMode.WAIT_TX_DATA,
order.preserveOrder()).run();
}
}
private void sendExceptionAck(CommandInvocationId id, Throwable throwable, int topologyId) {
final Address origin = id.getAddress();
if (trace) {
log.tracef("Sending exception ack for command %s. Originator=%s.", id, origin);
}
if (origin.equals(localAddress)) {
commandAckCollector.completeExceptionally(id.getId(), throwable, topologyId);
} else {
rpcManager.sendTo(origin, commandsFactory.buildExceptionAckCommand(id.getId(), throwable, topologyId), NONE);
}
}
private void sendBackupAck(CommandInvocationId id, int topologyId) {
final Address origin = id.getAddress();
boolean isLocal = localAddress.equals(origin);
if (trace) {
log.tracef("Sending ack for command %s. isLocal? %s.", id, isLocal);
}
if (isLocal) {
commandAckCollector.backupAck(id.getId(), origin, topologyId);
} else {
rpcManager.sendTo(origin, commandsFactory.buildBackupAckCommand(id.getId(), topologyId), NONE);
}
}
private BlockingRunnable createBackupWriteRpcRunnable(BackupWriteRpcCommand command, int commandTopologyId,
ReadyAction readyAction) {
readyAction.addListener(remoteCommandsExecutor::checkForReadyTasks);
return new DefaultTopologyRunnable(this, command, Reply.NO_OP, TopologyMode.READY_TX_DATA, commandTopologyId,
false) {
@Override
public boolean isReady() {
return super.isReady() && readyAction.isReady();
}
@Override
protected void onException(Throwable throwable) {
super.onException(throwable);
readyAction.onException();
readyAction.onFinally(); //notified TriangleOrderManager before sending the ack.
sendExceptionAck(((BackupWriteRpcCommand) command).getCommandInvocationId(), throwable, commandTopologyId);
}
@Override
protected void afterInvoke() {
super.afterInvoke();
readyAction.onFinally();
sendBackupAck(((BackupWriteRpcCommand) command).getCommandInvocationId(), commandTopologyId);
}
};
}
private void sendPutMapBackupAck(CommandInvocationId id, int topologyId, int segment) {
final Address origin = id.getAddress();
if (trace) {
log.tracef("Sending ack for command %s. Originator=%s.", id, origin);
}
if (id.getAddress().equals(localAddress)) {
commandAckCollector.multiKeyBackupAck(id.getId(), localAddress, segment, topologyId);
} else {
rpcManager
.sendTo(origin, commandsFactory.buildBackupMultiKeyAckCommand(id.getId(), segment, topologyId), NONE);
}
}
private BlockingRunnable createBackupPutMapRunnable(BackupPutMapRpcCommand command, int commandTopologyId,
ReadyAction readyAction) {
readyAction.addListener(remoteCommandsExecutor::checkForReadyTasks);
return new DefaultTopologyRunnable(this, command, Reply.NO_OP, TopologyMode.READY_TX_DATA, commandTopologyId,
false) {
@Override
public boolean isReady() {
return super.isReady() && readyAction.isReady();
}
@Override
protected void onException(Throwable throwable) {
super.onException(throwable);
readyAction.onException();
readyAction.onFinally();
sendExceptionAck(((BackupPutMapRpcCommand) command).getCommandInvocationId(), throwable, commandTopologyId);
}
@Override
protected void afterInvoke() {
super.afterInvoke();
readyAction.onFinally();
Object key = ((BackupPutMapRpcCommand) command).getMap().keySet().iterator().next();
int segment = clusteringDependentLogic.getCacheTopology().getDistribution(key).segmentId();
sendPutMapBackupAck(((BackupPutMapRpcCommand) command).getCommandInvocationId(), commandTopologyId,
segment);
}
};
}
private ReadyAction createReadyAction(int topologyId, RemoteLockCommand command) {
if (command.hasSkipLocking()) {
return null;
}
Collection<?> keys = command.getKeysToLock();
if (keys.isEmpty()) {
return null;
}
final long timeoutMillis = command.hasZeroLockAcquisition() ? 0 : lockTimeout;
DefaultReadyAction action = new DefaultReadyAction(new ActionState(command, topologyId, timeoutMillis),
this,
new LockAction(lockManager, clusteringDependentLogic));
action.registerListener();
return action;
}
private ReadyAction createReadyAction(int topologyId, SingleRpcCommand singleRpcCommand) {
ReplicableCommand command = singleRpcCommand.getCommand();
return command instanceof RemoteLockCommand ?
createReadyAction(topologyId, (RemoteLockCommand & ReplicableCommand) command) :
null;
}
private ReadyAction createTriangleOrderAction(ReplicableCommand command, int topologyId, long sequence, Object key) {
return new DefaultReadyAction(new ActionState(command, topologyId, 0), this,
new TriangleOrderAction(this, sequence, key));
}
}