Search in sources :

Example 1 with GlobalTransactionEvent

use of io.seata.core.event.GlobalTransactionEvent in project seata by seata.

the class DefaultCore method doGlobalRollback.

@Override
public boolean doGlobalRollback(GlobalSession globalSession, boolean retrying) throws TransactionException {
    boolean success = true;
    // start rollback event
    eventBus.post(new GlobalTransactionEvent(globalSession.getTransactionId(), GlobalTransactionEvent.ROLE_TC, globalSession.getTransactionName(), globalSession.getApplicationId(), globalSession.getTransactionServiceGroup(), globalSession.getBeginTime(), null, globalSession.getStatus()));
    if (globalSession.isSaga()) {
        success = getCore(BranchType.SAGA).doGlobalRollback(globalSession, retrying);
    } else {
        Boolean result = SessionHelper.forEach(globalSession.getReverseSortedBranches(), branchSession -> {
            BranchStatus currentBranchStatus = branchSession.getStatus();
            if (currentBranchStatus == BranchStatus.PhaseOne_Failed) {
                globalSession.removeBranch(branchSession);
                return CONTINUE;
            }
            try {
                BranchStatus branchStatus = branchRollback(globalSession, branchSession);
                switch(branchStatus) {
                    case PhaseTwo_Rollbacked:
                        globalSession.removeBranch(branchSession);
                        LOGGER.info("Rollback branch transaction successfully, xid = {} branchId = {}", globalSession.getXid(), branchSession.getBranchId());
                        return CONTINUE;
                    case PhaseTwo_RollbackFailed_Unretryable:
                        SessionHelper.endRollbackFailed(globalSession);
                        LOGGER.info("Rollback branch transaction fail and stop retry, xid = {} branchId = {}", globalSession.getXid(), branchSession.getBranchId());
                        return false;
                    default:
                        LOGGER.info("Rollback branch transaction fail and will retry, xid = {} branchId = {}", globalSession.getXid(), branchSession.getBranchId());
                        if (!retrying) {
                            globalSession.queueToRetryRollback();
                        }
                        return false;
                }
            } catch (Exception ex) {
                StackTraceLogger.error(LOGGER, ex, "Rollback branch transaction exception, xid = {} branchId = {} exception = {}", new String[] { globalSession.getXid(), String.valueOf(branchSession.getBranchId()), ex.getMessage() });
                if (!retrying) {
                    globalSession.queueToRetryRollback();
                }
                throw new TransactionException(ex);
            }
        });
        // Return if the result is not null
        if (result != null) {
            return result;
        }
        // In db mode, there is a problem of inconsistent data in multiple copies, resulting in new branch
        // transaction registration when rolling back.
        // 1. New branch transaction and rollback branch transaction have no data association
        // 2. New branch transaction has data association with rollback branch transaction
        // The second query can solve the first problem, and if it is the second problem, it may cause a rollback
        // failure due to data changes.
        GlobalSession globalSessionTwice = SessionHolder.findGlobalSession(globalSession.getXid());
        if (globalSessionTwice != null && globalSessionTwice.hasBranch()) {
            LOGGER.info("Rollbacking global transaction is NOT done, xid = {}.", globalSession.getXid());
            return false;
        }
    }
    if (success) {
        SessionHelper.endRollbacked(globalSession);
        // rollbacked event
        eventBus.post(new GlobalTransactionEvent(globalSession.getTransactionId(), GlobalTransactionEvent.ROLE_TC, globalSession.getTransactionName(), globalSession.getApplicationId(), globalSession.getTransactionServiceGroup(), globalSession.getBeginTime(), System.currentTimeMillis(), globalSession.getStatus()));
        LOGGER.info("Rollback global transaction successfully, xid = {}.", globalSession.getXid());
    }
    return success;
}
Also used : TransactionException(io.seata.core.exception.TransactionException) GlobalSession(io.seata.server.session.GlobalSession) BranchStatus(io.seata.core.model.BranchStatus) GlobalTransactionEvent(io.seata.core.event.GlobalTransactionEvent) TransactionException(io.seata.core.exception.TransactionException) NotSupportYetException(io.seata.common.exception.NotSupportYetException)

