Package org.infinispan.transaction.xa.recovery

Source Code of org.infinispan.transaction.xa.recovery.RecoveryManagerImpl

package org.infinispan.transaction.xa.recovery;

import org.infinispan.commands.CommandsFactory;
import org.infinispan.commands.remote.RemoveRecoveryInfoCommand;
import org.infinispan.commands.remote.GetInDoubtTransactionsCommand;
import org.infinispan.factories.annotations.Inject;
import org.infinispan.remoting.responses.Response;
import org.infinispan.remoting.responses.SuccessfulResponse;
import org.infinispan.remoting.rpc.RpcManager;
import org.infinispan.remoting.transport.Address;
import org.infinispan.transaction.RemoteTransaction;
import org.infinispan.transaction.TransactionTable;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;

import javax.transaction.xa.Xid;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentMap;

/**
* Default implementation for {@link RecoveryManager}
*
* @author Mircea.Markus@jboss.com
* @since 5.0
*/
public class RecoveryManagerImpl implements RecoveryManager {

   private static Log log = LogFactory.getLog(RecoveryManagerImpl.class);

   private volatile RpcManager rpcManager;
   private volatile CommandsFactory commandFactory;

   /**
    * This relies on XIDs for different TMs being unique. E.g. for JBossTM this is correct as long as we have the right
    * config for <IP,port/process,sequencenumber>. This is expected to stand true for all major TM on the market.
    */
   private final ConcurrentMap<RecoveryInfoKey, RecoveryAwareRemoteTransaction> preparedTransactions;

   private final String cacheName;


   private volatile RecoveryAwareTransactionTable txTable;

   /**
    * we only broadcast the first time when node is started, then we just return the local cached prepared
    * transactions.
    */
   private volatile boolean broadcastForPreparedTx = true;

   public RecoveryManagerImpl(ConcurrentMap<RecoveryInfoKey, RecoveryAwareRemoteTransaction> recoveryHolder, String cacheName) {
      this.preparedTransactions = recoveryHolder;
      this.cacheName = cacheName;
   }

   @Inject
   public void init(RpcManager rpcManager, CommandsFactory commandsFactory, TransactionTable txTable) {
      this.rpcManager = rpcManager;
      this.commandFactory = commandsFactory;
      this.txTable = (RecoveryAwareTransactionTable)txTable;
   }

   @Override
   public RecoveryIterator getPreparedTransactionsFromCluster() {
      //1. get local transactions first
      PreparedTxIterator iterator = new PreparedTxIterator();
      iterator.add(txTable.getLocalPreparedXids());

      //2. then the remote ones
      if (notOnlyMeInTheCluster() && broadcastForPreparedTx) {
         boolean success = true;
         Map<Address, Response> responses = getAllPreparedTxFromCluster();
         for (Map.Entry<Address, Response> rEntry : responses.entrySet()) {
            Response thisResponse = rEntry.getValue();
            if (isSuccessful(thisResponse)) {
               List<Xid> responseValue = (List<Xid>) ((SuccessfulResponse) thisResponse).getResponseValue();
               if (log.isTraceEnabled()) {
                  log.trace("Received Xid lists %s from node %s", responseValue, rEntry.getKey());
               }
               iterator.add(responseValue);
            } else {
               log.warn("Missing the list of prepared transactions from node %s. Received response is %s",
                        rEntry.getKey(), rEntry.getValue());
               success = false;
            }
         }
         //this makes sure that the broadcast only happens once!
         this.broadcastForPreparedTx = !success;
         if (!broadcastForPreparedTx) log.info("Finished broadcasting for remote prepared transactions. " +
                                                     "Returning only local values from now on.");
      }
      return iterator;
   }

   @Override
   public void removeRecoveryInformation(Collection<Address> lockOwners, Xid xid, boolean sync) {
      //todo make sure this gets broad casted or at least flushed
      if (rpcManager != null) {
         RemoveRecoveryInfoCommand ftc = commandFactory.buildRemoveRecoveryInfoCommand(Collections.singletonList(xid));
         rpcManager.invokeRemotely(lockOwners, ftc, false);
      }
   }


   @Override
   public void removeLocalRecoveryInformation(List<Xid> xids) {
      for (Xid xid : xids) {
         RemoteTransaction remove = preparedTransactions.remove(new RecoveryInfoKey(xid, cacheName));
         if (remove != null && log.isTraceEnabled()) {
            log.trace("removed xid: %s", xid);
         }
      }
   }

   /**
    * A transaction is in doubt if it is {@link org.infinispan.transaction.RemoteTransaction#isInDoubt()}.
    */
   @Override
   public List<Xid> getLocalInDoubtTransactions() {
      List<Xid> result = new ArrayList<Xid>();
      for (Map.Entry<RecoveryInfoKey, RecoveryAwareRemoteTransaction> entry : preparedTransactions.entrySet()) {
         RecoveryAwareRemoteTransaction rt = entry.getValue();
         RecoveryInfoKey cacheNamePair = entry.getKey();
         if (cacheNamePair.sameCacheName(cacheName) && rt.isInDoubt()) {
            XidAware globalTransaction = (XidAware) rt.getGlobalTransaction();
            if (log.isTraceEnabled()) {
               log.trace("Found in doubt transaction: %s", globalTransaction);
            }
            result.add(globalTransaction.getXid());
         }
      }
      if (log.isTraceEnabled()) log.trace("Returning %s ", result);
      return result;
   }


   public void registerPreparedTransaction(RecoveryAwareRemoteTransaction remoteTransaction) {
      Xid xid = ((XidAware)remoteTransaction.getGlobalTransaction()).getXid();
      RemoteTransaction previous = preparedTransactions.put(new RecoveryInfoKey(xid, cacheName), remoteTransaction);
      if (previous != null) {
         log.error("There's already a prepared transaction with this xid: %s. New transaction is %s. Are there two " +
                         "different transactions having same Xid in the cluster?", previous, remoteTransaction);
         throw new IllegalStateException("Are there two different transactions having same Xid in the cluster?");
      }
   }


   public void nodesLeft(List<Address> leavers) {
      if (log.isTraceEnabled())
         log.trace("Handling leavers: %s. There are %s prepared transactions to check", leavers, preparedTransactions.values().size());
      for (RecoveryAwareRemoteTransaction rt : preparedTransactions.values()) {
         rt.computeOrphan(leavers);
      }
   }

   public RemoteTransaction getPreparedTransaction(Xid xid) {
      return preparedTransactions.get(new RecoveryInfoKey(xid, cacheName));
   }

   private boolean isSuccessful(Response thisResponse) {
      return thisResponse != null && thisResponse.isValid() && thisResponse.isSuccessful();
   }

   private boolean notOnlyMeInTheCluster() {
      return rpcManager != null && rpcManager.getTransport().getMembers().size() > 1;
   }

   private Map<Address, Response> getAllPreparedTxFromCluster() {
      GetInDoubtTransactionsCommand command = commandFactory.buildGetInDoubtTransactionsCommand();
      Map<Address, Response> addressResponseMap = rpcManager.invokeRemotely(null, command, true, false);
      if (log.isTraceEnabled()) log.trace("getAllPreparedTxFromCluster received from cluster: %s", addressResponseMap);
      return addressResponseMap;
   }

   public ConcurrentMap<RecoveryInfoKey, RecoveryAwareRemoteTransaction> getPreparedTransactions() {
      return preparedTransactions;
   }
}
TOP

Related Classes of org.infinispan.transaction.xa.recovery.RecoveryManagerImpl

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.