Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

optimize: check relation of before status and after status when updating global session #4629

Merged
merged 21 commits into from Jun 10, 2022
Merged
3 changes: 3 additions & 0 deletions changes/en-us/2.0.0.md
Expand Up @@ -23,6 +23,8 @@ The version is updated as follows:

### optimize:
- [[#4567](https://github.com/seata/seata/pull/4567)] Support where method condition(find_in_set)
- [[#4629](https://github.com/seata/seata/pull/4629)] check relation of before status and after status when updating global session


### test:
- [[#1234](https://github.com/seata/seata/pull/1234)] Please delete the sample later
Expand All @@ -34,6 +36,7 @@ Thanks to these contributors for their code commits. Please report an unintended

- [slievrly](https://github.com/slievrly)
- [doubleDimple](https://github.com/doubleDimple)
- [Bughue](https://github.com/Bughue)

Also, we receive many valuable issues, questions and advices from our community. Thanks for you all.

Expand Down
2 changes: 2 additions & 0 deletions changes/zh-cn/2.0.0.md
Expand Up @@ -23,6 +23,7 @@ Seata 是一款开源的分布式事务解决方案,提供高性能和简单

### optimize:
- [[#4567](https://github.com/seata/seata/pull/4567)] 支持where条件带函数find_in_set支持
- [[#4629](https://github.com/seata/seata/pull/4629)] 更新globalsession状态时检查更改前后的对应关系正确性

### test:
- [[#1234](https://github.com/seata/seata/pull/1234)] 样例,后续请删除
Expand All @@ -34,6 +35,7 @@ Seata 是一款开源的分布式事务解决方案,提供高性能和简单

- [slievrly](https://github.com/slievrly)
- [doubleDimple](https://github.com/doubleDimple)
- [Bughue](https://github.com/Bughue)

同时,我们收到了社区反馈的很多有价值的issue和建议,非常感谢大家。

Expand Down
Expand Up @@ -756,7 +756,7 @@ public void queueToRetryCommit() throws TransactionException {
public void queueToRetryRollback() throws TransactionException {
this.addSessionLifecycleListener(SessionHolder.getRetryRollbackingSessionManager());
GlobalStatus currentStatus = this.getStatus();
if (SessionHelper.isTimeoutGlobalStatus(currentStatus)) {
if (SessionStatusValidator.isTimeoutGlobalStatus(currentStatus)) {
this.setStatus(GlobalStatus.TimeoutRollbackRetrying);
} else {
this.setStatus(GlobalStatus.RollbackRetrying);
Expand Down
11 changes: 3 additions & 8 deletions server/src/main/java/io/seata/server/session/SessionHelper.java
Expand Up @@ -167,7 +167,7 @@ public static void endRollbacked(GlobalSession globalSession, boolean retryGloba
GlobalStatus currentStatus = globalSession.getStatus();
boolean retryBranch =
currentStatus == GlobalStatus.TimeoutRollbackRetrying || currentStatus == GlobalStatus.RollbackRetrying;
if (isTimeoutGlobalStatus(currentStatus)) {
if (SessionStatusValidator.isTimeoutGlobalStatus(currentStatus)) {
globalSession.changeGlobalStatus(GlobalStatus.TimeoutRollbacked);
} else {
globalSession.changeGlobalStatus(GlobalStatus.Rollbacked);
Expand All @@ -192,7 +192,7 @@ public static void endRollbacked(GlobalSession globalSession, boolean retryGloba
*/
public static void endRollbackFailed(GlobalSession globalSession, boolean retryGlobal) throws TransactionException {
GlobalStatus currentStatus = globalSession.getStatus();
if (isTimeoutGlobalStatus(currentStatus)) {
if (SessionStatusValidator.isTimeoutGlobalStatus(currentStatus)) {
globalSession.changeGlobalStatus(GlobalStatus.TimeoutRollbackFailed);
} else {
globalSession.changeGlobalStatus(GlobalStatus.RollbackFailed);
Expand All @@ -202,12 +202,7 @@ public static void endRollbackFailed(GlobalSession globalSession, boolean retryG
MetricsPublisher.postSessionDoneEvent(globalSession, retryGlobal, false);
}

public static boolean isTimeoutGlobalStatus(GlobalStatus status) {
return status == GlobalStatus.TimeoutRollbacked
|| status == GlobalStatus.TimeoutRollbackFailed
|| status == GlobalStatus.TimeoutRollbacking
|| status == GlobalStatus.TimeoutRollbackRetrying;
}


/**
* Foreach global sessions.
Expand Down
@@ -0,0 +1,87 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.server.session;

import io.seata.core.model.GlobalStatus;

/**
* The type change status validator.
*
* @author Bughue
*/
public class SessionStatusValidator {

/**
* is timeout global status
*
* @param status the global session
*/
public static boolean isTimeoutGlobalStatus(GlobalStatus status) {
return status == GlobalStatus.TimeoutRollbacked
|| status == GlobalStatus.TimeoutRollbackFailed
|| status == GlobalStatus.TimeoutRollbacking
|| status == GlobalStatus.TimeoutRollbackRetrying;
}

/**
* is rollback global status
*
* @param status the global session
*/
public static boolean isRollbackGlobalStatus(GlobalStatus status) {
return status == GlobalStatus.Rollbacking
|| status == GlobalStatus.RollbackRetrying
|| status == GlobalStatus.Rollbacked
|| status == GlobalStatus.RollbackFailed
|| status == GlobalStatus.RollbackRetryTimeout;
}

/**
* is commit global status
*
* @param status the global session
*/
public static boolean isCommitGlobalStatus(GlobalStatus status) {
return status == GlobalStatus.Committing
|| status == GlobalStatus.AsyncCommitting
|| status == GlobalStatus.CommitRetrying
|| status == GlobalStatus.Committed
|| status == GlobalStatus.CommitFailed
|| status == GlobalStatus.CommitRetryTimeout;
}

/**
* check the relation of before status and after status
*
* @param before the global session
* @param after the global session
*/
public static boolean validateUpdateStatus(GlobalStatus before, GlobalStatus after) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A state flow control can be designed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A state flow control can be designed.

to prevent too many state updates at the same time?

if (isTimeoutGlobalStatus(before) && isCommitGlobalStatus(after)) {
return false;
}
if (isCommitGlobalStatus(before) && isTimeoutGlobalStatus(after)) {
return false;
}
if (isRollbackGlobalStatus(before) && isCommitGlobalStatus(after)) {
return false;
}
if (isCommitGlobalStatus(before) && isRollbackGlobalStatus(after)) {
return false;
}
return true;
}
}
Expand Up @@ -28,6 +28,7 @@
import java.util.stream.Collectors;
import io.seata.config.Configuration;
import io.seata.config.ConfigurationFactory;
import io.seata.server.session.SessionStatusValidator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.ImmutableMap;
Expand Down Expand Up @@ -326,6 +327,12 @@ private boolean updateGlobalTransactionDO(GlobalTransactionDO globalTransactionD
jedis.unwatch();
return true;
}
GlobalStatus before = GlobalStatus.get(Integer.parseInt(previousStatus));
GlobalStatus after = GlobalStatus.get(globalTransactionDO.getStatus());
if (!SessionStatusValidator.validateUpdateStatus(before, after)) {
throw new StoreException("Illegal changing of global status, update global transaction failed."
+ " beforeStatus[" + before.name() + "] cannot be changed to afterStatus[" + after.name() + "]");
}
Bughue marked this conversation as resolved.
Show resolved Hide resolved

String previousGmtModified = statusAndGmtModified.get(1);
Transaction multi = jedis.multi();
Expand Down
@@ -0,0 +1,45 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.server.session;
funky-eyes marked this conversation as resolved.
Show resolved Hide resolved

import io.seata.core.model.GlobalStatus;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

/**
* the type change status validator test
*
* @author Bughue
*/
@SpringBootTest
public class SessionStatusValidatorTest {

@Test
public void testValidateUpdateStatus(){
Assertions.assertEquals(true, SessionStatusValidator.validateUpdateStatus(GlobalStatus.Begin, GlobalStatus.Committing));
Assertions.assertEquals(true, SessionStatusValidator.validateUpdateStatus(GlobalStatus.Committing, GlobalStatus.Committed));

Assertions.assertEquals(false, SessionStatusValidator.validateUpdateStatus(GlobalStatus.Committing, GlobalStatus.TimeoutRollbacking));
Assertions.assertEquals(false, SessionStatusValidator.validateUpdateStatus(GlobalStatus.TimeoutRollbacking, GlobalStatus.Committing));
Assertions.assertEquals(false, SessionStatusValidator.validateUpdateStatus(GlobalStatus.Committing, GlobalStatus.Rollbacking));
Assertions.assertEquals(false, SessionStatusValidator.validateUpdateStatus(GlobalStatus.Rollbacking, GlobalStatus.Committing));

Assertions.assertEquals(false, SessionStatusValidator.validateUpdateStatus(GlobalStatus.Committed, GlobalStatus.Rollbacked));
Assertions.assertEquals(false, SessionStatusValidator.validateUpdateStatus(GlobalStatus.Committed, GlobalStatus.TimeoutRollbacking));

}
}