Example 2 with GlobalTransactionEvent

use of io.seata.core.event.GlobalTransactionEvent in project seata by seata.

the class DefaultCore method begin.

@Override
public String begin(String applicationId, String transactionServiceGroup, String name, int timeout) throws TransactionException {
    GlobalSession session = GlobalSession.createGlobalSession(applicationId, transactionServiceGroup, name, timeout);
    MDC.put(RootContext.MDC_KEY_XID, session.getXid());
    session.addSessionLifecycleListener(SessionHolder.getRootSessionManager());
    session.begin();
    // transaction start event
    eventBus.post(new GlobalTransactionEvent(session.getTransactionId(), GlobalTransactionEvent.ROLE_TC, session.getTransactionName(), applicationId, transactionServiceGroup, session.getBeginTime(), null, session.getStatus()));
    return session.getXid();
}
Also used : GlobalSession(io.seata.server.session.GlobalSession) GlobalTransactionEvent(io.seata.core.event.GlobalTransactionEvent)

Example 3 with GlobalTransactionEvent

use of io.seata.core.event.GlobalTransactionEvent in project seata by seata.

the class DefaultCoreForEventBusTest method test.

@Test
public void test() throws IOException, TransactionException, InterruptedException {
    class GlobalTransactionEventSubscriber {

        private final Map<GlobalStatus, AtomicInteger> eventCounters;

        public Map<GlobalStatus, AtomicInteger> getEventCounters() {
            return eventCounters;
        }

        public GlobalTransactionEventSubscriber() {
            this.eventCounters = new ConcurrentHashMap<>();
        }

        @Subscribe
        public void processGlobalTransactionEvent(GlobalTransactionEvent event) {
            AtomicInteger counter = eventCounters.computeIfAbsent(event.getStatus(), status -> new AtomicInteger(0));
            counter.addAndGet(1);
        }
    }
    SessionHolder.init(null);
    RemotingServer remotingServer = new DefaultCoordinatorTest.MockServerMessageSender();
    DefaultCoordinator coordinator = new DefaultCoordinator(remotingServer);
    coordinator.init();
    try {
        DefaultCore core = new DefaultCore(remotingServer);
        GlobalTransactionEventSubscriber subscriber = new GlobalTransactionEventSubscriber();
        // avoid transactional interference from other unit tests
        Thread.sleep(1500);
        EventBusManager.get().register(subscriber);
        // start a transaction
        String xid = core.begin("test_app_id", "default_group", "test_tran_name", 30000);
        Assertions.assertEquals(1, subscriber.getEventCounters().get(GlobalStatus.Begin).get());
        // commit this transaction
        core.commit(xid);
        // we need sleep for a short while because default canBeCommittedAsync() is true
        Thread.sleep(1000);
        // check
        Assertions.assertEquals(1, subscriber.getEventCounters().get(GlobalStatus.AsyncCommitting).get());
        Assertions.assertEquals(1, subscriber.getEventCounters().get(GlobalStatus.Committed).get());
        // start another new transaction
        xid = core.begin("test_app_id", "default_group", "test_tran_name2", 30000);
        Assertions.assertEquals(2, subscriber.getEventCounters().get(GlobalStatus.Begin).get());
        core.rollback(xid);
        // check
        Assertions.assertEquals(1, subscriber.getEventCounters().get(GlobalStatus.Rollbacking).get());
        Assertions.assertEquals(1, subscriber.getEventCounters().get(GlobalStatus.Rollbacked).get());
        // start more one new transaction for test timeout and let this transaction immediately timeout
        xid = core.begin("test_app_id", "default_group", "test_tran_name3", 0);
        // sleep for check ->  DefaultCoordinator.timeoutCheck
        Thread.sleep(1000);
        // at lease retry once because DefaultCoordinator.timeoutCheck is 1 second
        Assertions.assertTrue(subscriber.getEventCounters().get(GlobalStatus.TimeoutRollbacking).get() >= 1);
    } finally {
        coordinator.destroy();
        SessionHolder.destroy();
    }
}
Also used : GlobalStatus(io.seata.core.model.GlobalStatus) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) DefaultCore(io.seata.server.coordinator.DefaultCore) DefaultCoordinator(io.seata.server.coordinator.DefaultCoordinator) RemotingServer(io.seata.core.rpc.RemotingServer) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) Map(java.util.Map) GlobalTransactionEvent(io.seata.core.event.GlobalTransactionEvent) Test(org.junit.jupiter.api.Test) DefaultCoordinatorTest(io.seata.server.coordinator.DefaultCoordinatorTest)

