All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.atomikos.icatch.imp.RecoveryDomainService Maven / Gradle / Ivy

/**
 * Copyright (C) 2000-2023 Atomikos 
 *
 * LICENSE CONDITIONS
 *
 * See http://www.atomikos.com/Main/WhichLicenseApplies for details.
 */

package com.atomikos.icatch.imp;

import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import com.atomikos.datasource.RecoverableResource;
import com.atomikos.icatch.config.Configuration;
import com.atomikos.icatch.event.transaction.TransactionHeuristicEvent;
import com.atomikos.logging.Logger;
import com.atomikos.logging.LoggerFactory;
import com.atomikos.publish.EventPublisher;
import com.atomikos.recovery.LogReadException;
import com.atomikos.recovery.PendingTransactionRecord;
import com.atomikos.recovery.RecoveryLog;
import com.atomikos.recovery.TxState;
import com.atomikos.thread.TaskManager;
import com.atomikos.timing.AlarmTimer;
import com.atomikos.timing.AlarmTimerListener;
import com.atomikos.timing.PooledAlarmTimer;

public class RecoveryDomainService {

	private static final Logger LOGGER = LoggerFactory.createLogger(RecoveryDomainService.class);
    
	private RecoveryLog recoveryLog;
	private boolean stopped; // for case 189603: avoid API-based recovery attempts after shutdown

	public RecoveryDomainService(RecoveryLog recoveryLog) {
		this.recoveryLog = recoveryLog;
	}

	private long maxTimeout;
	private PooledAlarmTimer recoveryTimer;
	private String recoveryDomainName;

	public void init() {

	    long recoveryDelay = Configuration.getConfigProperties().getRecoveryDelay();
	    setMaxTimeout(Configuration.getConfigProperties().getMaxTimeout());
	    recoveryDomainName = Configuration.getConfigProperties().getTmUniqueName();
	    recoveryTimer = new PooledAlarmTimer(recoveryDelay);

	    recoveryTimer.addAlarmTimerListener(new AlarmTimerListener() {

	        @Override
	        public void alarm(AlarmTimer timer) {				
	            performRecovery();
	        }
	    });			
	    TaskManager.SINGLETON.executeTask(recoveryTimer);
	  
	}

    public void setMaxTimeout(long maxTimeout) {
		this.maxTimeout = maxTimeout;
	}

	protected synchronized boolean performRecovery() {
		boolean perform = !stopped && recoveryLog.isActive();
		if (perform) { 		
			try {
				boolean allOk = true;
				long startOfRecovery = System.currentTimeMillis();
				Set resourcesToRecover = getResourcesForRecovery();
				Collection indoubtCoordinators = recoveryLog.getIndoubtTransactionRecords();
				Collection foreignIndoubtCoordinators = extractForeignRecords(indoubtCoordinators);
				Collection foreignCoordinatorsForHeuristicAbort = extractForeignIndoubtCoordinatorsForHeuristicAbort(foreignIndoubtCoordinators, startOfRecovery);
				Collection expiredCommittingCoordinators = recoveryLog.getExpiredPendingCommittingTransactionRecordsAt(startOfRecovery);
			
				for (RecoverableResource recoverableResource : resourcesToRecover) {
					try {
						allOk = allOk && recoverableResource.recover(startOfRecovery, expiredCommittingCoordinators, foreignIndoubtCoordinators);
					} catch (Throwable e) {
						allOk = false;
						LOGGER.logError(e.getMessage(), e);
					}
				}
				
				Collection recordsToDelete = new HashSet<>();
				if (allOk) {
				    recordsToDelete.addAll(expiredCommittingCoordinators);
				    Collection expiredNativeIndoubtCoordinators = extractNativeIndoubtCoordinatorsExpiredSince(startOfRecovery - maxTimeout, indoubtCoordinators);
				    recordsToDelete.addAll(expiredNativeIndoubtCoordinators);
				}
				recordsToDelete.addAll(foreignCoordinatorsForHeuristicAbort);
				recoveryLog.forgetTransactionRecords(recordsToDelete);
				
			} catch (Throwable e) {
				LOGGER.logError(e.getMessage(), e);
			}
		}
		return perform;
	}


    private Collection extractNativeIndoubtCoordinatorsExpiredSince(long momentInThePast,
            Collection collection) {
        return PendingTransactionRecord.collectLineages(
                (PendingTransactionRecord r) -> r.isLocalRoot(recoveryDomainName) && !r.isForeignInDomain(recoveryDomainName) && r.expires < momentInThePast && r.state == TxState.IN_DOUBT ,
                collection);
    }

    private Collection extractForeignRecords(
            Collection collection) {
        return PendingTransactionRecord.collectLineages(
                (PendingTransactionRecord r) -> r.isForeignInDomain(recoveryDomainName), 
                collection);
    }

    private Collection extractForeignIndoubtCoordinatorsForHeuristicAbort(
            Collection foreignIndoubtCoordinators, long startOfRecovery) {
        HashSet ret = new HashSet<>();
        Iterator it = foreignIndoubtCoordinators.iterator();
        while (it.hasNext()) {
            PendingTransactionRecord record = it.next();
            if (record.expires + maxTimeout < startOfRecovery) {
                if (record.allowsHeuristicTermination(recoveryDomainName)) {
                    ret.add(record);           
                } else {
                    //pending expired in-doubt => generate warning
                    TransactionHeuristicEvent event = new TransactionHeuristicEvent(record.id, record.superiorId, TxState.IN_DOUBT);
                    EventPublisher.INSTANCE.publish(event);
                }
            }
            for (PendingTransactionRecord entry : ret) {
                foreignIndoubtCoordinators.remove(entry); //remove - so presumed abort will terminate this one
                PendingTransactionRecord.removeAllDescendants(entry, foreignIndoubtCoordinators); //make sure that local descendants also abort
                TransactionHeuristicEvent event = new TransactionHeuristicEvent(record.id, record.superiorId, TxState.HEUR_ABORTED);
                EventPublisher.INSTANCE.publish(event);
            }
        }
        return ret;
    }


    private Set getResourcesForRecovery() {
	    Collection resources = null;
        resources = Configuration.getResources();
	    return filterDuplicates(resources); //cf case 170618
    }

    private Set filterDuplicates(Collection resources) {
       return new HashSet(resources);
    }

    public synchronized void stop() {
		if (recoveryTimer != null) {
			recoveryTimer.stopTimer();
			recoveryTimer = null;
		}
		stopped = true; 
	}
    
    /**
     * 
     * @return False if nothing more to do.
     */
    public synchronized boolean hasPendingParticipantsFromLastRecoveryScan() {
        if (!recoveryLog.isActive()) {
            // another instance has taken over => we're off the hook
            return false;
        }
        for (RecoverableResource res : getResourcesForRecovery()) {
			if (res.hasPendingParticipantsFromLastRecoveryScan()) {
				return true;
			}
		}
		return false;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy