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;
}
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();
}
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();
}
}
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. ");
}
}
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;
}
Aggregations