Example 4 with GlobalTransactionEvent

use of io.seata.core.event.GlobalTransactionEvent in project seata by seata.

the class DefaultCoordinator method timeoutCheck.

/**
 * Timeout check.
 *
 * @throws TransactionException the transaction exception
 */
protected void timeoutCheck() throws TransactionException {
    Collection<GlobalSession> allSessions = SessionHolder.getRootSessionManager().allSessions();
    if (CollectionUtils.isEmpty(allSessions)) {
        return;
    }
    if (allSessions.size() > 0 && LOGGER.isDebugEnabled()) {
        LOGGER.debug("Global transaction timeout check begin, size: {}", allSessions.size());
    }
    SessionHelper.forEach(allSessions, globalSession -> {
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug(globalSession.getXid() + " " + globalSession.getStatus() + " " + globalSession.getBeginTime() + " " + globalSession.getTimeout());
        }
        boolean shouldTimeout = SessionHolder.lockAndExecute(globalSession, () -> {
            if (globalSession.getStatus() != GlobalStatus.Begin || !globalSession.isTimeout()) {
                return false;
            }
            globalSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager());
            globalSession.close();
            globalSession.changeStatus(GlobalStatus.TimeoutRollbacking);
            // transaction timeout and start rollbacking event
            eventBus.post(new GlobalTransactionEvent(globalSession.getTransactionId(), GlobalTransactionEvent.ROLE_TC, globalSession.getTransactionName(), globalSession.getApplicationId(), globalSession.getTransactionServiceGroup(), globalSession.getBeginTime(), null, globalSession.getStatus()));
            return true;
        });
        if (!shouldTimeout) {
            // The function of this 'return' is 'continue'.
            return;
        }
        LOGGER.info("Global transaction[{}] is timeout and will be rollback.", globalSession.getXid());
        globalSession.addSessionLifecycleListener(SessionHolder.getRetryRollbackingSessionManager());
        SessionHolder.getRetryRollbackingSessionManager().addGlobalSession(globalSession);
    });
    if (allSessions.size() > 0 && LOGGER.isDebugEnabled()) {
        LOGGER.debug("Global transaction timeout check end. ");
    }
}
Also used : GlobalSession(io.seata.server.session.GlobalSession) GlobalTransactionEvent(io.seata.core.event.GlobalTransactionEvent)

Example 5 with GlobalTransactionEvent

use of io.seata.core.event.GlobalTransactionEvent in project seata by seata.

the class DefaultCore method doGlobalCommit.

