-
Notifications
You must be signed in to change notification settings - Fork 8.8k
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
feature: Implement scheduled handling for end status transaction #7133
Conversation
GlobalStatus.TimeoutRollbacked, | ||
GlobalStatus.Rollbacked |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
由于rollback的概率是非常小的,并且rollbacked和TimeoutRollbacked的状态残留的概率更低,故这两种状态是特意放置在server重启时进行处理。
Since the probability of rollback is very low, and the chances of the rollbacked and TimeoutRollbacked states persisting are even lower, these two states are specifically handled during server restart.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
由于rollback的概率是非常小的,并且rollbacked和TimeoutRollbacked的状态残留的概率更低,故这两种状态是特意放置在server重启时进行处理。 Since the probability of rollback is very low, and the chances of the rollbacked and TimeoutRollbacked states persisting are even lower, these two states are specifically handled during server restart.
Thank you for your comment :)
I didn’t fully understand.
Currently, in DefaultCoordinator, there is compensation handling for the committed state, but there is no compensation handling for the TimeoutRollbacked and Rollbacked states.
Based on the comment above, I added these two cases to the array for management.
If any changes are needed, could you please provide additional explanations?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the probability of a transaction rollback occurring is very low, the likelihood of the rollbacked intermediate state persisting is even lower. After the rollbacking state, it transitions to rollbacked and is then deleted. These two actions are separated by just one I/O operation, meaning that once the status is successfully updated to rollbacked, the transaction will be immediately deleted, unless a network or storage-side exception occurs during deletion, preventing the transaction from being deleted. Otherwise, there will be no rollbacked transactions left.
The commit of a transaction is a high-probability event, so the likelihood of the committed state persisting is much higher than that of rollbacked. To prevent storage-side pressure and meet performance requirements, committed transactions need to be processed during the server's runtime, while the low-probability rollbacked transactions are handled during the server startup.
Since both of these probabilities are very low, the final result is that they are both deleted. If you think it is necessary to handle rollbacked transactions during runtime, I suggest that you create a separate scheduled task to batch process transactions in the rollbacked and committed states, rather than blocking transactions that urgently need to be rolled back and retried.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you think it is necessary to handle rollbacked transactions during runtime, I suggest that you create a separate scheduled task to batch process transactions in the rollbacked and committed states, rather than blocking transactions that urgently need to be rolled back and retried.
Thank you for your suggestion!
The idea you proposed is very interesting, and I’m planning to work on it. :)
Additionally, I’ve posted a message on the Seata project’s issue list and mailing list regarding this topic.
I’ll leave the links below—could you kindly take a look when you have a chance?
- https://lists.apache.org/thread/xlkshkqs5v2hflp10jgz418kos8vwrsc
- optimize seata-client TM&RM netty eventloop #6774
Thanks again! 🚀
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in fc28796 !
PTAL ⚡️
In the issue description, it was mentioned that the state remains unchanged unless the server is So, aside from handling it specifically during a server restart, I believe additional measures should be taken. WDYT? |
Do you use the DingTalk app? |
Unfortunately, no.. However, if using DingTalk allows for better communication, I’m willing to install it! |
Of course, email is also a great communication channel, but I would like to invite you to join the Seata contributors' DingTalk group if you're willing to install it. If you install it, please send your DingTalk ID to my email, and I will invite you to join the Seata contributors' group. |
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## 2.x #7133 +/- ##
============================================
- Coverage 52.20% 52.17% -0.03%
- Complexity 6808 6811 +3
============================================
Files 1154 1154
Lines 41058 41112 +54
Branches 4813 4818 +5
============================================
+ Hits 21434 21451 +17
- Misses 17585 17621 +36
- Partials 2039 2040 +1
|
* @return time to dead session. if not greater than 0, then deadSession | ||
*/ | ||
public long timeToDeadSession() { | ||
if (this.status == GlobalStatus.Rollbacked || this.status == GlobalStatus.TimeoutRollbacked) { | ||
return beginTime - System.currentTimeMillis(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The beginTime does not change after the creation of the global session, which will cause any global session to be added to the needDoRollbackedSessions list.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I implemented it this way to set a slightly shorter interval for sessions in the end
state.
If possible, could you please provide a more detailed explanation? 🙂
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The beginTime is set when the transaction is created. When the Seata server's business thread changes the transaction status from "Rollbacking" to "Rollbacked," your timeToDeadSession method directly subtracts the current timestamp from beginTime, which will inevitably result in a negative value. As a result, the business thread will delete the transaction, and your scheduled task will also delete the transaction, leading to a concurrency issue and thread safety problem. I believe that beginTime should be increased by the corresponding transaction timeout, plus an additional buffer time to ensure thread safety.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then, would it be appropriate to create a RETRY_DEAD_THRESHOLD
specifically for the end state?
Of course, this timeToDeadSession method is more relevant for ongoing statuses like "rollbacking" or "committing." For transactions in the "end" status, I believe the interval can be shortened.
As suggested in the discussion, since we can reduce the interval in the end state, I believe decreasing the size of RETRY_DEAD_THRESHOLD
could be a reasonable approach.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The beginTime is set when the transaction is created. When the Seata server's business thread changes the transaction status from "Rollbacking" to "Rollbacked," your timeToDeadSession method directly subtracts the current timestamp from beginTime, which will inevitably result in a negative value. As a result, the business thread will delete the transaction, and your scheduled task will also delete the transaction, leading to a concurrency issue and thread safety problem. I believe that beginTime should be increased by the corresponding transaction timeout, plus an additional buffer time to ensure thread safety.
Since our comments were written at the same time, I didn’t see yours when I was writing mine. 😅
Thank you for the additional explanation!
In that case, I will adjust the execution interval for sessions in the end state
to be shorter using an if
condition.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RETRY_DEAD_THRESHOLD
I think this can be done this way
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RETRY_DEAD_THRESHOLD
I think this can be done this way
Then no changes required?
Then, would it be appropriate to create a RETRY_DEAD_THRESHOLD specifically for the end state?
or you mean this way?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Create a corresponding RETRY_DEAD_THRESHOLD for the end state
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in 997775c !
PTAL ⚡️
|
||
private final GlobalStatus[] retryCommittingStatuses = new GlobalStatus[] {GlobalStatus.CommitRetrying, GlobalStatus.Committed}; | ||
|
||
private final GlobalStatus[] rollbackingStatuses = new GlobalStatus[] {GlobalStatus.Rollbacking}; | ||
private final GlobalStatus[] committingStatuses = new GlobalStatus[] {GlobalStatus.Committing}; | ||
private final GlobalStatus[] rollbackedStatuses = new GlobalStatus[] {GlobalStatus.Rollbacked, GlobalStatus.TimeoutRollbacked}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not process transactions in other end states together?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will include the CommitFailed
, RollbackFailed
, TimeoutRollbackFailed
, Finished
, CommitRetryTimeout
, and RollbackRetryTimeout
states :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in a9ec30f !
server/src/main/java/org/apache/seata/server/coordinator/DefaultCoordinator.java
Outdated
Show resolved
Hide resolved
server/src/main/java/org/apache/seata/server/coordinator/DefaultCoordinator.java
Outdated
Show resolved
Hide resolved
/** | ||
* The constant ROLLBACKED_RETRY_PERIOD. | ||
*/ | ||
String ROLLBACKED_RETRY_PERIOD = RECOVERY_PREFIX + "rollbackedRetryPeriod"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://github.com/apache/incubator-seata/blob/2.x/script/config-center/config.txt
https://github.com/apache/incubator-seata/blob/2.x/server/src/main/resources/application.example.yml
https://github.com/apache/incubator-seata/blob/2.x/server/src/main/resources/application.raft.example.yml
https://github.com/apache/incubator-seata/blob/2.x/seata-spring-autoconfigure/seata-spring-autoconfigure-server/src/main/java/org/apache/seata/spring/boot/autoconfigure/properties/server/ServerProperties.java
This configuration item needs to be supplemented in the above file, and I think it is no longer appropriate to name this configuration starting with rollback
, because it is a configuration name for handling transactions in the end
state..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in 932c51f !
server/src/main/java/org/apache/seata/server/session/GlobalSession.java
Outdated
Show resolved
Hide resolved
server/src/main/java/org/apache/seata/server/coordinator/DefaultCoordinator.java
Outdated
Show resolved
Hide resolved
|
||
// commit retry timeout event | ||
SessionHelper.endRollbackFailed(rollbackedSession, true, true); | ||
|
||
//The function of this 'return' is 'continue'. | ||
return; | ||
} | ||
core.doGlobalRollback(rollbackedSession, true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should no longer perform rollback processing. For different states of transactions in the end phase, call the SessionHelper.endRollbacked
and SessionHelper.endCommitted
methods respectively.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in a7a9e52 !
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When you have time, I would appreciate it if you could review this comment and the issue below.
…sion.java Co-authored-by: funkye <[email protected]>
…ltCoordinator.java Co-authored-by: funkye <[email protected]>
…ltCoordinator.java Co-authored-by: funkye <[email protected]>
common/src/main/java/org/apache/seata/common/DefaultValues.java
Outdated
Show resolved
Hide resolved
Co-authored-by: funkye <[email protected]>
Co-authored-by: funkye <[email protected]>
server/src/main/java/org/apache/seata/server/coordinator/DefaultCoordinator.java
Outdated
Show resolved
Hide resolved
private void handleEndStateSession(GlobalSession globalSession) throws TransactionException { | ||
GlobalStatus status = globalSession.getStatus(); | ||
|
||
if (status == GlobalStatus.CommitFailed) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The commit state is not limited to just one, and the rollback state is the same. I suggest putting the handling of various end states directly in the SessionHelper, and then calling it from handleEndStateSession. The only input parameter needed is a globalsession
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I understand it, instead of directly managing the transaction end states with conditional statements in handleEndStateSession
, the responsibility
is being delegated to SessionHelper
.
Is my understanding correct?
Below is the implementation based on my understanding.
private void handleEndStateSession(GlobalSession globalSession) throws TransactionException {
SessionHelper.processEndState(globalSession);
}
The state values are just examples.
public class SessionHelper {
public static void processEndState(GlobalSession globalSession) throws TransactionException {
GlobalStatus status = globalSession.getStatus();
switch (status) {
case Committed:
case CommitSuccess:
endCommitted(globalSession, false);
break;
case CommitFailed:
case CommitPending:
endCommitted(globalSession, true);
break;
case Rollbacked:
case RollbackSuccess:
endRollbacked(globalSession, false);
break;
case RollbackFailed:
case RollbackPending:
endRollbacked(globalSession, true);
break;
default:
log.warn("Unknown transaction state: {}", status);
}
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I think it should be this way, encapsulating the processing logic in the SessionHelper to hide the internal implementation from the outside.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in 5837ae6 !
server/src/main/java/org/apache/seata/server/coordinator/DefaultCoordinator.java
Outdated
Show resolved
Hide resolved
server/src/main/java/org/apache/seata/server/coordinator/DefaultCoordinator.java
Outdated
Show resolved
Hide resolved
…ltCoordinator.java Co-authored-by: funkye <[email protected]>
…ltCoordinator.java Co-authored-by: funkye <[email protected]>
…ltCoordinator.java Co-authored-by: funkye <[email protected]>
You can see in the comment that the word Please take a look and provide your feedback. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe the committed
state should be handled within the end
state task, and transactions in a failed state should not be retried. Failed transactions need to be addressed by the operations team, rather than being handled by scheduled tasks.
server/src/main/java/org/apache/seata/server/session/SessionHelper.java
Outdated
Show resolved
Hide resolved
server/src/main/java/org/apache/seata/server/session/SessionHelper.java
Outdated
Show resolved
Hide resolved
server/src/main/java/org/apache/seata/server/coordinator/DefaultCoordinator.java
Outdated
Show resolved
Hide resolved
server/src/main/java/org/apache/seata/server/coordinator/DefaultCoordinator.java
Outdated
Show resolved
Hide resolved
…ltCoordinator.java Co-authored-by: funkye <[email protected]>
…lper.java Co-authored-by: funkye <[email protected]>
…ltCoordinator.java Co-authored-by: funkye <[email protected]>
…lper.java Co-authored-by: funkye <[email protected]>
server/src/main/java/org/apache/seata/server/session/GlobalSession.java
Outdated
Show resolved
Hide resolved
That sounds like a good point. However, while going through your comment, a few questions came to mind:
|
For the reasons I mentioned earlier:
In the above points or some examples I did not mention, transactions are not deleted in the end state, leaving residual data. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please take a look :)
If everything looks good, please proceed with committing the suggestion.
server/src/main/java/org/apache/seata/server/coordinator/DefaultCoordinator.java
Outdated
Show resolved
Hide resolved
server/src/main/java/org/apache/seata/server/session/SessionHelper.java
Outdated
Show resolved
Hide resolved
server/src/main/java/org/apache/seata/server/session/GlobalSession.java
Outdated
Show resolved
Hide resolved
…ltCoordinator.java Co-authored-by: Yongjun Hong <[email protected]>
…sion.java Co-authored-by: Yongjun Hong <[email protected]>
…lper.java Co-authored-by: Yongjun Hong <[email protected]>
Is there anything else left to do? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Ⅰ. Describe what this PR did
Ⅱ. Does this pull request fix one issue?
fixes #6875
Ⅲ. Why don't you add test cases (unit test/integration test)?
Ⅳ. Describe how to verify it
Ⅴ. Special notes for reviews