@Override
public boolean doGlobalCommit(GlobalSession globalSession, boolean retrying) throws TransactionException {
    boolean success = true;
    // start committing event
    eventBus.post(new GlobalTransactionEvent(globalSession.getTransactionId(), GlobalTransactionEvent.ROLE_TC, globalSession.getTransactionName(), globalSession.getApplicationId(), globalSession.getTransactionServiceGroup(), globalSession.getBeginTime(), null, globalSession.getStatus()));
    if (globalSession.isSaga()) {
        success = getCore(BranchType.SAGA).doGlobalCommit(globalSession, retrying);
    } else {
        Boolean result = SessionHelper.forEach(globalSession.getSortedBranches(), branchSession -> {
            // if not retrying, skip the canBeCommittedAsync branches
            if (!retrying && branchSession.canBeCommittedAsync()) {
                return CONTINUE;
            }
            BranchStatus currentStatus = branchSession.getStatus();
            if (currentStatus == BranchStatus.PhaseOne_Failed) {
                globalSession.removeBranch(branchSession);
                return CONTINUE;
            }
            try {
                BranchStatus branchStatus = getCore(branchSession.getBranchType()).branchCommit(globalSession, branchSession);
                switch(branchStatus) {
                    case PhaseTwo_Committed:
                        globalSession.removeBranch(branchSession);
                        return CONTINUE;
                    case PhaseTwo_CommitFailed_Unretryable:
                        if (globalSession.canBeCommittedAsync()) {
                            LOGGER.error("Committing branch transaction[{}], status: PhaseTwo_CommitFailed_Unretryable, please check the business log.", branchSession.getBranchId());
                            return CONTINUE;
                        } else {
                            SessionHelper.endCommitFailed(globalSession);
                            LOGGER.error("Committing global transaction[{}] finally failed, caused by branch transaction[{}] commit failed.", globalSession.getXid(), branchSession.getBranchId());
                            return false;
                        }
                    default:
                        if (!retrying) {
                            globalSession.queueToRetryCommit();
                            return false;
                        }
                        if (globalSession.canBeCommittedAsync()) {
                            LOGGER.error("Committing branch transaction[{}], status:{} and will retry later", branchSession.getBranchId(), branchStatus);
                            return CONTINUE;
                        } else {
                            LOGGER.error("Committing global transaction[{}] failed, caused by branch transaction[{}] commit failed, will retry later.", globalSession.getXid(), branchSession.getBranchId());
                            return false;
                        }
                }
            } catch (Exception ex) {
                StackTraceLogger.error(LOGGER, ex, "Committing branch transaction exception: {}", new String[] { branchSession.toString() });
                if (!retrying) {
                    globalSession.queueToRetryCommit();
                    throw new TransactionException(ex);
                }
            }
            return CONTINUE;
        });
        // Return if the result is not null
        if (result != null) {
            return result;
        }
        // do print log and return false
        if (globalSession.hasBranch() && !globalSession.canBeCommittedAsync()) {
            LOGGER.info("Committing global transaction is NOT done, xid = {}.", globalSession.getXid());
            return false;
        }
    }
    // If success and there is no branch, end the global transaction.
    if (success && globalSession.getBranchSessions().isEmpty()) {
        SessionHelper.endCommitted(globalSession);
        // committed event
        eventBus.post(new GlobalTransactionEvent(globalSession.getTransactionId(), GlobalTransactionEvent.ROLE_TC, globalSession.getTransactionName(), globalSession.getApplicationId(), globalSession.getTransactionServiceGroup(), globalSession.getBeginTime(), System.currentTimeMillis(), globalSession.getStatus()));
        LOGGER.info("Committing global transaction is successfully done, xid = {}.", globalSession.getXid());
    }
    return success;
}
Also used : TransactionException(io.seata.core.exception.TransactionException) BranchStatus(io.seata.core.model.BranchStatus) GlobalTransactionEvent(io.seata.core.event.GlobalTransactionEvent) TransactionException(io.seata.core.exception.TransactionException) NotSupportYetException(io.seata.common.exception.NotSupportYetException)

Aggregations

GlobalTransactionEvent (io.seata.core.event.GlobalTransactionEvent)9 GlobalSession (io.seata.server.session.GlobalSession)6 NotSupportYetException (io.seata.common.exception.NotSupportYetException)4 TransactionException (io.seata.core.exception.TransactionException)4 BranchStatus (io.seata.core.model.BranchStatus)4 BranchSession (io.seata.server.session.BranchSession)2 GlobalStatus (io.seata.core.model.GlobalStatus)1 RemotingServer (io.seata.core.rpc.RemotingServer)1 DefaultCoordinator (io.seata.server.coordinator.DefaultCoordinator)1 DefaultCoordinatorTest (io.seata.server.coordinator.DefaultCoordinatorTest)1 DefaultCore (io.seata.server.coordinator.DefaultCore)1 Map (java.util.Map)1 ConcurrentHashMap (java.util.concurrent.ConcurrentHashMap)1 AtomicInteger (java.util.concurrent.atomic.AtomicInteger)1 Test (org.junit.jupiter.api.Test)1