From afeb3631d8110495d929030fd4424018e1d44074 Mon Sep 17 00:00:00 2001 From: Jingliu Xiong <928124786@qq.com> Date: Sat, 16 Nov 2024 20:33:48 +0800 Subject: [PATCH 01/31] feature: add console transaction control --- .../seata/common/ConfigurationKeys.java | 10 + .../org/apache/seata/common/Constants.java | 5 + .../apache/seata/common/DefaultValues.java | 4 + .../apache/seata/common/result/Result.java | 2 + .../seata/common/result/SingleResult.java | 8 + .../static/console-fe/src/locales/en-us.ts | 8 + .../static/console-fe/src/locales/zh-cn.ts | 8 + .../pages/GlobalLockInfo/GlobalLockInfo.tsx | 92 +++-- .../pages/TransactionInfo/TransactionInfo.tsx | 338 +++++++++++++++--- .../console-fe/src/service/globalLockInfo.ts | 24 +- .../console-fe/src/service/transactionInfo.ts | 102 ++++++ .../exception/TransactionExceptionCode.java | 6 +- .../apache/seata/core/model/BranchStatus.java | 8 +- .../apache/seata/core/model/GlobalStatus.java | 22 +- .../seata/core/protocol/MessageType.java | 8 + .../transaction/BranchDeleteRequest.java | 92 +++++ .../transaction/BranchDeleteResponse.java | 15 + .../transaction/RMInboundHandler.java | 7 + .../core/rpc/netty/NettyRemotingServer.java | 1 + .../core/rpc/netty/RmNettyRemotingClient.java | 4 + .../client/RmBranchDeleteProcessor.java | 52 +++ .../api/fence/DefaultCommonFenceHandler.java | 7 + .../tx/api/fence/FenceHandler.java | 1 + .../java/org/apache/seata/rm/RMHandlerAT.java | 26 ++ .../java/org/apache/seata/rm/RMHandlerXA.java | 28 ++ .../org/apache/seata/rm/DefaultRMHandler.java | 7 + .../apache/seata/saga/rm/RMHandlerSaga.java | 7 + .../BranchDeleteRequestConvertor.java | 43 +++ .../BranchDeleteResponseConvertor.java | 63 ++++ .../manager/ProtobufConvertManager.java | 14 + .../transcation/branchDeleteRequest.proto | 39 ++ .../transcation/branchDeleteResponse.proto | 30 ++ .../protocol/transcation/messageType.proto | 8 + .../BranchDeleteRequestConvertorTest.java | 47 +++ .../BranchDeleteResponseConvertorTest.java | 53 +++ .../serializer/seata/MessageCodecFactory.java | 16 + .../transaction/BranchDeleteRequestCodec.java | 108 ++++++ .../BranchDeleteResponseCodec.java | 32 ++ .../BranchDeleteRequestCodecTest.java | 57 +++ .../BranchDeleteResponseCodecTest.java | 59 +++ .../aop/GlobalExceptionHandlerAdvice.java | 76 ++++ .../controller/BranchSessionController.java | 52 ++- .../controller/GlobalLockController.java | 30 ++ .../controller/GlobalSessionController.java | 75 ++++ .../console/exception/ConsoleException.java | 23 ++ .../console/impl/AbstractBranchService.java | 93 +++++ .../console/impl/AbstractGlobalService.java | 128 +++++++ .../console/impl/AbstractLockService.java | 28 ++ .../server/console/impl/AbstractService.java | 145 ++++++++ .../impl/db/BranchSessionDBServiceImpl.java | 3 +- .../impl/db/GlobalLockDBServiceImpl.java | 36 +- .../impl/db/GlobalSessionDBServiceImpl.java | 3 +- .../file/BranchSessionFileServiceImpl.java | 27 +- .../impl/file/GlobalLockFileServiceImpl.java | 9 +- .../file/GlobalSessionFileServiceImpl.java | 3 +- .../redis/BranchSessionRedisServiceImpl.java | 3 +- .../redis/GlobalLockRedisServiceImpl.java | 54 ++- .../redis/GlobalSessionRedisServiceImpl.java | 3 +- .../console/service/BranchSessionService.java | 27 ++ .../console/service/GlobalLockService.java | 16 + .../console/service/GlobalSessionService.java | 40 +++ .../server/coordinator/AbstractCore.java | 24 ++ .../apache/seata/server/coordinator/Core.java | 9 + .../coordinator/DefaultCoordinator.java | 68 ++++ .../seata/server/coordinator/DefaultCore.java | 35 +- .../TransactionCoordinatorOutbound.java | 12 +- .../seata/server/session/BranchSession.java | 14 + .../seata/server/session/GlobalSession.java | 22 +- .../seata/server/session/SessionHolder.java | 3 + .../server/storage/SessionConverter.java | 2 + .../db/session/DataBaseSessionManager.java | 3 +- .../server/storage/file/lock/FileLocker.java | 10 +- .../file/session/FileSessionManager.java | 1 + .../redis/session/RedisSessionManager.java | 3 +- .../coordinator/DefaultCoordinatorTest.java | 48 ++- .../seata/server/lock/LockManagerTest.java | 21 ++ .../session/FileSessionManagerTest.java | 242 +++++++++++++ .../file/FileTransactionStoreManagerTest.java | 8 + .../seata/rm/fence/SpringFenceHandler.java | 5 + .../org/apache/seata/rm/tcc/RMHandlerTCC.java | 31 ++ 80 files changed, 2795 insertions(+), 101 deletions(-) create mode 100644 core/src/main/java/org/apache/seata/core/protocol/transaction/BranchDeleteRequest.java create mode 100644 core/src/main/java/org/apache/seata/core/protocol/transaction/BranchDeleteResponse.java create mode 100644 core/src/main/java/org/apache/seata/core/rpc/processor/client/RmBranchDeleteProcessor.java create mode 100644 serializer/seata-serializer-protobuf/src/main/java/org/apache/seata/serializer/protobuf/convertor/BranchDeleteRequestConvertor.java create mode 100644 serializer/seata-serializer-protobuf/src/main/java/org/apache/seata/serializer/protobuf/convertor/BranchDeleteResponseConvertor.java create mode 100644 serializer/seata-serializer-protobuf/src/main/resources/protobuf/org/apache/seata/protocol/transcation/branchDeleteRequest.proto create mode 100644 serializer/seata-serializer-protobuf/src/main/resources/protobuf/org/apache/seata/protocol/transcation/branchDeleteResponse.proto create mode 100644 serializer/seata-serializer-protobuf/src/test/java/org/apache/seata/serializer/protobuf/convertor/BranchDeleteRequestConvertorTest.java create mode 100644 serializer/seata-serializer-protobuf/src/test/java/org/apache/seata/serializer/protobuf/convertor/BranchDeleteResponseConvertorTest.java create mode 100644 serializer/seata-serializer-seata/src/main/java/org/apache/seata/serializer/seata/protocol/transaction/BranchDeleteRequestCodec.java create mode 100644 serializer/seata-serializer-seata/src/main/java/org/apache/seata/serializer/seata/protocol/transaction/BranchDeleteResponseCodec.java create mode 100644 serializer/seata-serializer-seata/src/test/java/org/apache/seata/serializer/seata/protocol/transaction/BranchDeleteRequestCodecTest.java create mode 100644 serializer/seata-serializer-seata/src/test/java/org/apache/seata/serializer/seata/protocol/transaction/BranchDeleteResponseCodecTest.java create mode 100644 server/src/main/java/org/apache/seata/server/console/aop/GlobalExceptionHandlerAdvice.java create mode 100644 server/src/main/java/org/apache/seata/server/console/exception/ConsoleException.java create mode 100644 server/src/main/java/org/apache/seata/server/console/impl/AbstractBranchService.java create mode 100644 server/src/main/java/org/apache/seata/server/console/impl/AbstractGlobalService.java create mode 100644 server/src/main/java/org/apache/seata/server/console/impl/AbstractLockService.java create mode 100644 server/src/main/java/org/apache/seata/server/console/impl/AbstractService.java diff --git a/common/src/main/java/org/apache/seata/common/ConfigurationKeys.java b/common/src/main/java/org/apache/seata/common/ConfigurationKeys.java index ff8436b6dc9..06432802f92 100644 --- a/common/src/main/java/org/apache/seata/common/ConfigurationKeys.java +++ b/common/src/main/java/org/apache/seata/common/ConfigurationKeys.java @@ -299,6 +299,16 @@ public interface ConfigurationKeys { */ String DISTRIBUTED_LOCK_DB_TABLE = STORE_DB_PREFIX + "distributedLockTable"; + /** + * the constant AUTO_RESTART_TIME + */ + String AUTO_RESTART_TIME = SERVER_PREFIX + "autoRestartTime"; + + /** + * the constant AUTO_RESTART_PERIOD + */ + String AUTO_RESTART_PERIOD = SERVER_PREFIX + "autoRestartPeriod"; + /** * The constant STORE_DB_DATASOURCE_TYPE. */ diff --git a/common/src/main/java/org/apache/seata/common/Constants.java b/common/src/main/java/org/apache/seata/common/Constants.java index 43da1827e05..cb2a6bc4fb6 100644 --- a/common/src/main/java/org/apache/seata/common/Constants.java +++ b/common/src/main/java/org/apache/seata/common/Constants.java @@ -185,6 +185,11 @@ public interface Constants { */ String AUTO_COMMIT = "autoCommit"; + /** + * The constant AUTO_RESTART_SESSION + */ + String AUTO_RESTART_SESSION = "autoRestartSession"; + /** * The constant SKIP_CHECK_LOCK */ diff --git a/common/src/main/java/org/apache/seata/common/DefaultValues.java b/common/src/main/java/org/apache/seata/common/DefaultValues.java index eb0d40bb308..8ee92360db6 100644 --- a/common/src/main/java/org/apache/seata/common/DefaultValues.java +++ b/common/src/main/java/org/apache/seata/common/DefaultValues.java @@ -315,4 +315,8 @@ public interface DefaultValues { String DRUID_LOCATION = "lib/sqlparser/druid.jar"; int DEFAULT_ROCKET_MQ_MSG_TIMEOUT = 60 * 1000; + + long DEFAULT_AUTO_RESTART_TIME = 24 * 60 * 60 * 1000; + + long DEFAULT_AUTO_RESTART_PERIOD = 60 * 60 * 1000; } diff --git a/common/src/main/java/org/apache/seata/common/result/Result.java b/common/src/main/java/org/apache/seata/common/result/Result.java index 39eb93ee6a4..7bb8254fed4 100644 --- a/common/src/main/java/org/apache/seata/common/result/Result.java +++ b/common/src/main/java/org/apache/seata/common/result/Result.java @@ -26,6 +26,8 @@ public class Result implements Serializable { public static final String SUCCESS_CODE = "200"; public static final String SUCCESS_MSG = "success"; + public static final String FAIL_CODE = "400"; + private String code = SUCCESS_CODE; private String message = SUCCESS_MSG; diff --git a/common/src/main/java/org/apache/seata/common/result/SingleResult.java b/common/src/main/java/org/apache/seata/common/result/SingleResult.java index 97394053fab..a9f1b8b1bb0 100644 --- a/common/src/main/java/org/apache/seata/common/result/SingleResult.java +++ b/common/src/main/java/org/apache/seata/common/result/SingleResult.java @@ -47,10 +47,18 @@ public static SingleResult failure(Code errorCode) { return new SingleResult(errorCode.getCode(), errorCode.getMsg()); } + public static SingleResult failure(String msg) { + return new SingleResult<>(FAIL_CODE, msg); + } + public static SingleResult success(T data) { return new SingleResult<>(SUCCESS_CODE, SUCCESS_MSG,data); } + public static SingleResult success() { + return new SingleResult<>(SUCCESS_CODE, SUCCESS_MSG, null); + } + public T getData() { return data; } diff --git a/console/src/main/resources/static/console-fe/src/locales/en-us.ts b/console/src/main/resources/static/console-fe/src/locales/en-us.ts index 58e3a8fba06..0dea3a95bed 100644 --- a/console/src/main/resources/static/console-fe/src/locales/en-us.ts +++ b/console/src/main/resources/static/console-fe/src/locales/en-us.ts @@ -61,6 +61,14 @@ const enUs: ILocale = { showBranchSessionTitle: 'View branch session', showGlobalLockTitle: 'View global lock', branchSessionDialogTitle: 'Branch session info', + deleteGlobalSessionTitle: 'Delete global session', + stopGlobalSessionTitle: 'Stop global session retry', + startGlobalSessionTitle: 'Start global session retry', + sendGlobalSessionTitle: 'Commit or rollback global session', + changeGlobalSessionTitle: 'Change global session status', + deleteBranchSessionTitle: 'Delete branch session', + stopBranchSessionTitle: 'Stop branch session retry', + startBranchSessionTitle: 'Start branch session retry', }, GlobalLockInfo: { title: 'GlobalLockInfo', diff --git a/console/src/main/resources/static/console-fe/src/locales/zh-cn.ts b/console/src/main/resources/static/console-fe/src/locales/zh-cn.ts index e2ed430d514..8ff59f86b10 100644 --- a/console/src/main/resources/static/console-fe/src/locales/zh-cn.ts +++ b/console/src/main/resources/static/console-fe/src/locales/zh-cn.ts @@ -61,6 +61,14 @@ const zhCn: ILocale = { showBranchSessionTitle: '查看分支信息', showGlobalLockTitle: '查看全局锁', branchSessionDialogTitle: '分支事务信息', + deleteGlobalSessionTitle: '删除全局事务', + stopGlobalSessionTitle: '停止全局事务重试', + startGlobalSessionTitle: '开启全局事务重试', + sendGlobalSessionTitle: '提交或回滚全局事务', + changeGlobalSessionTitle: '更新全局事务状态', + deleteBranchSessionTitle: '删除分支事务', + stopBranchSessionTitle: '停止分支事务重启', + startBranchSessionTitle: '开启分支事务重试', }, GlobalLockInfo: { title: '全局锁信息', diff --git a/console/src/main/resources/static/console-fe/src/pages/GlobalLockInfo/GlobalLockInfo.tsx b/console/src/main/resources/static/console-fe/src/pages/GlobalLockInfo/GlobalLockInfo.tsx index 5a0c6362011..31e235769f8 100644 --- a/console/src/main/resources/static/console-fe/src/pages/GlobalLockInfo/GlobalLockInfo.tsx +++ b/console/src/main/resources/static/console-fe/src/pages/GlobalLockInfo/GlobalLockInfo.tsx @@ -15,17 +15,19 @@ * limitations under the License. */ import React from 'react'; -import { ConfigProvider, Table, Button, DatePicker, Form, Icon, Pagination, Input } from '@alicloud/console-components'; +import { ConfigProvider, Table, Button, DatePicker, Form, Icon, Pagination, Input, Dialog, Message } from '@alicloud/console-components'; import Actions, { LinkButton } from '@alicloud/console-components-actions'; import { withRouter } from 'react-router-dom'; import Page from '@/components/Page'; import { GlobalProps } from '@/module'; import styled, { css } from 'styled-components'; -import getData, { GlobalLockParam } from '@/service/globalLockInfo'; +import getData, {checkData, deleteData, GlobalLockParam } from '@/service/globalLockInfo'; import PropTypes from 'prop-types'; import moment from 'moment'; import './index.scss'; +import {get} from "lodash"; +import {enUsKey, getCurrentLanguage} from "@/reducers/locale"; const { RangePicker } = DatePicker; const FormItem = Form.Item; @@ -37,7 +39,7 @@ type GlobalLockInfoState = { globalLockParam: GlobalLockParam; } - class GlobalLockInfo extends React.Component { +class GlobalLockInfo extends React.Component { static displayName = 'GlobalLockInfo'; static propTypes = { @@ -148,12 +150,53 @@ type GlobalLockInfoState = { this.search(); } + deleteCell = (val: string, index: number, record: any) => { + const {locale = {}} = this.props; + const { + deleteGlobalLockTitle + } = locale; + let width = getCurrentLanguage() === enUsKey ? '120px' : '80px' + return ( + + + ) + } + + render() { const { locale = {} } = this.props; const { title, subTitle, createTimeLabel, inputFilterPlaceholder, searchButtonLabel, resetButtonLabel, + operateTitle, } = locale; return ( {/* global lock table */}
- - - - - - - - - - -
- + + + + + + + + + + + +
+
); diff --git a/console/src/main/resources/static/console-fe/src/pages/TransactionInfo/TransactionInfo.tsx b/console/src/main/resources/static/console-fe/src/pages/TransactionInfo/TransactionInfo.tsx index f6ae8c26ea4..090a4cc6af5 100644 --- a/console/src/main/resources/static/console-fe/src/pages/TransactionInfo/TransactionInfo.tsx +++ b/console/src/main/resources/static/console-fe/src/pages/TransactionInfo/TransactionInfo.tsx @@ -15,17 +15,19 @@ * limitations under the License. */ import React from 'react'; -import { ConfigProvider, Table, Button, DatePicker, Form, Icon, Switch, Pagination, Dialog, Input, Select } from '@alicloud/console-components'; +import { ConfigProvider, Table, Button, DatePicker, Form, Icon, Switch, Pagination, Dialog, Input, Select, Message } from '@alicloud/console-components'; import Actions, { LinkButton } from '@alicloud/console-components-actions'; import { withRouter } from 'react-router-dom'; import Page from '@/components/Page'; import { GlobalProps } from '@/module'; import styled, { css } from 'styled-components'; -import getData, { GlobalSessionParam } from '@/service/transactionInfo'; +import getData, { changeGlobalData, deleteBranchData, deleteGlobalData, GlobalSessionParam, sendGlobalCommitOrRollback, startBranchData, startGlobalData, stopBranchData, stopGlobalData } from '@/service/transactionInfo'; import PropTypes from 'prop-types'; import moment from 'moment'; import './index.scss'; +import { get as lodashGet} from "lodash"; +import {enUsKey, getCurrentLanguage} from "@/reducers/locale"; const { RangePicker } = DatePicker; const FormItem = Form.Item; @@ -42,6 +44,7 @@ type TransactionInfoState = { total: number; loading: boolean; branchSessionDialogVisible: boolean; + xid : string; currentBranchSession: Array; globalSessionParam : GlobalSessionParam; } @@ -155,6 +158,24 @@ const statusList:Array = [ iconType: 'warning', iconColor: '#FFA003', }, + { + label: 'Deleting', + value: 18, + iconType: 'warning', + iconColor: '#FFA003', + }, + { + label: 'StopCommitRetry', + value: 19, + iconType: 'ellipsis', + iconColor: 'rgb(3, 193, 253)', + }, + { + label: 'StopRollbackRetry', + value: 20, + iconType: 'ellipsis', + iconColor: 'rgb(3, 193, 253)', + }, ]; const branchSessionStatusList:Array = [ @@ -225,13 +246,23 @@ const branchSessionStatusList:Array = [ iconColor: '#FF3333', }, { - label: 'PhaseOne_RDONLY', + label: 'STOP_RETRY', value: 13, iconType: 'ellipsis', iconColor: 'rgb(3, 193, 253)', }, ]; +const commonWarnning = 'Global transaction commit or rollback inconsistency problem exists.' +const warnning = new Map([ + ['stopBranchSession', new Map([['AT', ''], ['XA', ''], + ['TCC', 'Please check if this may affect the logic of other branches.'], ['SAGA', '']])], + ['deleteBranchSession', + new Map([['AT', 'The global lock and undo log will be deleted too, dirty write problem exists.'], + ['XA', 'The xa branch will rollback'], ['TCC', ''], ['SAGA', '']])], + ['deleteGlobalSession', new Map([['AT', ''], ['XA', ''], ['TCC', ''], ['SAGA', '']])], +]) + class TransactionInfo extends React.Component { static displayName = 'TransactionInfo'; @@ -245,6 +276,7 @@ class TransactionInfo extends React.Component total: 0, loading: false, branchSessionDialogVisible: false, + xid: '', currentBranchSession: [], globalSessionParam: { withBranch: false, @@ -288,6 +320,13 @@ class TransactionInfo extends React.Component element.beginTime = (element.beginTime == null || element.beginTime === '') ? null : moment(Number(element.beginTime)).format('YYYY-MM-DD HH:mm:ss'); }); + if (this.state.branchSessionDialogVisible) { + data.data.forEach((item:any) => { + if (item.xid == this.state.xid) { + this.state.currentBranchSession = item.branchSessionVOs + } + }) + } this.setState({ list: data.data, total: data.total, @@ -299,10 +338,10 @@ class TransactionInfo extends React.Component } searchFilterOnChange = (key:string, val:string) => { - this.setState({ - globalSessionParam: Object.assign(this.state.globalSessionParam, - { [key]: val }), - }); + this.setState({ + globalSessionParam: Object.assign(this.state.globalSessionParam, + { [key]: val }), + }); } branchSessionSwitchOnChange = (checked: boolean, e: any) => { @@ -363,15 +402,21 @@ class TransactionInfo extends React.Component const { showBranchSessionTitle, showGlobalLockTitle, + deleteGlobalSessionTitle, + stopGlobalSessionTitle, + startGlobalSessionTitle, + sendGlobalSessionTitle, + changeGlobalSessionTitle, } = locale; + let width = getCurrentLanguage() === enUsKey ? '450px' : '350px' return ( - + {/* {when withBranch false, hide 'View branch session' button} */} {this.state.globalSessionParam.withBranch ? ( - {showBranchSessionTitle} + {showBranchSessionTitle} ) : null} @@ -386,6 +431,118 @@ class TransactionInfo extends React.Component > {showGlobalLockTitle} + + + + {record.status == 19 || record.status == 20 ? ( + + ) : + } + + + + ); } @@ -393,9 +550,13 @@ class TransactionInfo extends React.Component const { locale = {}, history } = this.props; const { showGlobalLockTitle, + deleteBranchSessionTitle, + stopBranchSessionTitle, + startBranchSessionTitle, } = locale; + let width = getCurrentLanguage() === enUsKey ? '450px' : '350px' return ( - + { history.push({ @@ -407,6 +568,79 @@ class TransactionInfo extends React.Component > {showGlobalLockTitle} + + + + + {record.status == 13 ? ( + + ) : } + ); } @@ -427,16 +661,18 @@ class TransactionInfo extends React.Component } showBranchSessionDialog = (val: string, index: number, record: any) => () => { - this.setState({ - branchSessionDialogVisible: true, - currentBranchSession: record.branchSessionVOs, - }); + this.setState({ + branchSessionDialogVisible: true, + currentBranchSession: record.branchSessionVOs, + xid: record.xid, + }); } closeBranchSessionDialog = () => { this.setState({ branchSessionDialogVisible: false, currentBranchSession: [], + xid: '', }); } @@ -455,14 +691,14 @@ class TransactionInfo extends React.Component {/* search form */}
@@ -519,40 +755,40 @@ class TransactionInfo extends React.Component {/* global session table */}
- - - - - - - - - - - + + + + + + + + + + +
+ - -
{/* branch session dialog */} - +
diff --git a/console/src/main/resources/static/console-fe/src/service/globalLockInfo.ts b/console/src/main/resources/static/console-fe/src/service/globalLockInfo.ts index 3c16bf15255..c7c909b25bc 100644 --- a/console/src/main/resources/static/console-fe/src/service/globalLockInfo.ts +++ b/console/src/main/resources/static/console-fe/src/service/globalLockInfo.ts @@ -29,7 +29,7 @@ export type GlobalLockParam = { timeEnd?: number }; - export default async function fetchData(params:GlobalLockParam):Promise { +export default async function fetchData(params:GlobalLockParam):Promise { let result = await request('/console/globalLock/query', { method: 'get', params, @@ -37,3 +37,25 @@ export type GlobalLockParam = { return result; } + +export async function deleteData(params: GlobalLockParam): Promise { + let result = await request('/console/globalLock/delete', { + method: 'delete', + params, + }); + return result; +} + +export async function checkData(params: GlobalLockParam): Promise { + const xid = params.xid + const branchId = params.branchId + + let result = await request('/console/globalLock/check', { + method: 'get', + params: { + xid, + branchId + }, + }); + return result; +} diff --git a/console/src/main/resources/static/console-fe/src/service/transactionInfo.ts b/console/src/main/resources/static/console-fe/src/service/transactionInfo.ts index 8e953ac849c..b3840e29842 100644 --- a/console/src/main/resources/static/console-fe/src/service/transactionInfo.ts +++ b/console/src/main/resources/static/console-fe/src/service/transactionInfo.ts @@ -28,6 +28,14 @@ export type GlobalSessionParam = { timeEnd?: number }; +export type BranchSessionParam = { + xid?: string, + branchId?: string, + applicationId?: string, + status?: number, + transactionName?: string, +}; + export default async function fetchData(params:GlobalSessionParam):Promise { let result = await request('/console/globalSession/query', { method: 'get', @@ -36,3 +44,97 @@ export default async function fetchData(params:GlobalSessionParam):Promise return result; } + +export async function deleteGlobalData(params: GlobalSessionParam): Promise { + const xid = params.xid + let result = await request('/console/globalSession/deleteGlobalSession', { + method: 'delete', + params: { + xid + }, + }); + return result; +} + +export async function stopGlobalData(params: GlobalSessionParam): Promise { + const xid = params.xid + let result = await request('/console/globalSession/stopGlobalSession', { + method: 'PUT', + params: { + xid + }, + }); + return result; +} + +export async function startGlobalData(params: GlobalSessionParam): Promise { + const xid = params.xid + let result = await request('/console/globalSession/startGlobalSession', { + method: 'PUT', + params: { + xid + }, + }); + return result; +} + +export async function sendGlobalCommitOrRollback(params: BranchSessionParam): Promise { + const xid = params.xid + let result = await request('/console/globalSession/sendCommitOrRollback', { + method: 'PUT', + params: { + xid + }, + }); + return result; +} + +export async function changeGlobalData(params: GlobalSessionParam): Promise { + const xid = params.xid + let result = await request('/console/globalSession/changeGlobalStatus', { + method: 'PUT', + params: { + xid + }, + }); + return result; +} + +export async function deleteBranchData(params: BranchSessionParam): Promise { + const xid = params.xid + const branchId = params.branchId + let result = await request('/console/branchSession/deleteBranchSession', { + method: 'delete', + params: { + xid, + branchId + }, + }); + return result; +} + +export async function stopBranchData(params: BranchSessionParam): Promise { + const xid = params.xid + const branchId = params.branchId + let result = await request('/console/branchSession/stopBranchSession', { + method: 'PUT', + params: { + xid, + branchId + }, + }); + return result; +} + +export async function startBranchData(params: BranchSessionParam): Promise { + const xid = params.xid + const branchId = params.branchId + let result = await request('/console/branchSession/startBranchSession', { + method: 'PUT', + params: { + xid, + branchId + }, + }); + return result; +} diff --git a/core/src/main/java/org/apache/seata/core/exception/TransactionExceptionCode.java b/core/src/main/java/org/apache/seata/core/exception/TransactionExceptionCode.java index 16a2e899dc6..2c50e1ebbc8 100644 --- a/core/src/main/java/org/apache/seata/core/exception/TransactionExceptionCode.java +++ b/core/src/main/java/org/apache/seata/core/exception/TransactionExceptionCode.java @@ -140,8 +140,12 @@ public enum TransactionExceptionCode { /** * Broken transaction exception code. */ - Broken; + Broken, + /** + * Failed to send branch rollback request transaction exception code. + */ + FailedToSendBranchDeleteRequest; /** * Get transaction exception code. diff --git a/core/src/main/java/org/apache/seata/core/model/BranchStatus.java b/core/src/main/java/org/apache/seata/core/model/BranchStatus.java index c728670d742..9dc4313e4c6 100644 --- a/core/src/main/java/org/apache/seata/core/model/BranchStatus.java +++ b/core/src/main/java/org/apache/seata/core/model/BranchStatus.java @@ -108,7 +108,13 @@ public enum BranchStatus { * The results of the Phase one are read-only. * Description: After the branch prepare in the Oracle database, only purely read-only query statements were executed. */ - PhaseOne_RDONLY(13); + PhaseOne_RDONLY(13), + + /** + * Stop retry + * description:user operate to stop retry + */ + STOP_RETRY(14); private int code; diff --git a/core/src/main/java/org/apache/seata/core/model/GlobalStatus.java b/core/src/main/java/org/apache/seata/core/model/GlobalStatus.java index 655328f57bd..846113e2b6f 100644 --- a/core/src/main/java/org/apache/seata/core/model/GlobalStatus.java +++ b/core/src/main/java/org/apache/seata/core/model/GlobalStatus.java @@ -128,7 +128,25 @@ public enum GlobalStatus { * The rollback retry Timeout . */ // Finally: failed to rollback since retry timeout - RollbackRetryTimeout(17, "global transaction still failed after commit failure and retries for some time"); + RollbackRetryTimeout(17, "global transaction still failed after commit failure and retries for some time"), + + /** + * Deleting . + */ + // Finally: deleting + Deleting(18, "global transaction is deleting, willing delete soon"), + + /** + * The rollback retry Timeout . + */ + // stop commit retry + StopCommitRetry(19,"global transaction is retry commit but stop retry now"), + + /** + * The rollback retry Timeout . + */ + // stop rollback retry + StopRollbackRetry(20,"global transaction is retry rollback but stop retry now"); private final int code; private final String desc; @@ -194,7 +212,7 @@ public static boolean isOnePhaseTimeout(GlobalStatus status) { */ public static boolean isTwoPhaseSuccess(GlobalStatus status) { if (status == GlobalStatus.Committed || status == GlobalStatus.Rollbacked - || status == GlobalStatus.TimeoutRollbacked) { + || status == GlobalStatus.TimeoutRollbacked || status == GlobalStatus.Deleting) { return true; } return false; diff --git a/core/src/main/java/org/apache/seata/core/protocol/MessageType.java b/core/src/main/java/org/apache/seata/core/protocol/MessageType.java index 58adbfbce6f..63c8a536f94 100644 --- a/core/src/main/java/org/apache/seata/core/protocol/MessageType.java +++ b/core/src/main/java/org/apache/seata/core/protocol/MessageType.java @@ -70,6 +70,14 @@ public interface MessageType { * The constant TYPE_GLOBAL_LOCK_QUERY_RESULT. */ short TYPE_GLOBAL_LOCK_QUERY_RESULT = 22; + /** + * The constant TYPE_BRANCH_DELETE. + */ + short TYPE_BRANCH_DELETE = 23; + /** + * The constant TYPE_BRANCH_DELETE_RESULT. + */ + short TYPE_BRANCH_DELETE_RESULT = 24; /** * The constant TYPE_BRANCH_COMMIT. diff --git a/core/src/main/java/org/apache/seata/core/protocol/transaction/BranchDeleteRequest.java b/core/src/main/java/org/apache/seata/core/protocol/transaction/BranchDeleteRequest.java new file mode 100644 index 00000000000..a815bc32a77 --- /dev/null +++ b/core/src/main/java/org/apache/seata/core/protocol/transaction/BranchDeleteRequest.java @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.core.protocol.transaction; + +import org.apache.seata.core.model.BranchType; +import org.apache.seata.core.protocol.MessageType; +import org.apache.seata.core.rpc.RpcContext; + +import java.io.Serializable; + +/** + * BranchDeleteRequest + */ +public class BranchDeleteRequest extends AbstractTransactionRequestToRM implements Serializable { + private static final long serialVersionUID = 7134732523612364742L; + + /** + * The Xid. + */ + protected String xid; + + /** + * The Branch id. + */ + protected long branchId; + + /** + * The Branch type. + */ + protected BranchType branchType = BranchType.AT; + + /** + * The resource id. + */ + private String resourceId; + + @Override + public short getTypeCode() { + return MessageType.TYPE_BRANCH_DELETE; + } + + @Override + public AbstractTransactionResponse handle(RpcContext rpcContext) { + return handler.handle(this); + } + + public String getXid() { + return xid; + } + + public void setXid(String xid) { + this.xid = xid; + } + + public long getBranchId() { + return branchId; + } + + public void setBranchId(long branchId) { + this.branchId = branchId; + } + + public BranchType getBranchType() { + return branchType; + } + + public void setBranchType(BranchType branchType) { + this.branchType = branchType; + } + + public String getResourceId() { + return resourceId; + } + + public void setResourceId(String resourceId) { + this.resourceId = resourceId; + } +} diff --git a/core/src/main/java/org/apache/seata/core/protocol/transaction/BranchDeleteResponse.java b/core/src/main/java/org/apache/seata/core/protocol/transaction/BranchDeleteResponse.java new file mode 100644 index 00000000000..4517fc6c526 --- /dev/null +++ b/core/src/main/java/org/apache/seata/core/protocol/transaction/BranchDeleteResponse.java @@ -0,0 +1,15 @@ +package org.apache.seata.core.protocol.transaction; + +import org.apache.seata.core.protocol.MessageType; + +/** + * BranchDeleteResponse + * + * + */ +public class BranchDeleteResponse extends AbstractBranchEndResponse { + @Override + public short getTypeCode() { + return MessageType.TYPE_BRANCH_DELETE_RESULT; + } +} diff --git a/core/src/main/java/org/apache/seata/core/protocol/transaction/RMInboundHandler.java b/core/src/main/java/org/apache/seata/core/protocol/transaction/RMInboundHandler.java index 0288f0a6a87..5b17fa95fc9 100644 --- a/core/src/main/java/org/apache/seata/core/protocol/transaction/RMInboundHandler.java +++ b/core/src/main/java/org/apache/seata/core/protocol/transaction/RMInboundHandler.java @@ -45,4 +45,11 @@ public interface RMInboundHandler { */ void handle(UndoLogDeleteRequest request); + /** + * Handle branch delete . + * + * @param request the request + * @return the branch delete response + */ + BranchDeleteResponse handle(BranchDeleteRequest request); } diff --git a/core/src/main/java/org/apache/seata/core/rpc/netty/NettyRemotingServer.java b/core/src/main/java/org/apache/seata/core/rpc/netty/NettyRemotingServer.java index 3010efc4ae2..19b84aeb741 100644 --- a/core/src/main/java/org/apache/seata/core/rpc/netty/NettyRemotingServer.java +++ b/core/src/main/java/org/apache/seata/core/rpc/netty/NettyRemotingServer.java @@ -111,6 +111,7 @@ private void registerProcessor() { // 2. registry on response message processor ServerOnResponseProcessor onResponseProcessor = new ServerOnResponseProcessor(getHandler(), getFutures()); + super.registerProcessor(MessageType.TYPE_BRANCH_DELETE_RESULT, onResponseProcessor, branchResultMessageExecutor); super.registerProcessor(MessageType.TYPE_BRANCH_COMMIT_RESULT, onResponseProcessor, branchResultMessageExecutor); super.registerProcessor(MessageType.TYPE_BRANCH_ROLLBACK_RESULT, onResponseProcessor, branchResultMessageExecutor); // 3. registry rm message processor diff --git a/core/src/main/java/org/apache/seata/core/rpc/netty/RmNettyRemotingClient.java b/core/src/main/java/org/apache/seata/core/rpc/netty/RmNettyRemotingClient.java index 0b9fd0e12bc..26114cdb54c 100644 --- a/core/src/main/java/org/apache/seata/core/rpc/netty/RmNettyRemotingClient.java +++ b/core/src/main/java/org/apache/seata/core/rpc/netty/RmNettyRemotingClient.java @@ -46,6 +46,7 @@ import org.apache.seata.core.rpc.processor.client.ClientHeartbeatProcessor; import org.apache.seata.core.rpc.processor.client.ClientOnResponseProcessor; import org.apache.seata.core.rpc.processor.client.RmBranchCommitProcessor; +import org.apache.seata.core.rpc.processor.client.RmBranchDeleteProcessor; import org.apache.seata.core.rpc.processor.client.RmBranchRollbackProcessor; import org.apache.seata.core.rpc.processor.client.RmUndoLogProcessor; import org.slf4j.Logger; @@ -333,5 +334,8 @@ private void registerProcessor() { // 5.registry heartbeat message processor ClientHeartbeatProcessor clientHeartbeatProcessor = new ClientHeartbeatProcessor(); super.registerProcessor(MessageType.TYPE_HEARTBEAT_MSG, clientHeartbeatProcessor, null); + // 6.registry rm handler branch delete processor + RmBranchDeleteProcessor rmBranchDeleteProcessor = new RmBranchDeleteProcessor(getTransactionMessageHandler(), this); + super.registerProcessor(MessageType.TYPE_BRANCH_DELETE, rmBranchDeleteProcessor, messageExecutor); } } diff --git a/core/src/main/java/org/apache/seata/core/rpc/processor/client/RmBranchDeleteProcessor.java b/core/src/main/java/org/apache/seata/core/rpc/processor/client/RmBranchDeleteProcessor.java new file mode 100644 index 00000000000..a0c7f5309fc --- /dev/null +++ b/core/src/main/java/org/apache/seata/core/rpc/processor/client/RmBranchDeleteProcessor.java @@ -0,0 +1,52 @@ +package org.apache.seata.core.rpc.processor.client; + +import io.netty.channel.ChannelHandlerContext; +import org.apache.seata.common.util.NetUtil; +import org.apache.seata.core.protocol.RpcMessage; +import org.apache.seata.core.protocol.transaction.BranchDeleteRequest; +import org.apache.seata.core.protocol.transaction.BranchDeleteResponse; +import org.apache.seata.core.rpc.RemotingClient; +import org.apache.seata.core.rpc.TransactionMessageHandler; +import org.apache.seata.core.rpc.processor.RemotingProcessor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The type Rm branch delete processor. + */ +public class RmBranchDeleteProcessor implements RemotingProcessor { + + private static final Logger LOGGER = LoggerFactory.getLogger(RmBranchDeleteProcessor.class); + + private TransactionMessageHandler handler; + + private RemotingClient remotingClient; + + public RmBranchDeleteProcessor(TransactionMessageHandler handler, RemotingClient remotingClient) { + this.handler = handler; + this.remotingClient = remotingClient; + } + + @Override + public void process(ChannelHandlerContext ctx, RpcMessage rpcMessage) throws Exception { + String remoteAddress = NetUtil.toStringAddress(ctx.channel().remoteAddress()); + Object msg = rpcMessage.getBody(); + if (LOGGER.isInfoEnabled()) { + LOGGER.info("rm handle branch rollback process: {}", msg); + } + handleBranchDelete(rpcMessage, remoteAddress, (BranchDeleteRequest) msg); + } + + private void handleBranchDelete(RpcMessage request, String serverAddress, BranchDeleteRequest branchDeleteRequest) { + BranchDeleteResponse resultMessage; + resultMessage = (BranchDeleteResponse) handler.onRequest(branchDeleteRequest, null); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("branch delete result: {}", resultMessage); + } + try { + this.remotingClient.sendAsyncResponse(serverAddress, request, resultMessage); + } catch (Throwable throwable) { + LOGGER.error("branch delete error: {}", throwable.getMessage(), throwable); + } + } +} diff --git a/integration-tx-api/src/main/java/org/apache/seata/integration/tx/api/fence/DefaultCommonFenceHandler.java b/integration-tx-api/src/main/java/org/apache/seata/integration/tx/api/fence/DefaultCommonFenceHandler.java index 3e8307628d7..52a2e7313d2 100644 --- a/integration-tx-api/src/main/java/org/apache/seata/integration/tx/api/fence/DefaultCommonFenceHandler.java +++ b/integration-tx-api/src/main/java/org/apache/seata/integration/tx/api/fence/DefaultCommonFenceHandler.java @@ -67,4 +67,11 @@ public int deleteFenceByDate(Date datetime) { check(); return fenceHandler.deleteFenceByDate(datetime); } + + + @Override + public boolean deleteFenceByXidAndBranchId(String xid, Long branchId) { + check(); + return fenceHandler.deleteFenceByXidAndBranchId(xid, branchId); + } } diff --git a/integration-tx-api/src/main/java/org/apache/seata/integration/tx/api/fence/FenceHandler.java b/integration-tx-api/src/main/java/org/apache/seata/integration/tx/api/fence/FenceHandler.java index 11978290bff..fbaabc5e57c 100644 --- a/integration-tx-api/src/main/java/org/apache/seata/integration/tx/api/fence/FenceHandler.java +++ b/integration-tx-api/src/main/java/org/apache/seata/integration/tx/api/fence/FenceHandler.java @@ -32,4 +32,5 @@ public interface FenceHandler { int deleteFenceByDate(Date datetime); + boolean deleteFenceByXidAndBranchId(String xid, Long branchId); } diff --git a/rm-datasource/src/main/java/org/apache/seata/rm/RMHandlerAT.java b/rm-datasource/src/main/java/org/apache/seata/rm/RMHandlerAT.java index c199c284ff0..1c1d562cf1d 100644 --- a/rm-datasource/src/main/java/org/apache/seata/rm/RMHandlerAT.java +++ b/rm-datasource/src/main/java/org/apache/seata/rm/RMHandlerAT.java @@ -22,8 +22,12 @@ import java.util.Date; import org.apache.seata.common.util.DateUtil; +import org.apache.seata.core.model.BranchStatus; import org.apache.seata.core.model.BranchType; import org.apache.seata.core.model.ResourceManager; +import org.apache.seata.core.protocol.ResultCode; +import org.apache.seata.core.protocol.transaction.BranchDeleteRequest; +import org.apache.seata.core.protocol.transaction.BranchDeleteResponse; import org.apache.seata.core.protocol.transaction.UndoLogDeleteRequest; import org.apache.seata.rm.datasource.DataSourceManager; import org.apache.seata.rm.datasource.DataSourceProxy; @@ -70,6 +74,28 @@ public void handle(UndoLogDeleteRequest request) { } } + @Override + public BranchDeleteResponse handle(BranchDeleteRequest request) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Start at delete undo log, xid:{}, branchId:{}.", request.getXid(), request.getBranchId()); + } + BranchDeleteResponse branchDeleteResponse = new BranchDeleteResponse(); + DataSourceManager dataSourceManager = (DataSourceManager) getResourceManager(); + try { + dataSourceManager.branchCommit(BranchType.AT, request.getXid(), request.getBranchId(), + request.getResourceId(), ""); + branchDeleteResponse.setResultCode(ResultCode.Success); + } catch (Exception e) { + branchDeleteResponse.setResultCode(ResultCode.Failed); + LOGGER.error("delete undo log fail, xid:{}, branchId:{}, ",request.getXid(), request.getBranchId(), e); + } + branchDeleteResponse.setXid(request.getXid()); + branchDeleteResponse.setBranchId(request.getBranchId()); + // this branch status is no importance + branchDeleteResponse.setBranchStatus(BranchStatus.Unknown); + return branchDeleteResponse; + } + Connection getConnection(DataSourceProxy dataSourceProxy) { try { return dataSourceProxy.getPlainConnection(); diff --git a/rm-datasource/src/main/java/org/apache/seata/rm/RMHandlerXA.java b/rm-datasource/src/main/java/org/apache/seata/rm/RMHandlerXA.java index a77092e3c67..c16eb2f8228 100644 --- a/rm-datasource/src/main/java/org/apache/seata/rm/RMHandlerXA.java +++ b/rm-datasource/src/main/java/org/apache/seata/rm/RMHandlerXA.java @@ -16,20 +16,48 @@ */ package org.apache.seata.rm; +import org.apache.seata.core.model.BranchStatus; import org.apache.seata.core.model.BranchType; import org.apache.seata.core.model.ResourceManager; +import org.apache.seata.core.protocol.ResultCode; +import org.apache.seata.core.protocol.transaction.BranchDeleteRequest; +import org.apache.seata.core.protocol.transaction.BranchDeleteResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * The type RM handler XA. * */ public class RMHandlerXA extends AbstractRMHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(RMHandlerXA.class); @Override protected ResourceManager getResourceManager() { return DefaultResourceManager.get().getResourceManager(BranchType.XA); } + @Override + public BranchDeleteResponse handle(BranchDeleteRequest request) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Start xa delete branch, xid:{}, branchId:{}", request.getXid(), request.getBranchId()); + } + BranchDeleteResponse branchDeleteResponse = new BranchDeleteResponse(); + try { + BranchStatus branchStatus = getResourceManager().branchRollback(request.getBranchType(), request.getXid(), + request.getBranchId(), request.getResourceId(), ""); + ResultCode code = branchStatus == BranchStatus.PhaseTwo_Rollbacked ? ResultCode.Success : ResultCode.Failed; + branchDeleteResponse.setResultCode(code); + branchDeleteResponse.setBranchStatus(branchStatus); + } catch (Exception e) { + branchDeleteResponse.setResultCode(ResultCode.Failed); + LOGGER.error("XA branch delete fail, reason: {}", e.getMessage(), e); + } + branchDeleteResponse.setXid(request.getXid()); + branchDeleteResponse.setBranchId(request.getBranchId()); + return branchDeleteResponse; + } + @Override public BranchType getBranchType() { return BranchType.XA; diff --git a/rm/src/main/java/org/apache/seata/rm/DefaultRMHandler.java b/rm/src/main/java/org/apache/seata/rm/DefaultRMHandler.java index 3e56358b598..7211d5b7999 100644 --- a/rm/src/main/java/org/apache/seata/rm/DefaultRMHandler.java +++ b/rm/src/main/java/org/apache/seata/rm/DefaultRMHandler.java @@ -24,6 +24,8 @@ import org.apache.seata.core.model.ResourceManager; import org.apache.seata.core.protocol.transaction.BranchCommitRequest; import org.apache.seata.core.protocol.transaction.BranchCommitResponse; +import org.apache.seata.core.protocol.transaction.BranchDeleteRequest; +import org.apache.seata.core.protocol.transaction.BranchDeleteResponse; import org.apache.seata.core.protocol.transaction.BranchRollbackRequest; import org.apache.seata.core.protocol.transaction.BranchRollbackResponse; import org.apache.seata.core.protocol.transaction.UndoLogDeleteRequest; @@ -73,6 +75,11 @@ public void handle(UndoLogDeleteRequest request) { getRMHandler(request.getBranchType()).handle(request); } + @Override + public BranchDeleteResponse handle(BranchDeleteRequest request) { + return getRMHandler(request.getBranchType()).handle(request); + } + protected AbstractRMHandler getRMHandler(BranchType branchType) { return allRMHandlersMap.get(branchType); } diff --git a/saga/seata-saga-rm/src/main/java/org/apache/seata/saga/rm/RMHandlerSaga.java b/saga/seata-saga-rm/src/main/java/org/apache/seata/saga/rm/RMHandlerSaga.java index dbf71bef795..5d4edb63f86 100644 --- a/saga/seata-saga-rm/src/main/java/org/apache/seata/saga/rm/RMHandlerSaga.java +++ b/saga/seata-saga-rm/src/main/java/org/apache/seata/saga/rm/RMHandlerSaga.java @@ -18,6 +18,8 @@ import org.apache.seata.core.model.BranchType; import org.apache.seata.core.model.ResourceManager; +import org.apache.seata.core.protocol.transaction.BranchDeleteRequest; +import org.apache.seata.core.protocol.transaction.BranchDeleteResponse; import org.apache.seata.core.protocol.transaction.UndoLogDeleteRequest; import org.apache.seata.rm.AbstractRMHandler; import org.apache.seata.rm.DefaultResourceManager; @@ -33,6 +35,11 @@ public void handle(UndoLogDeleteRequest request) { //DO nothing } + @Override + public BranchDeleteResponse handle(BranchDeleteRequest request) { + return null; + } + /** * get SAGA resource manager * diff --git a/serializer/seata-serializer-protobuf/src/main/java/org/apache/seata/serializer/protobuf/convertor/BranchDeleteRequestConvertor.java b/serializer/seata-serializer-protobuf/src/main/java/org/apache/seata/serializer/protobuf/convertor/BranchDeleteRequestConvertor.java new file mode 100644 index 00000000000..786f0353880 --- /dev/null +++ b/serializer/seata-serializer-protobuf/src/main/java/org/apache/seata/serializer/protobuf/convertor/BranchDeleteRequestConvertor.java @@ -0,0 +1,43 @@ +package org.apache.seata.serializer.protobuf.convertor; + +import org.apache.seata.core.model.BranchType; +import org.apache.seata.core.protocol.transaction.BranchDeleteRequest; +import org.apache.seata.serializer.protobuf.generated.AbstractMessageProto; +import org.apache.seata.serializer.protobuf.generated.AbstractTransactionRequestProto; +import org.apache.seata.serializer.protobuf.generated.BranchDeleteRequestProto; +import org.apache.seata.serializer.protobuf.generated.BranchTypeProto; +import org.apache.seata.serializer.protobuf.generated.MessageTypeProto; + +/** + * BranchDeleteRequestConvertor + */ +public class BranchDeleteRequestConvertor implements PbConvertor { + @Override + public BranchDeleteRequestProto convert2Proto(BranchDeleteRequest branchDeleteRequest) { + final short typeCode = branchDeleteRequest.getTypeCode(); + + final AbstractMessageProto abstractMessage = AbstractMessageProto.newBuilder().setMessageType( + MessageTypeProto.forNumber(typeCode)).build(); + + final AbstractTransactionRequestProto abstractTransactionRequestProto = AbstractTransactionRequestProto + .newBuilder().setAbstractMessage(abstractMessage).build(); + + final String resourceId = branchDeleteRequest.getResourceId(); + return BranchDeleteRequestProto.newBuilder().setAbstractTransactionRequest( + abstractTransactionRequestProto).setXid(branchDeleteRequest.getXid()).setBranchId( + branchDeleteRequest.getBranchId()).setBranchType(BranchTypeProto.valueOf( + branchDeleteRequest.getBranchType().name())).setResourceId(resourceId == null ? "" : resourceId).build(); + } + + @Override + public BranchDeleteRequest convert2Model(BranchDeleteRequestProto branchDeleteRequestProto) { + BranchDeleteRequest branchDeleteRequest = new BranchDeleteRequest(); + branchDeleteRequest.setBranchId(branchDeleteRequestProto.getBranchId()); + branchDeleteRequest.setResourceId(branchDeleteRequestProto.getResourceId()); + branchDeleteRequest.setXid(branchDeleteRequestProto.getXid()); + branchDeleteRequest.setBranchType( + BranchType.valueOf(branchDeleteRequestProto.getBranchType().name())); + return branchDeleteRequest; + } +} + diff --git a/serializer/seata-serializer-protobuf/src/main/java/org/apache/seata/serializer/protobuf/convertor/BranchDeleteResponseConvertor.java b/serializer/seata-serializer-protobuf/src/main/java/org/apache/seata/serializer/protobuf/convertor/BranchDeleteResponseConvertor.java new file mode 100644 index 00000000000..7f3a4501fa4 --- /dev/null +++ b/serializer/seata-serializer-protobuf/src/main/java/org/apache/seata/serializer/protobuf/convertor/BranchDeleteResponseConvertor.java @@ -0,0 +1,63 @@ +package org.apache.seata.serializer.protobuf.convertor; + +import org.apache.seata.core.exception.TransactionExceptionCode; +import org.apache.seata.core.model.BranchStatus; +import org.apache.seata.core.protocol.ResultCode; +import org.apache.seata.core.protocol.transaction.BranchDeleteResponse; +import org.apache.seata.serializer.protobuf.generated.AbstractBranchEndResponseProto; +import org.apache.seata.serializer.protobuf.generated.AbstractMessageProto; +import org.apache.seata.serializer.protobuf.generated.AbstractResultMessageProto; +import org.apache.seata.serializer.protobuf.generated.AbstractTransactionResponseProto; +import org.apache.seata.serializer.protobuf.generated.BranchDeleteResponseProto; +import org.apache.seata.serializer.protobuf.generated.BranchStatusProto; +import org.apache.seata.serializer.protobuf.generated.MessageTypeProto; +import org.apache.seata.serializer.protobuf.generated.ResultCodeProto; +import org.apache.seata.serializer.protobuf.generated.TransactionExceptionCodeProto; + +/** + * BranchDeleteResponseConvertor + */ +public class BranchDeleteResponseConvertor implements PbConvertor { + @Override + public BranchDeleteResponseProto convert2Proto(BranchDeleteResponse branchDeleteResponse) { + final short typeCode = branchDeleteResponse.getTypeCode(); + + final AbstractMessageProto abstractMessage = AbstractMessageProto.newBuilder().setMessageType( + MessageTypeProto.forNumber(typeCode)).build(); + + final String msg = branchDeleteResponse.getMsg(); + final AbstractResultMessageProto abstractResultMessageProto = AbstractResultMessageProto.newBuilder().setMsg( + msg == null ? "" : msg).setResultCode(ResultCodeProto.valueOf(branchDeleteResponse.getResultCode().name())) + .setAbstractMessage(abstractMessage).build(); + + AbstractTransactionResponseProto abstractTransactionResponseProto = AbstractTransactionResponseProto + .newBuilder().setAbstractResultMessage(abstractResultMessageProto).setTransactionExceptionCode( + TransactionExceptionCodeProto.valueOf(branchDeleteResponse.getTransactionExceptionCode().name())) + .build(); + + final AbstractBranchEndResponseProto abstractBranchEndResponse = AbstractBranchEndResponseProto.newBuilder(). + setAbstractTransactionResponse(abstractTransactionResponseProto).setXid(branchDeleteResponse.getXid()) + .setBranchId(branchDeleteResponse.getBranchId()).setBranchStatus( + BranchStatusProto.forNumber(branchDeleteResponse.getBranchStatus().getCode())).build(); + + return BranchDeleteResponseProto.newBuilder().setAbstractBranchEndResponse( + abstractBranchEndResponse).build(); + } + + @Override + public BranchDeleteResponse convert2Model(BranchDeleteResponseProto branchDeleteResponseProto) { + BranchDeleteResponse branchDeleteResponse = new BranchDeleteResponse(); + final AbstractBranchEndResponseProto abstractResultMessage = branchDeleteResponseProto.getAbstractBranchEndResponse(); + branchDeleteResponse.setBranchId(branchDeleteResponseProto.getAbstractBranchEndResponse().getBranchId()); + branchDeleteResponse.setBranchStatus( + BranchStatus.get(branchDeleteResponseProto.getAbstractBranchEndResponse().getBranchStatusValue())); + branchDeleteResponse.setXid(branchDeleteResponseProto.getAbstractBranchEndResponse().getXid()); + + branchDeleteResponse.setMsg(abstractResultMessage.getAbstractTransactionResponse().getAbstractResultMessage().getMsg()); + branchDeleteResponse.setResultCode(ResultCode.valueOf(abstractResultMessage.getAbstractTransactionResponse().getAbstractResultMessage().getResultCode().name())); + branchDeleteResponse.setTransactionExceptionCode(TransactionExceptionCode.valueOf( + abstractResultMessage.getAbstractTransactionResponse().getTransactionExceptionCode().name())); + return branchDeleteResponse; + } +} + diff --git a/serializer/seata-serializer-protobuf/src/main/java/org/apache/seata/serializer/protobuf/manager/ProtobufConvertManager.java b/serializer/seata-serializer-protobuf/src/main/java/org/apache/seata/serializer/protobuf/manager/ProtobufConvertManager.java index 4ce861b155a..0d85cee72a2 100644 --- a/serializer/seata-serializer-protobuf/src/main/java/org/apache/seata/serializer/protobuf/manager/ProtobufConvertManager.java +++ b/serializer/seata-serializer-protobuf/src/main/java/org/apache/seata/serializer/protobuf/manager/ProtobufConvertManager.java @@ -17,9 +17,13 @@ package org.apache.seata.serializer.protobuf.manager; import org.apache.seata.core.protocol.BatchResultMessage; +import org.apache.seata.core.protocol.transaction.BranchDeleteRequest; +import org.apache.seata.core.protocol.transaction.BranchDeleteResponse; import org.apache.seata.serializer.protobuf.convertor.BatchResultMessageConvertor; import org.apache.seata.serializer.protobuf.convertor.BranchCommitRequestConvertor; import org.apache.seata.serializer.protobuf.convertor.BranchCommitResponseConvertor; +import org.apache.seata.serializer.protobuf.convertor.BranchDeleteRequestConvertor; +import org.apache.seata.serializer.protobuf.convertor.BranchDeleteResponseConvertor; import org.apache.seata.serializer.protobuf.convertor.BranchRegisterRequestConvertor; import org.apache.seata.serializer.protobuf.convertor.BranchRegisterResponseConvertor; import org.apache.seata.serializer.protobuf.convertor.BranchReportRequestConvertor; @@ -47,6 +51,8 @@ import org.apache.seata.serializer.protobuf.convertor.RegisterTMRequestConvertor; import org.apache.seata.serializer.protobuf.convertor.RegisterTMResponseConvertor; import org.apache.seata.serializer.protobuf.generated.BatchResultMessageProto; +import org.apache.seata.serializer.protobuf.generated.BranchDeleteRequestProto; +import org.apache.seata.serializer.protobuf.generated.BranchDeleteResponseProto; import org.apache.seata.serializer.protobuf.generated.GlobalReportRequestProto; import org.apache.seata.serializer.protobuf.generated.GlobalReportResponseProto; import org.apache.seata.core.protocol.HeartbeatMessage; @@ -164,6 +170,10 @@ private static class SingletonHolder { new GlobalReportResponseConvertor()); protobufConvertManager.convertorMap.put(UndoLogDeleteRequest.class.getName(), new UndoLogDeleteRequestConvertor()); + protobufConvertManager.convertorMap.put(BranchDeleteRequest.class.getName(), + new BranchDeleteRequestConvertor()); + protobufConvertManager.convertorMap.put(BranchDeleteResponse.class.getName(), + new BranchDeleteResponseConvertor()); protobufConvertManager.convertorMap.put(MergedWarpMessage.class.getName(), new MergedWarpMessageConvertor()); @@ -223,6 +233,10 @@ private static class SingletonHolder { GlobalReportResponseProto.class); protobufConvertManager.protoClazzMap.put(UndoLogDeleteRequestProto.getDescriptor().getFullName(), UndoLogDeleteRequestProto.class); + protobufConvertManager.protoClazzMap.put(BranchDeleteResponseProto.getDescriptor().getFullName(), + BranchDeleteResponseProto.class); + protobufConvertManager.protoClazzMap.put(BranchDeleteRequestProto.getDescriptor().getFullName(), + BranchDeleteRequestProto.class); protobufConvertManager.protoClazzMap.put(MergedWarpMessageProto.getDescriptor().getFullName(), MergedWarpMessageProto.class); diff --git a/serializer/seata-serializer-protobuf/src/main/resources/protobuf/org/apache/seata/protocol/transcation/branchDeleteRequest.proto b/serializer/seata-serializer-protobuf/src/main/resources/protobuf/org/apache/seata/protocol/transcation/branchDeleteRequest.proto new file mode 100644 index 00000000000..d4520d7ff47 --- /dev/null +++ b/serializer/seata-serializer-protobuf/src/main/resources/protobuf/org/apache/seata/protocol/transcation/branchDeleteRequest.proto @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +syntax = "proto3"; + +package org.apache.seata.protocol.protobuf; + +import "branchType.proto"; +import "abstractTransactionRequest.proto"; + +option java_multiple_files = true; +option java_outer_classname = "BranchDeleteRequest"; +option java_package = "org.apache.seata.serializer.protobuf.generated"; + + +// PublishRequest is a publish request. +message BranchDeleteRequestProto { + AbstractTransactionRequestProto abstractTransactionRequest = 1; + string xid = 2; + + int64 branchId = 3; + + BranchTypeProto branchType = 4; + + string resourceId = 5; +} diff --git a/serializer/seata-serializer-protobuf/src/main/resources/protobuf/org/apache/seata/protocol/transcation/branchDeleteResponse.proto b/serializer/seata-serializer-protobuf/src/main/resources/protobuf/org/apache/seata/protocol/transcation/branchDeleteResponse.proto new file mode 100644 index 00000000000..b9cd45a95e5 --- /dev/null +++ b/serializer/seata-serializer-protobuf/src/main/resources/protobuf/org/apache/seata/protocol/transcation/branchDeleteResponse.proto @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +syntax = "proto3"; + +package org.apache.seata.protocol.protobuf; + +import "abstractBranchEndResponse.proto"; + +option java_multiple_files = true; +option java_outer_classname = "BranchDeleteResponse"; +option java_package = "org.apache.seata.serializer.protobuf.generated"; + +// PublishRequest is a publish request. +message BranchDeleteResponseProto { + AbstractBranchEndResponseProto abstractBranchEndResponse = 1; +} \ No newline at end of file diff --git a/serializer/seata-serializer-protobuf/src/main/resources/protobuf/org/apache/seata/protocol/transcation/messageType.proto b/serializer/seata-serializer-protobuf/src/main/resources/protobuf/org/apache/seata/protocol/transcation/messageType.proto index 754ce1173c0..7652ef5cdbd 100644 --- a/serializer/seata-serializer-protobuf/src/main/resources/protobuf/org/apache/seata/protocol/transcation/messageType.proto +++ b/serializer/seata-serializer-protobuf/src/main/resources/protobuf/org/apache/seata/protocol/transcation/messageType.proto @@ -70,6 +70,14 @@ enum MessageTypeProto { * The constant TYPE_GLOBAL_LOCK_QUERY_RESULT. */ TYPE_GLOBAL_LOCK_QUERY_RESULT = 22; + /** + * The constant TYPE_BRANCH_DELETE. + */ + TYPE_BRANCH_DELETE = 23; + /** + * The constant TYPE_BRANCH_DELETE_RESULT. + */ + TYPE_BRANCH_DELETE_RESULT = 24; /** * The constant TYPE_BRANCH_COMMIT. diff --git a/serializer/seata-serializer-protobuf/src/test/java/org/apache/seata/serializer/protobuf/convertor/BranchDeleteRequestConvertorTest.java b/serializer/seata-serializer-protobuf/src/test/java/org/apache/seata/serializer/protobuf/convertor/BranchDeleteRequestConvertorTest.java new file mode 100644 index 00000000000..6d314e9c94f --- /dev/null +++ b/serializer/seata-serializer-protobuf/src/test/java/org/apache/seata/serializer/protobuf/convertor/BranchDeleteRequestConvertorTest.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.serializer.protobuf.convertor; + +import org.apache.seata.core.model.BranchType; +import org.apache.seata.core.protocol.transaction.BranchDeleteRequest; +import org.apache.seata.serializer.protobuf.generated.BranchDeleteRequestProto; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class BranchDeleteRequestConvertorTest { + @Test + public void convert2Proto() { + BranchDeleteRequest branchDeleteRequest = new BranchDeleteRequest(); + branchDeleteRequest.setBranchType(BranchType.AT); + branchDeleteRequest.setXid("xid"); + branchDeleteRequest.setResourceId("resourceId"); + branchDeleteRequest.setBranchId(123); + + BranchDeleteRequestConvertor branchDeleteRequestConvertor = new BranchDeleteRequestConvertor(); + BranchDeleteRequestProto proto = branchDeleteRequestConvertor.convert2Proto( + branchDeleteRequest); + BranchDeleteRequest realRequest = branchDeleteRequestConvertor.convert2Model(proto); + + assertThat(realRequest.getTypeCode()).isEqualTo(branchDeleteRequest.getTypeCode()); + assertThat(realRequest.getBranchType()).isEqualTo(branchDeleteRequest.getBranchType()); + assertThat(realRequest.getXid()).isEqualTo(branchDeleteRequest.getXid()); + assertThat(realRequest.getResourceId()).isEqualTo(branchDeleteRequest.getResourceId()); + assertThat(realRequest.getBranchId()).isEqualTo(branchDeleteRequest.getBranchId()); + + } +} diff --git a/serializer/seata-serializer-protobuf/src/test/java/org/apache/seata/serializer/protobuf/convertor/BranchDeleteResponseConvertorTest.java b/serializer/seata-serializer-protobuf/src/test/java/org/apache/seata/serializer/protobuf/convertor/BranchDeleteResponseConvertorTest.java new file mode 100644 index 00000000000..9f5f9c8b0b0 --- /dev/null +++ b/serializer/seata-serializer-protobuf/src/test/java/org/apache/seata/serializer/protobuf/convertor/BranchDeleteResponseConvertorTest.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.serializer.protobuf.convertor; + +import org.apache.seata.core.exception.TransactionExceptionCode; +import org.apache.seata.core.model.BranchStatus; +import org.apache.seata.core.protocol.ResultCode; +import org.apache.seata.core.protocol.transaction.BranchDeleteResponse; +import org.apache.seata.serializer.protobuf.generated.BranchDeleteResponseProto; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * The type Branch delete response convertor test. + */ +public class BranchDeleteResponseConvertorTest { + @Test + public void convert2Proto() { + BranchDeleteResponse branchDeleteResponse = new BranchDeleteResponse(); + branchDeleteResponse.setMsg("msg"); + branchDeleteResponse.setResultCode(ResultCode.Failed); + branchDeleteResponse.setTransactionExceptionCode(TransactionExceptionCode.GlobalTransactionNotExist); + branchDeleteResponse.setXid("xid"); + branchDeleteResponse.setBranchStatus(BranchStatus.PhaseTwo_Rollbacked); + branchDeleteResponse.setBranchId(123); + + BranchDeleteResponseConvertor convertor = new BranchDeleteResponseConvertor(); + BranchDeleteResponseProto proto = convertor.convert2Proto(branchDeleteResponse); + BranchDeleteResponse real = convertor.convert2Model(proto); + assertThat((real.getTypeCode())).isEqualTo(branchDeleteResponse.getTypeCode()); + assertThat((real.getMsg())).isEqualTo(branchDeleteResponse.getMsg()); + assertThat((real.getResultCode())).isEqualTo(branchDeleteResponse.getResultCode()); + assertThat((real.getTransactionExceptionCode())).isEqualTo(branchDeleteResponse.getTransactionExceptionCode()); + assertThat((real.getBranchId())).isEqualTo(branchDeleteResponse.getBranchId()); + assertThat((real.getXid())).isEqualTo(branchDeleteResponse.getXid()); + assertThat((real.getBranchStatus())).isEqualTo(branchDeleteResponse.getBranchStatus()); + } +} diff --git a/serializer/seata-serializer-seata/src/main/java/org/apache/seata/serializer/seata/MessageCodecFactory.java b/serializer/seata-serializer-seata/src/main/java/org/apache/seata/serializer/seata/MessageCodecFactory.java index 6231c72bbde..65169eac513 100644 --- a/serializer/seata-serializer-seata/src/main/java/org/apache/seata/serializer/seata/MessageCodecFactory.java +++ b/serializer/seata-serializer-seata/src/main/java/org/apache/seata/serializer/seata/MessageCodecFactory.java @@ -19,6 +19,8 @@ import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import org.apache.seata.core.protocol.transaction.BranchDeleteRequest; +import org.apache.seata.core.protocol.transaction.BranchDeleteResponse; import org.apache.seata.serializer.seata.protocol.BatchResultMessageCodec; import org.apache.seata.serializer.seata.protocol.MergeResultMessageCodec; import org.apache.seata.serializer.seata.protocol.MergedWarpMessageCodec; @@ -28,6 +30,8 @@ import org.apache.seata.serializer.seata.protocol.RegisterTMResponseCodec; import org.apache.seata.serializer.seata.protocol.transaction.BranchCommitRequestCodec; import org.apache.seata.serializer.seata.protocol.transaction.BranchCommitResponseCodec; +import org.apache.seata.serializer.seata.protocol.transaction.BranchDeleteRequestCodec; +import org.apache.seata.serializer.seata.protocol.transaction.BranchDeleteResponseCodec; import org.apache.seata.serializer.seata.protocol.transaction.BranchRegisterRequestCodec; import org.apache.seata.serializer.seata.protocol.transaction.BranchRegisterResponseCodec; import org.apache.seata.serializer.seata.protocol.transaction.BranchReportRequestCodec; @@ -137,6 +141,12 @@ public static MessageSeataCodec getMessageCodec(short typeCode, byte version) { case MessageType.TYPE_BATCH_RESULT_MSG: msgCodec = new BatchResultMessageCodec(version); break; + case MessageType.TYPE_BRANCH_DELETE: + msgCodec = new BranchDeleteRequestCodec(); + break; + case MessageType.TYPE_BRANCH_DELETE_RESULT: + msgCodec = new BranchDeleteResponseCodec(); + break; case MessageType.TYPE_GLOBAL_BEGIN: msgCodec = new GlobalBeginRequestCodec(); break; @@ -295,6 +305,12 @@ public static AbstractMessage getMessage(short typeCode) { case MessageType.TYPE_BRANCH_ROLLBACK_RESULT: abstractMessage = new BranchRollbackResponse(); break; + case MessageType.TYPE_BRANCH_DELETE: + abstractMessage = new BranchDeleteRequest(); + break; + case MessageType.TYPE_BRANCH_DELETE_RESULT: + abstractMessage = new BranchDeleteResponse(); + break; default: break; } diff --git a/serializer/seata-serializer-seata/src/main/java/org/apache/seata/serializer/seata/protocol/transaction/BranchDeleteRequestCodec.java b/serializer/seata-serializer-seata/src/main/java/org/apache/seata/serializer/seata/protocol/transaction/BranchDeleteRequestCodec.java new file mode 100644 index 00000000000..6fef1352f68 --- /dev/null +++ b/serializer/seata-serializer-seata/src/main/java/org/apache/seata/serializer/seata/protocol/transaction/BranchDeleteRequestCodec.java @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.serializer.seata.protocol.transaction; + +import io.netty.buffer.ByteBuf; +import org.apache.seata.core.model.BranchType; +import org.apache.seata.core.protocol.transaction.BranchDeleteRequest; + +import java.nio.ByteBuffer; + +/** + * BranchDeleteRequestCodec + */ +public class BranchDeleteRequestCodec extends AbstractTransactionRequestToRMCodec { + @Override + public void encode(T t, ByteBuf out) { + BranchDeleteRequest branchDeleteRequest = (BranchDeleteRequest) t; + String xid = branchDeleteRequest.getXid(); + long branchId = branchDeleteRequest.getBranchId(); + BranchType branchType = branchDeleteRequest.getBranchType(); + String resourceId = branchDeleteRequest.getResourceId(); + + // 1. xid + if (xid != null) { + byte[] bs = xid.getBytes(UTF8); + out.writeShort((short) bs.length); + if (bs.length > 0) { + out.writeBytes(bs); + } + } else { + out.writeShort((short) 0); + } + // 2. Branch Id + out.writeLong(branchId); + // 3. Branch Type + out.writeByte(branchType.ordinal()); + // 4. Resource Id + if (resourceId != null) { + byte[] bs = resourceId.getBytes(UTF8); + out.writeShort((short) bs.length); + if (bs.length > 0) { + out.writeBytes(bs); + } + } else { + out.writeShort((short) 0); + } + } + + @Override + public void decode(T t, ByteBuffer in) { + BranchDeleteRequest branchDeleteRequest = (BranchDeleteRequest) t; + + int xidLen = 0; + if (in.remaining() >= 2) { + xidLen = in.getShort(); + } + if (xidLen <= 0) { + return; + } + if (in.remaining() < xidLen) { + return; + } + byte[] bs = new byte[xidLen]; + in.get(bs); + branchDeleteRequest.setXid(new String(bs, UTF8)); + + if (in.remaining() < 8) { + return; + } + branchDeleteRequest.setBranchId(in.getLong()); + + if (in.remaining() < 1) { + return; + } + branchDeleteRequest.setBranchType(BranchType.get(in.get())); + + int resourceIdLen = 0; + if (in.remaining() < 2) { + return; + } + resourceIdLen = in.getShort(); + + if (resourceIdLen <= 0) { + return; + } + if (in.remaining() < resourceIdLen) { + return; + } + bs = new byte[resourceIdLen]; + in.get(bs); + branchDeleteRequest.setResourceId(new String(bs, UTF8)); + } +} + diff --git a/serializer/seata-serializer-seata/src/main/java/org/apache/seata/serializer/seata/protocol/transaction/BranchDeleteResponseCodec.java b/serializer/seata-serializer-seata/src/main/java/org/apache/seata/serializer/seata/protocol/transaction/BranchDeleteResponseCodec.java new file mode 100644 index 00000000000..70e203ef6f2 --- /dev/null +++ b/serializer/seata-serializer-seata/src/main/java/org/apache/seata/serializer/seata/protocol/transaction/BranchDeleteResponseCodec.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.serializer.seata.protocol.transaction; + +import org.apache.seata.core.protocol.transaction.BranchDeleteResponse; + +import java.io.Serializable; + +/** + * BranchDeleteResponseCodec + */ +public class BranchDeleteResponseCodec extends AbstractBranchEndResponseCodec implements Serializable { + @Override + public Class getMessageClassType() { + return BranchDeleteResponse.class; + } +} + diff --git a/serializer/seata-serializer-seata/src/test/java/org/apache/seata/serializer/seata/protocol/transaction/BranchDeleteRequestCodecTest.java b/serializer/seata-serializer-seata/src/test/java/org/apache/seata/serializer/seata/protocol/transaction/BranchDeleteRequestCodecTest.java new file mode 100644 index 00000000000..c0b2ba89bf2 --- /dev/null +++ b/serializer/seata-serializer-seata/src/test/java/org/apache/seata/serializer/seata/protocol/transaction/BranchDeleteRequestCodecTest.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.serializer.seata.protocol.transaction; + +import org.apache.seata.core.model.BranchType; +import org.apache.seata.core.protocol.ProtocolConstants; +import org.apache.seata.core.protocol.transaction.BranchDeleteRequest; +import org.apache.seata.serializer.seata.SeataSerializer; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * The type Branch delete request codec test. + */ +public class BranchDeleteRequestCodecTest { + + /** + * The Seata codec. + */ + SeataSerializer seataSerializer = new SeataSerializer(ProtocolConstants.VERSION); + + /** + * Test codec. + */ + @Test + public void test_codec(){ + BranchDeleteRequest branchDeleteRequest = new BranchDeleteRequest(); + branchDeleteRequest.setBranchId(112232); + branchDeleteRequest.setBranchType(BranchType.TCC); + branchDeleteRequest.setResourceId("343"); + branchDeleteRequest.setXid("123"); + + byte[] bytes = seataSerializer.serialize(branchDeleteRequest); + + BranchDeleteRequest branchDeleteRequest2 = seataSerializer.deserialize(bytes); + + assertThat(branchDeleteRequest2.getBranchId()).isEqualTo(branchDeleteRequest.getBranchId()); + assertThat(branchDeleteRequest2.getBranchType()).isEqualTo(branchDeleteRequest.getBranchType()); + assertThat(branchDeleteRequest2.getResourceId()).isEqualTo(branchDeleteRequest.getResourceId()); + assertThat(branchDeleteRequest2.getXid()).isEqualTo(branchDeleteRequest.getXid()); + } +} diff --git a/serializer/seata-serializer-seata/src/test/java/org/apache/seata/serializer/seata/protocol/transaction/BranchDeleteResponseCodecTest.java b/serializer/seata-serializer-seata/src/test/java/org/apache/seata/serializer/seata/protocol/transaction/BranchDeleteResponseCodecTest.java new file mode 100644 index 00000000000..d3ccf3d1160 --- /dev/null +++ b/serializer/seata-serializer-seata/src/test/java/org/apache/seata/serializer/seata/protocol/transaction/BranchDeleteResponseCodecTest.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.serializer.seata.protocol.transaction; + +import org.apache.seata.core.exception.TransactionExceptionCode; +import org.apache.seata.core.model.BranchStatus; +import org.apache.seata.core.protocol.ProtocolConstants; +import org.apache.seata.core.protocol.ResultCode; +import org.apache.seata.core.protocol.transaction.BranchDeleteResponse; +import org.apache.seata.serializer.seata.SeataSerializer; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * The type Branch delete response codec test. + */ +public class BranchDeleteResponseCodecTest { + + /** + * The Seata codec. + */ + SeataSerializer seataSerializer = new SeataSerializer(ProtocolConstants.VERSION); + + /** + * Test codec. + */ + @Test + public void test_codec(){ + BranchDeleteResponse branchDeleteResponse = new BranchDeleteResponse(); + branchDeleteResponse.setMsg("test"); + branchDeleteResponse.setResultCode(ResultCode.Failed); + branchDeleteResponse.setTransactionExceptionCode(TransactionExceptionCode.BranchTransactionNotExist); + branchDeleteResponse.setBranchStatus(BranchStatus.PhaseTwo_CommitFailed_XAER_NOTA_Retryable); + + byte[] bytes = seataSerializer.serialize(branchDeleteResponse); + + BranchDeleteResponse branchDeleteResponse2 = seataSerializer.deserialize(bytes); + + assertThat(branchDeleteResponse2.getMsg()).isEqualTo(branchDeleteResponse.getMsg()); + assertThat(branchDeleteResponse2.getResultCode()).isEqualTo(branchDeleteResponse.getResultCode()); + assertThat(branchDeleteResponse2.getTransactionExceptionCode()).isEqualTo(branchDeleteResponse.getTransactionExceptionCode()); + assertThat(branchDeleteResponse2.getBranchStatus()).isEqualTo(branchDeleteResponse.getBranchStatus()); + } +} diff --git a/server/src/main/java/org/apache/seata/server/console/aop/GlobalExceptionHandlerAdvice.java b/server/src/main/java/org/apache/seata/server/console/aop/GlobalExceptionHandlerAdvice.java new file mode 100644 index 00000000000..4ac21f659cd --- /dev/null +++ b/server/src/main/java/org/apache/seata/server/console/aop/GlobalExceptionHandlerAdvice.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.server.console.aop; + +import org.apache.seata.common.exception.FrameworkException; +import org.apache.seata.common.exception.ShouldNeverHappenException; +import org.apache.seata.common.result.SingleResult; +import org.apache.seata.server.console.exception.ConsoleException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; + +@ControllerAdvice +@Component +public class GlobalExceptionHandlerAdvice { + private static final Logger LOGGER = LoggerFactory.getLogger(GlobalExceptionHandlerAdvice.class); + + @ExceptionHandler(ConsoleException.class) + @ResponseBody + public SingleResult handlerConsoleException(ConsoleException ex) { + LOGGER.error("console error, reason: {}", ex.getLogMessage(), ex); + return SingleResult.failure(ex.getMessage()); + } + + @ExceptionHandler(IllegalArgumentException.class) + @ResponseBody + public SingleResult handlerIllegalArgumentException(IllegalArgumentException ex) { + LOGGER.error("IllegalArgumentException: ", ex); + return SingleResult.failure(ex.getMessage()); + } + + @ExceptionHandler(IllegalStateException.class) + @ResponseBody + public SingleResult handlerIllegalStateException(IllegalStateException ex) { + LOGGER.error("IllegalStateException: ", ex); + return SingleResult.failure(ex.getMessage()); + } + + @ExceptionHandler(ShouldNeverHappenException.class) + @ResponseBody + public SingleResult handlerShouldNeverHappenException(ShouldNeverHappenException ex) { + LOGGER.error("ShouldNeverHappenException: ", ex); + return SingleResult.failure(ex.getMessage()); + } + + @ExceptionHandler(FrameworkException.class) + @ResponseBody + public SingleResult handlerFrameworkException(FrameworkException ex) { + LOGGER.error("FrameworkException: ", ex); + return SingleResult.failure(ex.getMessage()); + } + + @ExceptionHandler(Exception.class) + @ResponseBody + public SingleResult handleException(Exception ex) { + LOGGER.error("Exception: ", ex); + return SingleResult.failure(ex.getMessage()); + } +} diff --git a/server/src/main/java/org/apache/seata/server/console/controller/BranchSessionController.java b/server/src/main/java/org/apache/seata/server/console/controller/BranchSessionController.java index 1a194b17122..12c509e78a3 100644 --- a/server/src/main/java/org/apache/seata/server/console/controller/BranchSessionController.java +++ b/server/src/main/java/org/apache/seata/server/console/controller/BranchSessionController.java @@ -17,7 +17,13 @@ package org.apache.seata.server.console.controller; import javax.annotation.Resource; + +import org.apache.seata.common.result.SingleResult; import org.apache.seata.server.console.service.BranchSessionService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -25,11 +31,55 @@ * Branch Session Controller */ @RestController -@RequestMapping("console/branchSession") +@RequestMapping("/api/v1/console/branchSession") public class BranchSessionController { + private static final Logger LOGGER = LoggerFactory.getLogger(BranchSessionController.class); @Resource(type = BranchSessionService.class) private BranchSessionService branchSessionService; + /** + * Delete branch transaction + * + * @param xid the branch of xid + * @param branchId the branch id + * @return SingleResult + */ + @DeleteMapping("deleteBranchSession") + public SingleResult deleteBranchSession(String xid, String branchId) { + if (LOGGER.isInfoEnabled()) { + LOGGER.info("manual operation to delete the branch session, xid: {} branchId: {}", xid, branchId); + } + return branchSessionService.deleteBranchSession(xid, branchId); + } + + /** + * Stop branch transaction retry + * + * @param xid the branch of xid + * @param branchId the branch id + * @return SingleResult + */ + @PutMapping("stopBranchSession") + public SingleResult stopBranchSession(String xid, String branchId) { + if (LOGGER.isInfoEnabled()) { + LOGGER.info("manual operation to stop the branch session, xid: {} branchId: {}", xid, branchId); + } + return branchSessionService.stopBranchRetry(xid, branchId); + } + /** + * Start branch transaction retry + * + * @param xid the branch of xid + * @param branchId the branch id + * @return SingleResult + */ + @PutMapping("startBranchSession") + public SingleResult startBranchRetry(String xid, String branchId) { + if (LOGGER.isInfoEnabled()) { + LOGGER.info("manual operation to start the branch session, xid: {} branchId: {}", xid, branchId); + } + return branchSessionService.startBranchRetry(xid, branchId); + } } diff --git a/server/src/main/java/org/apache/seata/server/console/controller/GlobalLockController.java b/server/src/main/java/org/apache/seata/server/console/controller/GlobalLockController.java index 95d7cabf55c..9eaeaa24995 100644 --- a/server/src/main/java/org/apache/seata/server/console/controller/GlobalLockController.java +++ b/server/src/main/java/org/apache/seata/server/console/controller/GlobalLockController.java @@ -19,9 +19,13 @@ import javax.annotation.Resource; import org.apache.seata.common.result.PageResult; +import org.apache.seata.common.result.SingleResult; import org.apache.seata.server.console.param.GlobalLockParam; import org.apache.seata.server.console.vo.GlobalLockVO; import org.apache.seata.server.console.service.GlobalLockService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; @@ -34,6 +38,7 @@ @RestController @RequestMapping("/api/v1/console/globalLock") public class GlobalLockController { + private static final Logger LOGGER = LoggerFactory.getLogger(GlobalLockController.class); @Resource(type = GlobalLockService.class) private GlobalLockService globalLockService; @@ -48,4 +53,29 @@ public PageResult query(@ModelAttribute GlobalLockParam param) { return globalLockService.query(param); } + /** + * Delete global locks + * + * @param param the param + * @return SingleResult + */ + @DeleteMapping("delete") + public SingleResult delete(@ModelAttribute GlobalLockParam param) { + if (LOGGER.isInfoEnabled()) { + LOGGER.info("manual operation to delete the global lock, param: {}", param); + } + return globalLockService.deleteLock(param); + } + + /** + * Check if the lock exist the branch session + * + * @param xid xid + * @param branchId branch id + * @return the list of GlobalLockVO + */ + @GetMapping("check") + public SingleResult check(String xid, String branchId) { + return globalLockService.check(xid, branchId); + } } diff --git a/server/src/main/java/org/apache/seata/server/console/controller/GlobalSessionController.java b/server/src/main/java/org/apache/seata/server/console/controller/GlobalSessionController.java index aa039a663b6..8c1ff6aa51f 100644 --- a/server/src/main/java/org/apache/seata/server/console/controller/GlobalSessionController.java +++ b/server/src/main/java/org/apache/seata/server/console/controller/GlobalSessionController.java @@ -18,12 +18,17 @@ import javax.annotation.Resource; +import org.apache.seata.common.result.SingleResult; import org.apache.seata.server.console.param.GlobalSessionParam; import org.apache.seata.common.result.PageResult; import org.apache.seata.server.console.vo.GlobalSessionVO; import org.apache.seata.server.console.service.GlobalSessionService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -33,6 +38,7 @@ @RestController @RequestMapping("/api/v1/console/globalSession") public class GlobalSessionController { + private static final Logger LOGGER = LoggerFactory.getLogger(GlobalSessionController.class); @Resource(type = GlobalSessionService.class) private GlobalSessionService globalSessionService; @@ -47,4 +53,73 @@ public PageResult query(@ModelAttribute GlobalSessionParam para return globalSessionService.query(param); } + /** + * Delete the global session + * + * @param xid The xid + * @return SingleResult + */ + @DeleteMapping("deleteGlobalSession") + public SingleResult deleteGlobalSession(String xid) { + if (LOGGER.isInfoEnabled()) { + LOGGER.info("manual operation to delete the global session, xid: {}", xid); + } + return globalSessionService.deleteGlobalSession(xid); + } + + /** + * Stop the global session retry + * + * @param xid The xid + * @return SingleResult + */ + @PutMapping("stopGlobalSession") + public SingleResult stopGlobalSession(String xid) { + if (LOGGER.isInfoEnabled()) { + LOGGER.info("manual operation to stop the global session, xid: {}", xid); + } + return globalSessionService.stopGlobalRetry(xid); + } + + /** + * Start the global session retry + * + * @param xid The xid + * @return SingleResult + */ + @PutMapping("startGlobalSession") + public SingleResult startGlobalSession(String xid) { + if (LOGGER.isInfoEnabled()) { + LOGGER.info("manual operation to start the global session, xid: {}", xid); + } + return globalSessionService.startGlobalRetry(xid); + } + + /** + * Send global session to commit or rollback to rm + * + * @param xid The xid + * @return SingleResult + */ + @PutMapping("sendCommitOrRollback") + public SingleResult sendCommitOrRollback(String xid) { + if (LOGGER.isInfoEnabled()) { + LOGGER.info("manual operation to commit or rollback the global session, xid: {}", xid); + } + return globalSessionService.sendCommitOrRollback(xid); + } + + /** + * Change the global session status + * + * @param xid The xid + * @return SingleResult + */ + @PutMapping("changeGlobalStatus") + public SingleResult changeGlobalStatus(String xid) { + if (LOGGER.isInfoEnabled()) { + LOGGER.info("manual operation to change the global session, xid: {}", xid); + } + return globalSessionService.changeGlobalStatus(xid); + } } diff --git a/server/src/main/java/org/apache/seata/server/console/exception/ConsoleException.java b/server/src/main/java/org/apache/seata/server/console/exception/ConsoleException.java new file mode 100644 index 00000000000..9f71db64291 --- /dev/null +++ b/server/src/main/java/org/apache/seata/server/console/exception/ConsoleException.java @@ -0,0 +1,23 @@ +package org.apache.seata.server.console.exception; + +public class ConsoleException extends RuntimeException { + /** + * use for globalExceptionHandlerAdvice + * + * @see org.apache.seata.server.console.aop.GlobalExceptionHandlerAdvice + */ + private String logMessage; + + public ConsoleException(Throwable cause, String logMessage) { + super(cause); + this.logMessage = logMessage; + } + + public String getLogMessage() { + return logMessage; + } + + public void setLogMessage(String logMessage) { + this.logMessage = logMessage; + } +} diff --git a/server/src/main/java/org/apache/seata/server/console/impl/AbstractBranchService.java b/server/src/main/java/org/apache/seata/server/console/impl/AbstractBranchService.java new file mode 100644 index 00000000000..1ca67827176 --- /dev/null +++ b/server/src/main/java/org/apache/seata/server/console/impl/AbstractBranchService.java @@ -0,0 +1,93 @@ +package org.apache.seata.server.console.impl; + +import org.apache.seata.common.result.SingleResult; +import org.apache.seata.core.model.BranchStatus; +import org.apache.seata.core.model.GlobalStatus; +import org.apache.seata.server.console.exception.ConsoleException; +import org.apache.seata.server.console.service.BranchSessionService; +import org.apache.seata.server.session.BranchSession; +import org.apache.seata.server.session.GlobalSession; + +public abstract class AbstractBranchService extends AbstractService implements BranchSessionService { + @Override + public SingleResult stopBranchRetry(String xid, String branchId) { + CheckResult checkResult = commonCheckAndGetGlobalStatus(xid, branchId); + GlobalSession globalSession = checkResult.getGlobalSession(); + // saga is not support to operate + if (globalSession.isSaga()) { + throw new IllegalArgumentException("saga can not operate branch transactions because it have no determinative role"); + } + BranchSession branchSession = checkResult.getBranchSession(); + BranchStatus branchStatus = branchSession.getStatus(); + if (branchStatus.getCode() == BranchStatus.STOP_RETRY.getCode()) { + throw new IllegalArgumentException("current branch session is already stop"); + } + // For BranchStatus.PhaseOne_Done is finished, will remove soon, thus no support + if (branchStatus != BranchStatus.Unknown && branchStatus != BranchStatus.Registered && + branchStatus != BranchStatus.PhaseOne_Done) { + throw new IllegalArgumentException("current branch session is not support to stop"); + } + GlobalStatus status = globalSession.getStatus(); + BranchStatus newStatus = RETRY_STATUS.contains(status) || GlobalStatus.Rollbacking.equals(status) || + GlobalStatus.Committing.equals(status) || GlobalStatus.StopRollbackRetry.equals(status) || + GlobalStatus.StopCommitRetry.equals(status) ? BranchStatus.STOP_RETRY : null; + if (newStatus == null) { + throw new IllegalArgumentException("wrong status for global status"); + } + branchSession.setStatus(newStatus); + try { + globalSession.changeBranchStatus(branchSession, newStatus); + } catch (Exception e) { + throw new ConsoleException(e, String.format("stop branch session retry fail, xid:%s, branchId:%s", xid, branchId)); + } + return SingleResult.success(); + } + + @Override + public SingleResult startBranchRetry(String xid, String branchId) { + CheckResult checkResult = commonCheckAndGetGlobalStatus(xid, branchId); + GlobalSession globalSession = checkResult.getGlobalSession(); + // saga is not support to operate + if (globalSession.isSaga()) { + throw new IllegalArgumentException("saga can not operate branch transactions because it have no determinative role"); + } + BranchSession branchSession = checkResult.getBranchSession(); + BranchStatus branchStatus = branchSession.getStatus(); + if (!BranchStatus.STOP_RETRY.equals(branchStatus)) { + throw new IllegalArgumentException("current branch transactions status is not support to start retry"); + } + // BranchStatus.PhaseOne_Done and BranchStatus.Registered will become BranchStatus.Registered + BranchStatus newStatus = BranchStatus.Registered; + branchSession.setStatus(newStatus); + try { + globalSession.changeBranchStatus(branchSession, newStatus); + } catch (Exception e) { + throw new ConsoleException(e, String.format("start branch session retry fail, xid:%s, branchId:%s", xid, branchId)); + } + return SingleResult.success(); + } + + @Override + public SingleResult deleteBranchSession(String xid, String branchId) { + CheckResult checkResult = commonCheckAndGetGlobalStatus(xid, branchId); + GlobalSession globalSession = checkResult.getGlobalSession(); + // saga is not support to operate + if (globalSession.isSaga()) { + throw new IllegalArgumentException("saga can not operate branch transactions because it have no determinative role"); + } + GlobalStatus globalStatus = globalSession.getStatus(); + BranchSession branchSession = checkResult.getBranchSession(); + if (FAIL_STATUS.contains(globalStatus) || RETRY_STATUS.contains(globalStatus) + || FINISH_STATUS.contains(globalStatus) || GlobalStatus.StopRollbackRetry == globalStatus + || GlobalStatus.StopCommitRetry == globalStatus || GlobalStatus.Deleting == globalStatus) { + try { + boolean deleted = doDeleteBranch(globalSession, branchSession); + return deleted ? SingleResult.success() : + SingleResult.failure("delete branch fail, please retry again later"); + } catch (Exception e) { + throw new ConsoleException(e, String.format("delete branch session fail, xid:%s, branchId:%s", xid, branchId)); + } + } + throw new IllegalArgumentException("current global transaction is not support delete branch transaction"); + } +} diff --git a/server/src/main/java/org/apache/seata/server/console/impl/AbstractGlobalService.java b/server/src/main/java/org/apache/seata/server/console/impl/AbstractGlobalService.java new file mode 100644 index 00000000000..156d645043e --- /dev/null +++ b/server/src/main/java/org/apache/seata/server/console/impl/AbstractGlobalService.java @@ -0,0 +1,128 @@ +package org.apache.seata.server.console.impl; + +import org.apache.seata.common.result.SingleResult; +import org.apache.seata.core.model.GlobalStatus; +import org.apache.seata.server.console.exception.ConsoleException; +import org.apache.seata.server.console.service.GlobalSessionService; +import org.apache.seata.server.coordinator.DefaultCoordinator; +import org.apache.seata.server.session.BranchSession; +import org.apache.seata.server.session.GlobalSession; +import org.apache.seata.server.session.SessionHolder; + +import java.util.ArrayList; +import java.util.List; + +public abstract class AbstractGlobalService extends AbstractService implements GlobalSessionService { + @Override + public SingleResult deleteGlobalSession(String xid) { + GlobalSession globalSession = checkGlobalSession(xid); + GlobalStatus globalStatus = globalSession.getStatus(); + if (FAIL_STATUS.contains(globalStatus) || RETRY_STATUS.contains(globalStatus) || FINISH_STATUS.contains(globalStatus) + || GlobalStatus.Deleting.equals(globalStatus) || GlobalStatus.StopCommitRetry.equals(globalStatus) + || GlobalStatus.StopRollbackRetry.equals(globalStatus)) { + try { + if (!GlobalStatus.Deleting.equals(globalStatus)) { + globalSession.changeGlobalStatus(GlobalStatus.Deleting); + } + List branchSessions = globalSession.getBranchSessions(); + List iteratorBranchSessions = new ArrayList<>(branchSessions); + for (BranchSession branchSession : iteratorBranchSessions) { + if (!doDeleteBranch(globalSession, branchSession)) { + return SingleResult.failure("Delete branch fail, please try again"); + } + } + globalSession.end(); + return SingleResult.success(null); + } catch (Exception e) { + throw new ConsoleException(e, String.format("delete global session fail, xid:%s", xid)); + } + } + throw new IllegalArgumentException("current global transaction status is not support deleted"); + } + + @Override + public SingleResult stopGlobalRetry(String xid) { + GlobalSession globalSession = checkGlobalSession(xid); + GlobalStatus globalStatus = globalSession.getStatus(); + GlobalStatus newStatus = RETRY_COMMIT_STATUS.contains(globalStatus) || + GlobalStatus.Committing.equals(globalStatus) ? GlobalStatus.StopCommitRetry : + RETRY_ROLLBACK_STATUS.contains(globalStatus) || + GlobalStatus.Rollbacking.equals(globalStatus) ? GlobalStatus.StopRollbackRetry : null; + if (newStatus == null) { + throw new IllegalArgumentException("current global transaction status is not support stop"); + } + try { + globalSession.changeGlobalStatus(newStatus); + return SingleResult.success(); + } catch (Exception e) { + throw new ConsoleException(e, String.format("Stop global session retry fail, xid:%s", xid)); + } + } + + @Override + public SingleResult startGlobalRetry(String xid) { + GlobalSession globalSession = checkGlobalSession(xid); + GlobalStatus globalStatus = globalSession.getStatus(); + GlobalStatus newStatus = GlobalStatus.StopCommitRetry.equals(globalStatus) ? GlobalStatus.CommitRetrying : + GlobalStatus.StopRollbackRetry.equals(globalStatus) ? GlobalStatus.RollbackRetrying : null; + if (newStatus == null) { + throw new IllegalArgumentException("current global transaction status is not support start"); + } + try { + globalSession.changeGlobalStatus(newStatus); + return SingleResult.success(); + } catch (Exception e) { + throw new ConsoleException(e, String.format("Start global session retry fail, xid:%s", xid)); + } + } + + @Override + public SingleResult sendCommitOrRollback(String xid) { + GlobalSession globalSession = checkGlobalSession(xid); + GlobalStatus globalStatus = globalSession.getStatus(); + try { + boolean res; + if (RETRY_COMMIT_STATUS.contains(globalStatus) || GlobalStatus.Committing.equals(globalStatus) + || GlobalStatus.StopCommitRetry.equals(globalStatus)) { + res = DefaultCoordinator.getInstance().getCore().doGlobalCommit(globalSession, false); + if (res && globalSession.hasBranch() && globalSession.hasATBranch()) { + globalSession.clean(); + globalSession.asyncCommit(); + } else if (res && SessionHolder.findGlobalSession(xid) != null) { + globalSession.end(); + } + } else if (RETRY_ROLLBACK_STATUS.contains(globalStatus) || GlobalStatus.Rollbacking.equals(globalStatus) + || GlobalStatus.StopRollbackRetry.equals(globalStatus)) { + res = DefaultCoordinator.getInstance().getCore().doGlobalRollback(globalSession, false); + // the record is not deleted + if (res && SessionHolder.findGlobalSession(xid) != null) { + globalSession.changeGlobalStatus(GlobalStatus.Rollbacked); + globalSession.end(); + } + } else { + throw new IllegalArgumentException("current global transaction status is not support to do"); + } + return res ? SingleResult.success() : + SingleResult.failure("Commit or rollback fail, please try again"); + } catch (Exception e) { + throw new ConsoleException(e, String.format("send commit or rollback to rm fail, xid:%s", xid)); + } + } + + @Override + public SingleResult changeGlobalStatus(String xid) { + GlobalSession globalSession = checkGlobalSession(xid); + GlobalStatus globalStatus = globalSession.getStatus(); + GlobalStatus newStatus = FAIL_COMMIT_STATUS.contains(globalStatus) ? GlobalStatus.CommitRetrying : + FAIL_ROLLBACK_STATUS.contains(globalStatus) ? GlobalStatus.RollbackRetrying : null; + if (newStatus == null) { + throw new IllegalArgumentException("current global transaction status is not support to change"); + } + try { + globalSession.changeGlobalStatus(newStatus); + return SingleResult.success(); + } catch (Exception e) { + throw new ConsoleException(e, String.format("change global status fail, xid:%s", xid)); + } + } +} diff --git a/server/src/main/java/org/apache/seata/server/console/impl/AbstractLockService.java b/server/src/main/java/org/apache/seata/server/console/impl/AbstractLockService.java new file mode 100644 index 00000000000..9f5f3387df6 --- /dev/null +++ b/server/src/main/java/org/apache/seata/server/console/impl/AbstractLockService.java @@ -0,0 +1,28 @@ +package org.apache.seata.server.console.impl; + +import org.apache.seata.common.util.StringUtils; +import org.apache.seata.common.result.SingleResult; +import org.apache.seata.server.console.param.GlobalLockParam; +import org.apache.seata.server.console.service.GlobalLockService; + +public abstract class AbstractLockService extends AbstractService implements GlobalLockService { + + @Override + public SingleResult check(String xid, String branchId) { + try { + commonCheckAndGetGlobalStatus(xid, branchId); + } catch (IllegalArgumentException e) { + return SingleResult.success(Boolean.FALSE); + } + return SingleResult.success(Boolean.TRUE); + } + + protected void checkDeleteLock(GlobalLockParam param) { + commonCheck(param.getXid(), param.getBranchId()); + if (StringUtils.isBlank(param.getTableName()) || StringUtils.isBlank(param.getPk()) + || StringUtils.isBlank(param.getResourceId())) { + throw new IllegalArgumentException("tableName or resourceId or pk can not be empty"); + } + } +} + diff --git a/server/src/main/java/org/apache/seata/server/console/impl/AbstractService.java b/server/src/main/java/org/apache/seata/server/console/impl/AbstractService.java new file mode 100644 index 00000000000..571ef3383eb --- /dev/null +++ b/server/src/main/java/org/apache/seata/server/console/impl/AbstractService.java @@ -0,0 +1,145 @@ +package org.apache.seata.server.console.impl; + +import org.apache.seata.common.util.StringUtils; +import org.apache.seata.core.exception.TransactionException; +import org.apache.seata.core.model.BranchStatus; +import org.apache.seata.core.model.GlobalStatus; +import org.apache.seata.server.coordinator.DefaultCoordinator; +import org.apache.seata.server.lock.LockManager; +import org.apache.seata.server.lock.LockerManagerFactory; +import org.apache.seata.server.session.BranchSession; +import org.apache.seata.server.session.GlobalSession; +import org.apache.seata.server.session.SessionHolder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.TimeoutException; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * The abstract service. + */ +public abstract class AbstractService { + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractService.class); + + protected final LockManager lockManager = LockerManagerFactory.getLockManager(); + + protected static final List RETRY_COMMIT_STATUS = Arrays.asList(GlobalStatus.CommitRetrying, + GlobalStatus.Committed); + protected static final List RETRY_ROLLBACK_STATUS = Arrays.asList(GlobalStatus.RollbackRetrying, + GlobalStatus.TimeoutRollbackRetrying, GlobalStatus.TimeoutRollbacking); + + protected static final List RETRY_STATUS = Stream.concat(RETRY_COMMIT_STATUS.stream(), + RETRY_ROLLBACK_STATUS.stream()).collect(Collectors.toList()); + + protected static final List FAIL_COMMIT_STATUS = Arrays.asList(GlobalStatus.CommitFailed, + GlobalStatus.CommitRetryTimeout); + + protected static final List FAIL_ROLLBACK_STATUS = Arrays.asList(GlobalStatus.TimeoutRollbacked, + GlobalStatus.RollbackFailed, GlobalStatus.RollbackRetryTimeout); + + protected static final List FAIL_STATUS = Stream.concat(FAIL_COMMIT_STATUS.stream(), + FAIL_ROLLBACK_STATUS.stream()).collect(Collectors.toList()); + + protected static final List FINISH_STATUS = Arrays.asList(GlobalStatus.Committed, + GlobalStatus.Finished, GlobalStatus.Rollbacked); + + protected void commonCheck(String xid, String branchId) { + if (StringUtils.isBlank(xid)) { + throw new IllegalArgumentException("Wrong parameter for xid"); + } + if (StringUtils.isBlank(branchId)) { + throw new IllegalArgumentException("Wrong parameter for branchId"); + } + try { + Long.parseLong(branchId); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Wrong parameter for branchId, branch Id is not number"); + } + } + + protected GlobalSession checkGlobalSession(String xid) { + if (StringUtils.isBlank(xid)) { + throw new IllegalArgumentException("Wrong parameter for xid"); + } + GlobalSession globalSession = SessionHolder.findGlobalSession(xid); + if (Objects.isNull(globalSession)) { + throw new IllegalArgumentException("Global session is not exist, may be finished"); + } + return globalSession; + } + + /** + * check if exist global transaction and branch transaction + * + * @param xid xid + * @param branchId branchId + * @return CheckResult, throw IllegalArgumentException if not exist + */ + protected CheckResult commonCheckAndGetGlobalStatus(String xid, String branchId) { + commonCheck(xid, branchId); + GlobalSession globalSession = SessionHolder.findGlobalSession(xid); + if (Objects.isNull(globalSession)) { + throw new IllegalArgumentException("global session is not exist, may be finished"); + } + List branchSessions = globalSession.getBranchSessions(); + Long paramBranchId = Long.valueOf(branchId); + BranchSession branchSession = branchSessions.stream() + .filter(session -> paramBranchId.equals(session.getBranchId())) + .findAny() + .orElseThrow(() -> new IllegalArgumentException("branch session is not exist, may be finished")); + return new CheckResult(globalSession, branchSession); + } + + protected boolean doDeleteBranch(GlobalSession globalSession, BranchSession branchSession) throws TimeoutException, TransactionException { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Branch delete start, xid:{} branchId:{} branchType:{}", + branchSession.getXid(), branchSession.getBranchId(), branchSession.getBranchType()); + } + if (branchSession.getStatus() == BranchStatus.PhaseOne_Failed) { + globalSession.removeBranch(branchSession); + return true; + } + boolean result = DefaultCoordinator.getInstance().getCore().doBranchDelete(globalSession, branchSession); + if (result) { + if (branchSession.isAT()) { + result = lockManager.releaseLock(branchSession); + } + if (result) { + globalSession.removeBranch(branchSession); + return true; + } + } + return false; + } + + protected static class CheckResult { + private GlobalSession globalSession; + private BranchSession branchSession; + + public CheckResult(GlobalSession globalSession, BranchSession branchSession) { + this.globalSession = globalSession; + this.branchSession = branchSession; + } + + public GlobalSession getGlobalSession() { + return globalSession; + } + + public void setGlobalSession(GlobalSession globalSession) { + this.globalSession = globalSession; + } + + public BranchSession getBranchSession() { + return branchSession; + } + + public void setBranchSession(BranchSession branchSession) { + this.branchSession = branchSession; + } + } +} diff --git a/server/src/main/java/org/apache/seata/server/console/impl/db/BranchSessionDBServiceImpl.java b/server/src/main/java/org/apache/seata/server/console/impl/db/BranchSessionDBServiceImpl.java index 4746e109b26..375503445dd 100644 --- a/server/src/main/java/org/apache/seata/server/console/impl/db/BranchSessionDBServiceImpl.java +++ b/server/src/main/java/org/apache/seata/server/console/impl/db/BranchSessionDBServiceImpl.java @@ -35,6 +35,7 @@ import org.apache.seata.common.result.PageResult; import org.apache.seata.core.store.db.DataSourceProvider; import org.apache.seata.core.store.db.sql.log.LogStoreSqlsFactory; +import org.apache.seata.server.console.impl.AbstractBranchService; import org.apache.seata.server.console.service.BranchSessionService; import org.apache.seata.server.console.vo.BranchSessionVO; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; @@ -49,7 +50,7 @@ @Component @org.springframework.context.annotation.Configuration @ConditionalOnExpression("#{'db'.equals('${sessionMode}')}") -public class BranchSessionDBServiceImpl implements BranchSessionService { +public class BranchSessionDBServiceImpl extends AbstractBranchService implements BranchSessionService { private String branchTable; diff --git a/server/src/main/java/org/apache/seata/server/console/impl/db/GlobalLockDBServiceImpl.java b/server/src/main/java/org/apache/seata/server/console/impl/db/GlobalLockDBServiceImpl.java index 4ac3ea95755..b1ec8d40d96 100644 --- a/server/src/main/java/org/apache/seata/server/console/impl/db/GlobalLockDBServiceImpl.java +++ b/server/src/main/java/org/apache/seata/server/console/impl/db/GlobalLockDBServiceImpl.java @@ -28,6 +28,7 @@ import org.apache.seata.common.ConfigurationKeys; import org.apache.seata.common.exception.StoreException; import org.apache.seata.common.loader.EnhancedServiceLoader; +import org.apache.seata.common.result.SingleResult; import org.apache.seata.common.util.IOUtil; import org.apache.seata.common.util.PageUtil; import org.apache.seata.common.util.StringUtils; @@ -36,13 +37,18 @@ import org.apache.seata.common.result.PageResult; import org.apache.seata.core.store.db.DataSourceProvider; import org.apache.seata.core.store.db.sql.lock.LockStoreSqlFactory; +import org.apache.seata.server.console.exception.ConsoleException; +import org.apache.seata.server.console.impl.AbstractLockService; import org.apache.seata.server.console.param.GlobalLockParam; import org.apache.seata.server.console.service.GlobalLockService; import org.apache.seata.server.console.vo.GlobalLockVO; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Component; import static org.apache.seata.common.DefaultValues.DEFAULT_LOCK_DB_TABLE; +import static org.apache.seata.core.constants.RedisKeyConstants.SPLIT; /** @@ -52,8 +58,8 @@ @Component @org.springframework.context.annotation.Configuration @ConditionalOnExpression("#{'db'.equals('${lockMode}')}") -public class GlobalLockDBServiceImpl implements GlobalLockService { - +public class GlobalLockDBServiceImpl extends AbstractLockService implements GlobalLockService { + private static final Logger LOGGER = LoggerFactory.getLogger(GlobalLockDBServiceImpl.class); private String lockTable; private String dbType; @@ -108,6 +114,7 @@ public PageResult query(GlobalLockParam param) { count = countRs.getInt(1); } } catch (SQLException e) { + LOGGER.error("query global lock exception, param:{}", param, e); throw new StoreException(e); } finally { IOUtil.close(rs, countRs, ps, countPs, conn); @@ -115,6 +122,31 @@ public PageResult query(GlobalLockParam param) { return PageResult.success(list, count, param.getPageNum(), param.getPageSize()); } + @Override + public SingleResult deleteLock(GlobalLockParam param) { + checkDeleteLock(param); + String rowKey = buildRowKey(param.getTableName(), param.getPk(), param.getResourceId()); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("start to delete global lock,xid:{} branchId:{} row key:{} ", + param.getXid(), param.getBranchId(), rowKey); + } + String deleteLockSql = LockStoreSqlFactory.getLogStoreSql(dbType).getDeleteLockSql(lockTable); + try (Connection conn = dataSource.getConnection(); + PreparedStatement ps = conn.prepareStatement(deleteLockSql)) { + ps.setString(1, rowKey); + ps.setString(2, param.getXid()); + ps.executeUpdate(); + } catch (Exception e) { + throw new ConsoleException(e, String.format("delete global lock," + + "xid:%s ,branchId:%s ,row key:%s failed", param.getXid(), param.getBranchId(), rowKey)); + } + return SingleResult.success(); + } + + private String buildRowKey(String tableName, String pk, String resourceId) { + return resourceId + SPLIT + tableName + SPLIT + pk; + } + private String getWhereConditionByParam(GlobalLockParam param, List sqlParamList) { StringBuilder whereConditionBuilder = new StringBuilder(); if (StringUtils.isNotBlank(param.getXid())) { diff --git a/server/src/main/java/org/apache/seata/server/console/impl/db/GlobalSessionDBServiceImpl.java b/server/src/main/java/org/apache/seata/server/console/impl/db/GlobalSessionDBServiceImpl.java index f27b896dc5f..4bc5b16969d 100644 --- a/server/src/main/java/org/apache/seata/server/console/impl/db/GlobalSessionDBServiceImpl.java +++ b/server/src/main/java/org/apache/seata/server/console/impl/db/GlobalSessionDBServiceImpl.java @@ -38,6 +38,7 @@ import org.apache.seata.common.result.PageResult; import org.apache.seata.core.store.db.DataSourceProvider; import org.apache.seata.core.store.db.sql.log.LogStoreSqlsFactory; +import org.apache.seata.server.console.impl.AbstractGlobalService; import org.apache.seata.server.console.param.GlobalSessionParam; import org.apache.seata.server.console.service.BranchSessionService; import org.apache.seata.server.console.service.GlobalSessionService; @@ -55,7 +56,7 @@ @Component @org.springframework.context.annotation.Configuration @ConditionalOnExpression("#{'db'.equals('${sessionMode}')}") -public class GlobalSessionDBServiceImpl implements GlobalSessionService { +public class GlobalSessionDBServiceImpl extends AbstractGlobalService implements GlobalSessionService { private String globalTable; diff --git a/server/src/main/java/org/apache/seata/server/console/impl/file/BranchSessionFileServiceImpl.java b/server/src/main/java/org/apache/seata/server/console/impl/file/BranchSessionFileServiceImpl.java index 54741fdab6c..efd2337d158 100644 --- a/server/src/main/java/org/apache/seata/server/console/impl/file/BranchSessionFileServiceImpl.java +++ b/server/src/main/java/org/apache/seata/server/console/impl/file/BranchSessionFileServiceImpl.java @@ -16,13 +16,22 @@ */ package org.apache.seata.server.console.impl.file; -import org.apache.seata.common.exception.NotSupportYetException; +import org.apache.seata.common.util.StringUtils; +import org.apache.seata.server.console.impl.AbstractBranchService; import org.apache.seata.server.console.vo.BranchSessionVO; import org.apache.seata.common.result.PageResult; import org.apache.seata.server.console.service.BranchSessionService; +import org.apache.seata.server.session.GlobalSession; +import org.apache.seata.server.session.SessionHolder; +import org.apache.seata.server.storage.SessionConverter; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Component; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Set; + /** * Branch Session File ServiceImpl * @@ -30,10 +39,22 @@ @Component @org.springframework.context.annotation.Configuration @ConditionalOnExpression("#{'file'.equals('${sessionMode}')}") -public class BranchSessionFileServiceImpl implements BranchSessionService { +public class BranchSessionFileServiceImpl extends AbstractBranchService implements BranchSessionService { @Override public PageResult queryByXid(String xid) { - throw new NotSupportYetException(); + if (StringUtils.isBlank(xid)) { + throw new IllegalArgumentException("xid should not be blank"); + } + List branchSessionVOList = new ArrayList<>(0); + final Collection allSessions = SessionHolder.getRootSessionManager().allSessions(); + for (GlobalSession globalSession : allSessions) { + if (globalSession.getXid().equals(xid)) { + Set branchSessionVOS = SessionConverter.convertBranchSession(globalSession.getBranchSessions()); + branchSessionVOList = new ArrayList<>(branchSessionVOS); + break; + } + } + return PageResult.success(branchSessionVOList, branchSessionVOList.size(), 0, 0, 0); } } diff --git a/server/src/main/java/org/apache/seata/server/console/impl/file/GlobalLockFileServiceImpl.java b/server/src/main/java/org/apache/seata/server/console/impl/file/GlobalLockFileServiceImpl.java index 0aa574331b6..3480b4c185b 100644 --- a/server/src/main/java/org/apache/seata/server/console/impl/file/GlobalLockFileServiceImpl.java +++ b/server/src/main/java/org/apache/seata/server/console/impl/file/GlobalLockFileServiceImpl.java @@ -23,8 +23,10 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import org.apache.seata.common.result.SingleResult; import org.apache.seata.common.util.CollectionUtils; import org.apache.seata.common.util.StringUtils; +import org.apache.seata.server.console.impl.AbstractLockService; import org.apache.seata.server.console.param.GlobalLockParam; import org.apache.seata.common.result.PageResult; import org.apache.seata.server.console.vo.GlobalLockVO; @@ -49,7 +51,7 @@ @Component @org.springframework.context.annotation.Configuration @ConditionalOnExpression("#{'file'.equals('${lockMode}')}") -public class GlobalLockFileServiceImpl implements GlobalLockService { +public class GlobalLockFileServiceImpl extends AbstractLockService implements GlobalLockService { @Override public PageResult query(GlobalLockParam param) { @@ -71,6 +73,11 @@ public PageResult query(GlobalLockParam param) { } + @Override + public SingleResult deleteLock(GlobalLockParam param) { + throw new IllegalStateException("Not Support to delete lock in file mode"); + } + /** * filter with tableName and generate RowLock * diff --git a/server/src/main/java/org/apache/seata/server/console/impl/file/GlobalSessionFileServiceImpl.java b/server/src/main/java/org/apache/seata/server/console/impl/file/GlobalSessionFileServiceImpl.java index f2821f4ad83..5d62e352e59 100644 --- a/server/src/main/java/org/apache/seata/server/console/impl/file/GlobalSessionFileServiceImpl.java +++ b/server/src/main/java/org/apache/seata/server/console/impl/file/GlobalSessionFileServiceImpl.java @@ -22,6 +22,7 @@ import java.util.function.Predicate; import java.util.stream.Collectors; +import org.apache.seata.server.console.impl.AbstractGlobalService; import org.apache.seata.server.console.param.GlobalSessionParam; import org.apache.seata.common.result.PageResult; import org.apache.seata.server.console.vo.GlobalSessionVO; @@ -42,7 +43,7 @@ @Component @org.springframework.context.annotation.Configuration @ConditionalOnExpression("#{'file'.equals('${sessionMode}')}") -public class GlobalSessionFileServiceImpl implements GlobalSessionService { +public class GlobalSessionFileServiceImpl extends AbstractGlobalService implements GlobalSessionService { @Override public PageResult query(GlobalSessionParam param) { diff --git a/server/src/main/java/org/apache/seata/server/console/impl/redis/BranchSessionRedisServiceImpl.java b/server/src/main/java/org/apache/seata/server/console/impl/redis/BranchSessionRedisServiceImpl.java index 77b4accb63f..ca52abc0c7d 100644 --- a/server/src/main/java/org/apache/seata/server/console/impl/redis/BranchSessionRedisServiceImpl.java +++ b/server/src/main/java/org/apache/seata/server/console/impl/redis/BranchSessionRedisServiceImpl.java @@ -21,6 +21,7 @@ import org.apache.seata.common.util.CollectionUtils; import org.apache.seata.common.util.StringUtils; import org.apache.seata.common.result.PageResult; +import org.apache.seata.server.console.impl.AbstractBranchService; import org.apache.seata.server.console.vo.BranchSessionVO; import org.apache.seata.core.store.BranchTransactionDO; import org.apache.seata.server.console.service.BranchSessionService; @@ -37,7 +38,7 @@ @Component @org.springframework.context.annotation.Configuration @ConditionalOnExpression("#{'redis'.equals('${sessionMode}')}") -public class BranchSessionRedisServiceImpl implements BranchSessionService { +public class BranchSessionRedisServiceImpl extends AbstractBranchService implements BranchSessionService { @Override public PageResult queryByXid(String xid) { diff --git a/server/src/main/java/org/apache/seata/server/console/impl/redis/GlobalLockRedisServiceImpl.java b/server/src/main/java/org/apache/seata/server/console/impl/redis/GlobalLockRedisServiceImpl.java index 0ee7f6a4720..5be160d03f6 100644 --- a/server/src/main/java/org/apache/seata/server/console/impl/redis/GlobalLockRedisServiceImpl.java +++ b/server/src/main/java/org/apache/seata/server/console/impl/redis/GlobalLockRedisServiceImpl.java @@ -20,7 +20,14 @@ import java.util.Arrays; import java.util.List; import java.util.Map; +import java.util.StringJoiner; + +import org.apache.seata.common.result.SingleResult; import org.apache.seata.common.util.CollectionUtils; +import org.apache.seata.common.util.StringUtils; +import org.apache.seata.server.console.impl.AbstractLockService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Component; import org.apache.seata.common.util.BeanUtils; @@ -44,7 +51,8 @@ @Component @org.springframework.context.annotation.Configuration @ConditionalOnExpression("#{'redis'.equals('${lockMode}')}") -public class GlobalLockRedisServiceImpl implements GlobalLockService { +public class GlobalLockRedisServiceImpl extends AbstractLockService implements GlobalLockService { + private static final Logger LOGGER = LoggerFactory.getLogger(GlobalLockRedisServiceImpl.class); @Override public PageResult query(GlobalLockParam param) { @@ -69,6 +77,47 @@ public PageResult query(GlobalLockParam param) { } } + @Override + public SingleResult deleteLock(GlobalLockParam param) { + checkDeleteLock(param); + String rowKey = buildRowKey(param.getTableName(), param.getPk(), param.getResourceId()); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("start to delete global lock,xid:{} branchId:{} row key:{} ", + param.getXid(), param.getBranchId(), rowKey); + } + try (Jedis jedis = JedisPooledFactory.getJedisInstance()) { + // del the row key + jedis.del(rowKey); + String xidLockKey = buildXidLockKey(param.getXid()); + String rowKeys = jedis.hget(xidLockKey, param.getBranchId()); + if (StringUtils.isNotBlank(rowKeys)) { + // Check whether other locks exist. If so, update it. If not, delete it + if (rowKeys.contains(ROW_LOCK_KEY_SPLIT_CHAR)) { + String[] rowKeyArray = rowKeys.split(ROW_LOCK_KEY_SPLIT_CHAR); + StringJoiner lockKeysString = new StringJoiner(ROW_LOCK_KEY_SPLIT_CHAR); + for (String rk : rowKeyArray) { + if (rk.equalsIgnoreCase(rowKey)) { + continue; + } + lockKeysString.add(rk); + } + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("update global lock key from {} to :{} ",rowKeys, lockKeysString); + } + // update the new lock key + jedis.hset(xidLockKey, param.getBranchId(), lockKeysString.toString()); + } else { + // no other branch session + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("start to delete global lock key:{} ", xidLockKey); + } + jedis.del(xidLockKey); + } + } + } + return SingleResult.success(); + } + private List queryGlobalLockByRowKey(String buildRowKey) { return readGlobalLockByRowKey(buildRowKey); } @@ -115,4 +164,7 @@ private List readGlobalLockByRowKey(String key) { return vos; } + private String buildXidLockKey(String xid) { + return DEFAULT_REDIS_SEATA_GLOBAL_LOCK_PREFIX + xid; + } } diff --git a/server/src/main/java/org/apache/seata/server/console/impl/redis/GlobalSessionRedisServiceImpl.java b/server/src/main/java/org/apache/seata/server/console/impl/redis/GlobalSessionRedisServiceImpl.java index 0d6ae6321bb..81563f28971 100644 --- a/server/src/main/java/org/apache/seata/server/console/impl/redis/GlobalSessionRedisServiceImpl.java +++ b/server/src/main/java/org/apache/seata/server/console/impl/redis/GlobalSessionRedisServiceImpl.java @@ -21,6 +21,7 @@ import java.util.stream.Collectors; import org.apache.seata.common.util.CollectionUtils; import org.apache.seata.common.result.PageResult; +import org.apache.seata.server.console.impl.AbstractGlobalService; import org.apache.seata.server.console.param.GlobalSessionParam; import org.apache.seata.server.console.vo.GlobalSessionVO; import org.apache.seata.core.model.GlobalStatus; @@ -45,7 +46,7 @@ @Component @org.springframework.context.annotation.Configuration @ConditionalOnExpression("#{'redis'.equals('${sessionMode}')}") -public class GlobalSessionRedisServiceImpl implements GlobalSessionService { +public class GlobalSessionRedisServiceImpl extends AbstractGlobalService implements GlobalSessionService { private static final Logger LOGGER = LoggerFactory.getLogger(GlobalSessionRedisServiceImpl.class); diff --git a/server/src/main/java/org/apache/seata/server/console/service/BranchSessionService.java b/server/src/main/java/org/apache/seata/server/console/service/BranchSessionService.java index 7a12c2f3e27..19dc98e4818 100644 --- a/server/src/main/java/org/apache/seata/server/console/service/BranchSessionService.java +++ b/server/src/main/java/org/apache/seata/server/console/service/BranchSessionService.java @@ -16,6 +16,7 @@ */ package org.apache.seata.server.console.service; +import org.apache.seata.common.result.SingleResult; import org.apache.seata.server.console.vo.BranchSessionVO; import org.apache.seata.common.result.PageResult; @@ -31,4 +32,30 @@ public interface BranchSessionService { */ PageResult queryByXid(String xid); + /** + * Stop branch transaction retry + * + * @param xid the global transaction + * @param branchId the branch transaction + * @return SingleResult + */ + SingleResult deleteBranchSession(String xid, String branchId); + + /** + * Start branch transaction retry + * + * @param xid the global transaction + * @param branchId the branch transaction + * @return SingleResult + */ + SingleResult stopBranchRetry(String xid, String branchId); + + /** + * Delete branch transaction + * + * @param xid the global transaction + * @param branchId the branch transaction + * @return SingleResult + */ + SingleResult startBranchRetry(String xid, String branchId); } diff --git a/server/src/main/java/org/apache/seata/server/console/service/GlobalLockService.java b/server/src/main/java/org/apache/seata/server/console/service/GlobalLockService.java index 90f0e08a6be..328f41006ad 100644 --- a/server/src/main/java/org/apache/seata/server/console/service/GlobalLockService.java +++ b/server/src/main/java/org/apache/seata/server/console/service/GlobalLockService.java @@ -17,6 +17,7 @@ package org.apache.seata.server.console.service; import org.apache.seata.common.result.PageResult; +import org.apache.seata.common.result.SingleResult; import org.apache.seata.server.console.param.GlobalLockParam; import org.apache.seata.server.console.vo.GlobalLockVO; @@ -33,5 +34,20 @@ public interface GlobalLockService { */ PageResult query(GlobalLockParam param); + /** + * Delete lock by xid and branchId + * + * @param param param + * @return SingleResult + */ + SingleResult deleteLock(GlobalLockParam param); + /** + * Check if the lock exist the branch session + * + * @param xid xid + * @param branchId branchId + * @return True-exist False-not exist the branch session + */ + SingleResult check(String xid, String branchId); } diff --git a/server/src/main/java/org/apache/seata/server/console/service/GlobalSessionService.java b/server/src/main/java/org/apache/seata/server/console/service/GlobalSessionService.java index 84eb62c9068..2ef56c197ae 100644 --- a/server/src/main/java/org/apache/seata/server/console/service/GlobalSessionService.java +++ b/server/src/main/java/org/apache/seata/server/console/service/GlobalSessionService.java @@ -16,6 +16,7 @@ */ package org.apache.seata.server.console.service; +import org.apache.seata.common.result.SingleResult; import org.apache.seata.server.console.param.GlobalSessionParam; import org.apache.seata.server.console.vo.GlobalSessionVO; import org.apache.seata.common.result.PageResult; @@ -32,4 +33,43 @@ public interface GlobalSessionService { */ PageResult query(GlobalSessionParam param); + /** + * Delete the global session + * + * @param xid The xid + * @return SingleResult + */ + SingleResult deleteGlobalSession(String xid); + + /** + * Stop the global session retry + * + * @param xid The xid + * @return SingleResult + */ + SingleResult stopGlobalRetry(String xid); + + /** + * Start the global session retry + * + * @param xid The xid + * @return SingleResult + */ + SingleResult startGlobalRetry(String xid); + + /** + * Send global session to commit or rollback to rm + * + * @param xid The xid + * @return SingleResult + */ + SingleResult sendCommitOrRollback(String xid); + + /** + * Change the global session status + * + * @param xid The xid + * @return SingleResult + */ + SingleResult changeGlobalStatus(String xid); } diff --git a/server/src/main/java/org/apache/seata/server/coordinator/AbstractCore.java b/server/src/main/java/org/apache/seata/server/coordinator/AbstractCore.java index e7607f427b1..6330da74344 100644 --- a/server/src/main/java/org/apache/seata/server/coordinator/AbstractCore.java +++ b/server/src/main/java/org/apache/seata/server/coordinator/AbstractCore.java @@ -34,6 +34,8 @@ import org.apache.seata.core.model.GlobalStatus; import org.apache.seata.core.protocol.transaction.BranchCommitRequest; import org.apache.seata.core.protocol.transaction.BranchCommitResponse; +import org.apache.seata.core.protocol.transaction.BranchDeleteRequest; +import org.apache.seata.core.protocol.transaction.BranchDeleteResponse; import org.apache.seata.core.protocol.transaction.BranchRollbackRequest; import org.apache.seata.core.protocol.transaction.BranchRollbackResponse; import org.apache.seata.core.rpc.RemotingServer; @@ -49,6 +51,7 @@ import static org.apache.seata.core.exception.TransactionExceptionCode.BranchTransactionNotExist; import static org.apache.seata.core.exception.TransactionExceptionCode.FailedToAddBranch; +import static org.apache.seata.core.exception.TransactionExceptionCode.FailedToSendBranchDeleteRequest; import static org.apache.seata.core.exception.TransactionExceptionCode.GlobalTransactionNotActive; import static org.apache.seata.core.exception.TransactionExceptionCode.GlobalTransactionStatusInvalid; import static org.apache.seata.core.exception.TransactionExceptionCode.FailedToSendBranchCommitRequest; @@ -261,4 +264,25 @@ public GlobalStatus getStatus(String xid) throws TransactionException { public void doGlobalReport(GlobalSession globalSession, String xid, GlobalStatus globalStatus) throws TransactionException { } + + @Override + public Boolean doBranchDelete(GlobalSession globalSession, BranchSession branchSession) throws TransactionException { + return null; + } + + @Override + public BranchDeleteResponse branchDelete(GlobalSession globalSession, BranchSession branchSession) throws TransactionException { + BranchDeleteRequest request = new BranchDeleteRequest(); + try { + request.setBranchType(branchSession.getBranchType()); + request.setResourceId(branchSession.getResourceId()); + request.setXid(globalSession.getXid()); + request.setBranchId(branchSession.getBranchId()); + return (BranchDeleteResponse) remotingServer.sendSyncRequest( + branchSession.getResourceId(), branchSession.getClientId(), request, branchSession.isAT()); + } catch (TimeoutException e) { + throw new BranchTransactionException(FailedToSendBranchDeleteRequest, String.format("Send branch delete failed," + + " xid = %s branchId = %s", branchSession.getXid(), branchSession.getBranchId()), e); + } + } } diff --git a/server/src/main/java/org/apache/seata/server/coordinator/Core.java b/server/src/main/java/org/apache/seata/server/coordinator/Core.java index 04b335b3a04..c7b59a20c0a 100644 --- a/server/src/main/java/org/apache/seata/server/coordinator/Core.java +++ b/server/src/main/java/org/apache/seata/server/coordinator/Core.java @@ -18,6 +18,7 @@ import org.apache.seata.core.exception.TransactionException; import org.apache.seata.core.model.GlobalStatus; +import org.apache.seata.server.session.BranchSession; import org.apache.seata.server.session.GlobalSession; /** @@ -56,4 +57,12 @@ public interface Core extends TransactionCoordinatorInbound, TransactionCoordina */ void doGlobalReport(GlobalSession globalSession, String xid, GlobalStatus param) throws TransactionException; + /** + * Do branch delete. + * + * @param globalSession the global session + * @param branchSession the branch session + * @throws TransactionException the transaction exception + */ + Boolean doBranchDelete(GlobalSession globalSession, BranchSession branchSession) throws TransactionException; } diff --git a/server/src/main/java/org/apache/seata/server/coordinator/DefaultCoordinator.java b/server/src/main/java/org/apache/seata/server/coordinator/DefaultCoordinator.java index 9003fe268aa..237b1fea0d9 100644 --- a/server/src/main/java/org/apache/seata/server/coordinator/DefaultCoordinator.java +++ b/server/src/main/java/org/apache/seata/server/coordinator/DefaultCoordinator.java @@ -35,6 +35,7 @@ import org.apache.seata.core.constants.ConfigurationKeys; import org.apache.seata.core.context.RootContext; import org.apache.seata.core.exception.TransactionException; +import org.apache.seata.core.model.BranchStatus; import org.apache.seata.core.model.GlobalStatus; import org.apache.seata.core.protocol.AbstractMessage; import org.apache.seata.core.protocol.AbstractResultMessage; @@ -77,6 +78,7 @@ import org.slf4j.MDC; import static org.apache.seata.common.Constants.ASYNC_COMMITTING; +import static org.apache.seata.common.Constants.AUTO_RESTART_SESSION; import static org.apache.seata.common.Constants.COMMITTING; import static org.apache.seata.common.Constants.RETRY_COMMITTING; import static org.apache.seata.common.Constants.RETRY_ROLLBACKING; @@ -135,6 +137,17 @@ public class DefaultCoordinator extends AbstractTCInboundHandler implements Tran protected static final long UNDO_LOG_DELETE_PERIOD = CONFIG.getLong( ConfigurationKeys.TRANSACTION_UNDO_LOG_DELETE_PERIOD, DEFAULT_UNDO_LOG_DELETE_PERIOD); + /** + * The constant AUTO_RESTART_TIME + */ + protected static final long AUTO_RETRY_TIME = CONFIG.getLong(ConfigurationKeys.AUTO_RESTART_TIME, + DefaultValues.DEFAULT_AUTO_RESTART_TIME); + + /** + * The constant AUTO_RESTART_PERIOD + */ + protected static final long AUTO_RETRY_PERIOD = CONFIG.getLong(ConfigurationKeys.AUTO_RESTART_PERIOD, + DefaultValues.DEFAULT_AUTO_RESTART_PERIOD); /** * The Transaction undo log delay delete period */ @@ -182,6 +195,9 @@ public class DefaultCoordinator extends AbstractTCInboundHandler implements Tran private final ScheduledThreadPoolExecutor syncProcessing = new ScheduledThreadPoolExecutor(1, new NamedThreadFactory(SYNC_PROCESSING, 1)); + private final ScheduledThreadPoolExecutor autoRestartSession = + new ScheduledThreadPoolExecutor(1, new NamedThreadFactory(AUTO_RESTART_SESSION, 1)); + private final GlobalStatus[] retryRollbackingStatuses = new GlobalStatus[] { GlobalStatus.TimeoutRollbacking, GlobalStatus.TimeoutRollbackRetrying, GlobalStatus.RollbackRetrying}; @@ -462,6 +478,46 @@ protected void handleAsyncCommitting() { }); } + protected void handleAutoRestart() { + Collection allSessions = SessionHolder.getRootSessionManager().allSessions(); + if (CollectionUtils.isEmpty(allSessions)) { + return; + } + SessionHelper.forEach(allSessions, globalSession -> { + String xid = globalSession.getXid(); + List branchSessions = globalSession.getBranchSessions(); + branchSessions.forEach(branchSession -> { + if (BranchStatus.STOP_RETRY.equals(branchSession.getStatus()) + && System.currentTimeMillis() - branchSession.getGmtModified() >= AUTO_RETRY_TIME) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Auto restart the branch session to retry,xid: {}, branchId: {}", xid, branchSession.getBranchId()); + } + BranchStatus newStatus = BranchStatus.Registered; + branchSession.setStatus(newStatus); + try { + globalSession.changeBranchStatus(branchSession, newStatus); + } catch (Exception e) { + LOGGER.error("Change branch session status fail, xid: {}, branchId:{}", + globalSession.getXid(), branchSession.getBranchId(), e); + } + } + }); + GlobalStatus globalStatus = globalSession.getStatus(); + GlobalStatus newStatus = GlobalStatus.StopCommitRetry.equals(globalStatus) ? GlobalStatus.CommitRetrying : + GlobalStatus.StopRollbackRetry.equals(globalStatus) ? GlobalStatus.RollbackRetrying : null; + try { + if (Objects.nonNull(newStatus) && System.currentTimeMillis() - globalSession.getGmtModified() >= AUTO_RETRY_TIME) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Auto restart the global session to retry,xid: {}", globalSession.getXid()); + } + globalSession.changeGlobalStatus(newStatus); + } + } catch (Exception e) { + LOGGER.error("Auto restart global session retry fail, xid:{}", globalSession.getXid(), e); + } + }); + } + /** * Undo log delete. */ @@ -626,6 +682,8 @@ public void init() { () -> SessionHolder.distributedLockAndExecute(UNDOLOG_DELETE, this::undoLogDelete), UNDO_LOG_DELAY_DELETE_PERIOD, UNDO_LOG_DELETE_PERIOD, TimeUnit.MILLISECONDS); + autoRestartSession.scheduleAtFixedRate(this::handleAutoRestart, 0, AUTO_RETRY_PERIOD, TimeUnit.MILLISECONDS); + rollbackingSchedule(0); committingSchedule(0); @@ -658,6 +716,7 @@ public void destroy() { asyncCommitting.shutdown(); timeoutCheck.shutdown(); undoLogDelete.shutdown(); + autoRestartSession.shutdown(); if (branchRemoveExecutor != null) { branchRemoveExecutor.shutdown(); } @@ -667,6 +726,7 @@ public void destroy() { asyncCommitting.awaitTermination(TIMED_TASK_SHUTDOWN_MAX_WAIT_MILLS, TimeUnit.MILLISECONDS); timeoutCheck.awaitTermination(TIMED_TASK_SHUTDOWN_MAX_WAIT_MILLS, TimeUnit.MILLISECONDS); undoLogDelete.awaitTermination(TIMED_TASK_SHUTDOWN_MAX_WAIT_MILLS, TimeUnit.MILLISECONDS); + autoRestartSession.awaitTermination(TIMED_TASK_SHUTDOWN_MAX_WAIT_MILLS, TimeUnit.MILLISECONDS); if (branchRemoveExecutor != null) { branchRemoveExecutor.awaitTermination(TIMED_TASK_SHUTDOWN_MAX_WAIT_MILLS, TimeUnit.MILLISECONDS); } @@ -690,6 +750,14 @@ public void setRemotingServer(RemotingServer remotingServer) { this.remotingServer = remotingServer; } + /** + * get default core + * @return + */ + public DefaultCore getCore() { + return core; + } + /** * the task to remove branchSession */ diff --git a/server/src/main/java/org/apache/seata/server/coordinator/DefaultCore.java b/server/src/main/java/org/apache/seata/server/coordinator/DefaultCore.java index d6c93532fa7..e091284b45a 100644 --- a/server/src/main/java/org/apache/seata/server/coordinator/DefaultCore.java +++ b/server/src/main/java/org/apache/seata/server/coordinator/DefaultCore.java @@ -31,6 +31,8 @@ import org.apache.seata.core.model.BranchStatus; import org.apache.seata.core.model.BranchType; import org.apache.seata.core.model.GlobalStatus; +import org.apache.seata.core.protocol.ResultCode; +import org.apache.seata.core.protocol.transaction.BranchDeleteResponse; import org.apache.seata.core.rpc.RemotingServer; import org.apache.seata.server.metrics.MetricsPublisher; import org.apache.seata.server.session.BranchSession; @@ -130,6 +132,29 @@ public BranchStatus branchRollback(GlobalSession globalSession, BranchSession br return getCore(branchSession.getBranchType()).branchRollback(globalSession, branchSession); } + @Override + public BranchDeleteResponse branchDelete(GlobalSession globalSession, BranchSession branchSession) throws TransactionException { + return getCore(branchSession.getBranchType()).branchDelete(globalSession, branchSession); + } + + @Override + public Boolean doBranchDelete(GlobalSession globalSession, BranchSession branchSession) throws TransactionException { + if (globalSession.isSaga()) { + return true; + } + BranchDeleteResponse response = getCore(branchSession.getBranchType()).branchDelete(globalSession, branchSession); + if (isXaerNotaTimeout(globalSession, response.getBranchStatus())) { + LOGGER.info("Delete branch XAER_NOTA retry timeout, xid = {} branchId = {}", globalSession.getXid(), branchSession.getBranchId()); + return true; + } + if (response.getBranchStatus() == BranchStatus.PhaseTwo_RollbackFailed_Unretryable) { + LOGGER.error("Delete branch transaction fail and stop retry, xid = {} branchId = {}", globalSession.getXid(), branchSession.getBranchId()); + return true; + } + return ResultCode.Success.equals(response.getResultCode()); + } + + @Override public String begin(String applicationId, String transactionServiceGroup, String name, int timeout) throws TransactionException { @@ -220,6 +245,10 @@ public boolean doGlobalCommit(GlobalSession globalSession, boolean retrying) thr SessionHelper.removeBranch(globalSession, branchSession, !retrying); return CONTINUE; } + // skip the branch session if not retry + if (retrying && BranchStatus.STOP_RETRY.equals(currentStatus)) { + return CONTINUE; + } try { BranchStatus branchStatus = getCore(branchSession.getBranchType()).branchCommit(globalSession, branchSession); if (isXaerNotaTimeout(globalSession,branchStatus)) { @@ -322,6 +351,10 @@ public boolean doGlobalRollback(GlobalSession globalSession, boolean retrying) t SessionHelper.removeBranch(globalSession, branchSession, !retrying); return CONTINUE; } + // skip the branch session if not retry + if (retrying && BranchStatus.STOP_RETRY.equals(currentBranchStatus)) { + return CONTINUE; + } try { BranchStatus branchStatus = branchRollback(globalSession, branchSession); if (isXaerNotaTimeout(globalSession, branchStatus)) { @@ -362,7 +395,7 @@ public boolean doGlobalRollback(GlobalSession globalSession, boolean retrying) t // In db mode, lock and branch data residual problems may occur. // Therefore, execution needs to be delayed here and cannot be executed synchronously. - if (success) { + if (success && globalSession.getBranchSessions().isEmpty()) { SessionHelper.endRollbacked(globalSession, retrying); LOGGER.info("Rollback global transaction successfully, xid = {}.", globalSession.getXid()); } diff --git a/server/src/main/java/org/apache/seata/server/coordinator/TransactionCoordinatorOutbound.java b/server/src/main/java/org/apache/seata/server/coordinator/TransactionCoordinatorOutbound.java index 04ef312d9a9..b973522a02d 100644 --- a/server/src/main/java/org/apache/seata/server/coordinator/TransactionCoordinatorOutbound.java +++ b/server/src/main/java/org/apache/seata/server/coordinator/TransactionCoordinatorOutbound.java @@ -18,6 +18,7 @@ import org.apache.seata.core.exception.TransactionException; import org.apache.seata.core.model.BranchStatus; +import org.apache.seata.core.protocol.transaction.BranchDeleteResponse; import org.apache.seata.server.session.BranchSession; import org.apache.seata.server.session.GlobalSession; @@ -50,5 +51,14 @@ public interface TransactionCoordinatorOutbound { */ BranchStatus branchRollback(GlobalSession globalSession, BranchSession branchSession) throws TransactionException; - + /** + * Delete a branch transaction. + * + * @param globalSession the global session + * @param branchSession the branch session + * @return delete success or failed + * @throws TransactionException Any exception that fails this will be wrapped with TransactionException and thrown + * out. + */ + BranchDeleteResponse branchDelete(GlobalSession globalSession, BranchSession branchSession) throws TransactionException; } diff --git a/server/src/main/java/org/apache/seata/server/session/BranchSession.java b/server/src/main/java/org/apache/seata/server/session/BranchSession.java index 8688d65339a..acb5ade6ee3 100644 --- a/server/src/main/java/org/apache/seata/server/session/BranchSession.java +++ b/server/src/main/java/org/apache/seata/server/session/BranchSession.java @@ -74,6 +74,8 @@ public class BranchSession implements Lockable, Comparable, Sessi private LockStatus lockStatus = Locked; + private Long gmtModified; + private final Map> lockHolder; private final LockManager lockManager = LockerManagerFactory.getLockManager(); @@ -87,6 +89,14 @@ public BranchSession(BranchType branchType) { this.lockHolder = branchType == BranchType.AT ? new ConcurrentHashMap<>(8) : Collections.emptyMap(); } + public Long getGmtModified() { + return gmtModified; + } + + public void setGmtModified(Long gmtModified) { + this.gmtModified = gmtModified; + } + /** * Gets application data. * @@ -407,6 +417,8 @@ public byte[] encode() { byteBuffer.put((byte)status.getCode()); byteBuffer.put((byte)lockStatus.getCode()); + gmtModified = System.currentTimeMillis(); + byteBuffer.putLong(gmtModified); BufferUtils.flip(byteBuffer); byte[] result = new byte[byteBuffer.limit()]; byteBuffer.get(result); @@ -423,6 +435,7 @@ private int calBranchSessionSize(byte[] resourceIdBytes, byte[] lockKeyBytes, by + 4 // applicationDataBytes.length + 4 // xidBytes.size + 1 // statusCode + + 8 // gmtModified + (resourceIdBytes == null ? 0 : resourceIdBytes.length) + (lockKeyBytes == null ? 0 : lockKeyBytes.length) + (clientIdBytes == null ? 0 : clientIdBytes.length) @@ -481,6 +494,7 @@ public void decode(byte[] a) { this.branchType = BranchType.values()[branchTypeId]; } this.status = BranchStatus.get(byteBuffer.get()); + this.gmtModified = byteBuffer.getLong(); } } diff --git a/server/src/main/java/org/apache/seata/server/session/GlobalSession.java b/server/src/main/java/org/apache/seata/server/session/GlobalSession.java index 8cfc0ecbc67..cf3f892c8e8 100644 --- a/server/src/main/java/org/apache/seata/server/session/GlobalSession.java +++ b/server/src/main/java/org/apache/seata/server/session/GlobalSession.java @@ -97,6 +97,8 @@ public class GlobalSession implements SessionLifecycle, SessionStorable { private String applicationData; + private Long gmtModified; + private final boolean lazyLoadBranch; private volatile boolean active = true; @@ -203,7 +205,7 @@ public boolean isTimeout() { * @return if true retry commit or roll back */ public boolean isDeadSession() { - return (System.currentTimeMillis() - beginTime) > RETRY_DEAD_THRESHOLD; + return (System.currentTimeMillis() - gmtModified) > RETRY_DEAD_THRESHOLD; } /** @@ -604,6 +606,14 @@ public void setActive(boolean active) { this.active = active; } + public Long getGmtModified() { + return gmtModified; + } + + public void setGmtModified(Long gmtModified) { + this.gmtModified = gmtModified; + } + @Override public byte[] encode() { byte[] byApplicationIdBytes = applicationId != null ? applicationId.getBytes() : null; @@ -661,6 +671,8 @@ public byte[] encode() { } byteBuffer.putLong(beginTime); byteBuffer.put((byte)status.getCode()); + gmtModified = System.currentTimeMillis(); + byteBuffer.putLong(gmtModified); BufferUtils.flip(byteBuffer); byte[] result = new byte[byteBuffer.limit()]; byteBuffer.get(result); @@ -678,6 +690,7 @@ private int calGlobalSessionSize(byte[] byApplicationIdBytes, byte[] byServiceGr + 4 // applicationDataBytes.length + 8 // beginTime + 1 // statusCode + + 8 // gmtModified + (byApplicationIdBytes == null ? 0 : byApplicationIdBytes.length) + (byServiceGroupBytes == null ? 0 : byServiceGroupBytes.length) + (byTxNameBytes == null ? 0 : byTxNameBytes.length) @@ -724,6 +737,7 @@ public void decode(byte[] a) { this.beginTime = byteBuffer.getLong(); this.status = GlobalStatus.get(byteBuffer.get()); + this.gmtModified = byteBuffer.getLong(); } /** @@ -787,11 +801,17 @@ public void asyncCommit() throws TransactionException { } public void queueToRetryCommit() throws TransactionException { + if (this.status == GlobalStatus.StopCommitRetry || this.status == GlobalStatus.StopRollbackRetry) { + return; + } changeGlobalStatus(GlobalStatus.CommitRetrying); } public void queueToRetryRollback() throws TransactionException { GlobalStatus currentStatus = this.getStatus(); + if (currentStatus == GlobalStatus.StopCommitRetry || currentStatus == GlobalStatus.StopRollbackRetry) { + return; + } GlobalStatus newStatus; if (SessionStatusValidator.isTimeoutGlobalStatus(currentStatus)) { newStatus = GlobalStatus.TimeoutRollbackRetrying; diff --git a/server/src/main/java/org/apache/seata/server/session/SessionHolder.java b/server/src/main/java/org/apache/seata/server/session/SessionHolder.java index aa32ea001e6..aa078d41769 100644 --- a/server/src/main/java/org/apache/seata/server/session/SessionHolder.java +++ b/server/src/main/java/org/apache/seata/server/session/SessionHolder.java @@ -209,6 +209,9 @@ public static void reload(Collection allSessions, SessionMode sto throw new RuntimeException(e); } } + case StopCommitRetry: + case StopRollbackRetry: + case Deleting: break; default: { if (acquireLock) { diff --git a/server/src/main/java/org/apache/seata/server/storage/SessionConverter.java b/server/src/main/java/org/apache/seata/server/storage/SessionConverter.java index 4b90905a851..d4898408843 100644 --- a/server/src/main/java/org/apache/seata/server/storage/SessionConverter.java +++ b/server/src/main/java/org/apache/seata/server/storage/SessionConverter.java @@ -55,6 +55,7 @@ public static GlobalSession convertGlobalSession(GlobalTransactionDO globalTrans session.setStatus(GlobalStatus.get(globalTransactionDO.getStatus())); session.setApplicationData(globalTransactionDO.getApplicationData()); session.setBeginTime(globalTransactionDO.getBeginTime()); + session.setGmtModified(globalTransactionDO.getGmtModified().getTime()); return session; } @@ -76,6 +77,7 @@ public static BranchSession convertBranchSession(BranchTransactionDO branchTrans branchSession.setClientId(branchTransactionDO.getClientId()); branchSession.setResourceGroupId(branchTransactionDO.getResourceGroupId()); branchSession.setStatus(BranchStatus.get(branchTransactionDO.getStatus())); + branchSession.setGmtModified(branchTransactionDO.getGmtModified().getTime()); if (branchTransactionDO instanceof BranchTransactionDTO) { branchSession.setLockKey(((BranchTransactionDTO)branchTransactionDO).getLockKey()); } diff --git a/server/src/main/java/org/apache/seata/server/storage/db/session/DataBaseSessionManager.java b/server/src/main/java/org/apache/seata/server/storage/db/session/DataBaseSessionManager.java index 49b4f49be1f..e8a015d2482 100644 --- a/server/src/main/java/org/apache/seata/server/storage/db/session/DataBaseSessionManager.java +++ b/server/src/main/java/org/apache/seata/server/storage/db/session/DataBaseSessionManager.java @@ -138,7 +138,8 @@ public Collection allSessions() { return findGlobalSessions( new SessionCondition(GlobalStatus.UnKnown, GlobalStatus.Begin, GlobalStatus.Committing, GlobalStatus.CommitRetrying, GlobalStatus.Rollbacking, GlobalStatus.RollbackRetrying, - GlobalStatus.TimeoutRollbacking, GlobalStatus.TimeoutRollbackRetrying, GlobalStatus.AsyncCommitting)); + GlobalStatus.TimeoutRollbacking, GlobalStatus.TimeoutRollbackRetrying, GlobalStatus.AsyncCommitting, + GlobalStatus.StopRollbackRetry, GlobalStatus.StopCommitRetry, GlobalStatus.Deleting)); } @Override diff --git a/server/src/main/java/org/apache/seata/server/storage/file/lock/FileLocker.java b/server/src/main/java/org/apache/seata/server/storage/file/lock/FileLocker.java index c9630e0aa10..1447aff6bd6 100644 --- a/server/src/main/java/org/apache/seata/server/storage/file/lock/FileLocker.java +++ b/server/src/main/java/org/apache/seata/server/storage/file/lock/FileLocker.java @@ -186,6 +186,14 @@ public void cleanAllLocks() { LOCK_MAP.clear(); } + public static ConcurrentMap>> getLockMap() { + return LOCK_MAP; + } + + public static int getBucketPerTable() { + return BUCKET_PER_TABLE; + } + /** * Because bucket lock map will be key of HashMap(lockHolder), however {@link ConcurrentHashMap} overwrites * {@link Object#hashCode()} and {@link Object#equals(Object)}, that leads to hash key conflict in lockHolder. @@ -196,7 +204,7 @@ public static class BucketLockMap { private final ConcurrentHashMap bucketLockMap = new ConcurrentHashMap<>(); - ConcurrentHashMap get() { + public ConcurrentHashMap get() { return bucketLockMap; } diff --git a/server/src/main/java/org/apache/seata/server/storage/file/session/FileSessionManager.java b/server/src/main/java/org/apache/seata/server/storage/file/session/FileSessionManager.java index d7a097fed29..4a777fbd479 100644 --- a/server/src/main/java/org/apache/seata/server/storage/file/session/FileSessionManager.java +++ b/server/src/main/java/org/apache/seata/server/storage/file/session/FileSessionManager.java @@ -285,6 +285,7 @@ private void restore(List stores, Set removedGlob } else { if (this.checkSessionStatus(globalSession)) { foundGlobalSession.setStatus(globalSession.getStatus()); + foundGlobalSession.setGmtModified(globalSession.getGmtModified()); } else { sessionMap.remove(globalSession.getXid()); removedGlobalBuffer.add(globalSession.getXid()); diff --git a/server/src/main/java/org/apache/seata/server/storage/redis/session/RedisSessionManager.java b/server/src/main/java/org/apache/seata/server/storage/redis/session/RedisSessionManager.java index 4941d95824a..977a4d72569 100644 --- a/server/src/main/java/org/apache/seata/server/storage/redis/session/RedisSessionManager.java +++ b/server/src/main/java/org/apache/seata/server/storage/redis/session/RedisSessionManager.java @@ -131,7 +131,8 @@ public Collection allSessions() { return findGlobalSessions( new SessionCondition(GlobalStatus.UnKnown, GlobalStatus.Begin, GlobalStatus.Committing, GlobalStatus.CommitRetrying, GlobalStatus.Rollbacking, GlobalStatus.RollbackRetrying, - GlobalStatus.TimeoutRollbacking, GlobalStatus.TimeoutRollbackRetrying, GlobalStatus.AsyncCommitting)); + GlobalStatus.TimeoutRollbacking, GlobalStatus.TimeoutRollbackRetrying, GlobalStatus.AsyncCommitting, + GlobalStatus.StopRollbackRetry, GlobalStatus.StopCommitRetry, GlobalStatus.Deleting)); } @Override diff --git a/server/src/test/java/org/apache/seata/server/coordinator/DefaultCoordinatorTest.java b/server/src/test/java/org/apache/seata/server/coordinator/DefaultCoordinatorTest.java index 1de1f04c0ca..869d8a49d36 100644 --- a/server/src/test/java/org/apache/seata/server/coordinator/DefaultCoordinatorTest.java +++ b/server/src/test/java/org/apache/seata/server/coordinator/DefaultCoordinatorTest.java @@ -35,9 +35,13 @@ import org.apache.seata.core.exception.TransactionException; import org.apache.seata.core.model.BranchStatus; import org.apache.seata.core.model.BranchType; +import org.apache.seata.core.model.GlobalStatus; +import org.apache.seata.core.protocol.ResultCode; import org.apache.seata.core.protocol.RpcMessage; import org.apache.seata.core.protocol.transaction.BranchCommitRequest; import org.apache.seata.core.protocol.transaction.BranchCommitResponse; +import org.apache.seata.core.protocol.transaction.BranchDeleteRequest; +import org.apache.seata.core.protocol.transaction.BranchDeleteResponse; import org.apache.seata.core.protocol.transaction.BranchRollbackRequest; import org.apache.seata.core.protocol.transaction.BranchRollbackResponse; import org.apache.seata.core.rpc.RemotingServer; @@ -122,6 +126,7 @@ public void branchCommit() throws TransactionException { } Assertions.assertEquals(result, BranchStatus.PhaseTwo_Committed); globalSession = SessionHolder.findGlobalSession(xid); + globalSession.setStatus(GlobalStatus.Committed); Assertions.assertNotNull(globalSession); globalSession.end(); } @@ -140,6 +145,22 @@ public void branchRollback(String xid, Long branchId) { Assertions.assertEquals(result, BranchStatus.PhaseTwo_Rollbacked); } + @Test + public void branchDelete() { + try { + String xid = core.begin(applicationId, txServiceGroup, txName, timeout); + Long branchIdAt = core.branchRegister(BranchType.AT, resourceId, clientId, xid, applicationData, lockKeys_1); + Long branchIdXA = core.branchRegister(BranchType.XA, resourceId, clientId, xid, applicationData, null); + GlobalSession globalSession = SessionHolder.findGlobalSession(xid); + Assertions.assertTrue(core.doBranchDelete(globalSession, globalSession.getBranch(branchIdAt))); + Assertions.assertTrue(core.doBranchDelete(globalSession, globalSession.getBranch(branchIdXA))); + globalSession.setStatus(GlobalStatus.Deleting); + globalSession.end(); + } catch (Exception e) { + e.printStackTrace(); + Assertions.fail(e.getMessage()); + } + } @Test public void test_handleRetryRollbacking() throws TransactionException, InterruptedException { @@ -148,12 +169,13 @@ public void test_handleRetryRollbacking() throws TransactionException, Interrupt Long branchId = core.branchRegister(BranchType.AT, "abcd", clientId, xid, applicationData, lockKeys_2); Assertions.assertNotNull(branchId); - + GlobalSession globalSession = SessionHolder.findGlobalSession(xid); Thread.sleep(100); defaultCoordinator.timeoutCheck(); + TimeUnit.MILLISECONDS.sleep(globalSession.getGmtModified() - globalSession.getBeginTime()); defaultCoordinator.handleRetryRollbacking(); - GlobalSession globalSession = SessionHolder.findGlobalSession(xid); + globalSession = SessionHolder.findGlobalSession(xid); Assertions.assertNull(globalSession); } @@ -172,6 +194,7 @@ public void test_handleRetryRollbackingTimeOut() throws TransactionException, In ReflectionUtil.modifyStaticFinalField(defaultCoordinator.getClass(), "MAX_ROLLBACK_RETRY_TIMEOUT", 10L); ReflectionUtil.modifyStaticFinalField(defaultCoordinator.getClass(), "ROLLBACK_RETRY_TIMEOUT_UNLOCK_ENABLE", false); TimeUnit.MILLISECONDS.sleep(100); + TimeUnit.MILLISECONDS.sleep(globalSession.getGmtModified() - globalSession.getBeginTime()); globalSession.queueToRetryRollback(); defaultCoordinator.handleRetryRollbacking(); int lockSize = globalSession.getBranchSessions().get(0).getLockHolder().size(); @@ -201,6 +224,7 @@ public void test_handleRetryRollbackingTimeOut_unlock() throws TransactionExcept TimeUnit.MILLISECONDS.sleep(100); globalSession.queueToRetryRollback(); + TimeUnit.MILLISECONDS.sleep(globalSession.getGmtModified() - globalSession.getBeginTime()); defaultCoordinator.handleRetryRollbacking(); int lockSize = globalSession.getBranchSessions().get(0).getLockHolder().size(); @@ -213,6 +237,21 @@ public void test_handleRetryRollbackingTimeOut_unlock() throws TransactionExcept } } + @Test + public void test_handleRetryRollbackingButSkip() throws TransactionException, InterruptedException, NoSuchFieldException, IllegalAccessException { + String xid = core.begin(applicationId, txServiceGroup, txName, 10); + Long branchId = core.branchRegister(BranchType.TCC, "abcd", clientId, xid, applicationData, ""); + + Assertions.assertNotNull(branchId); + GlobalSession globalSession = SessionHolder.findGlobalSession(xid); + globalSession.getBranch(branchId).setStatus(BranchStatus.STOP_RETRY); + defaultCoordinator.handleRetryRollbacking(); + + globalSession = SessionHolder.findGlobalSession(xid); + Assertions.assertNotNull(globalSession); + globalSession.end(); + } + @AfterAll public static void afterClass() throws Exception { @@ -259,6 +298,11 @@ public Object sendSyncRequest(String resourceId, String clientId, Object message final BranchRollbackResponse branchRollbackResponse = new BranchRollbackResponse(); branchRollbackResponse.setBranchStatus(BranchStatus.PhaseTwo_Rollbacked); return branchRollbackResponse; + } else if (message instanceof BranchDeleteRequest) { + final BranchDeleteResponse branchDeleteResponse = new BranchDeleteResponse(); + branchDeleteResponse.setResultCode(ResultCode.Success); + branchDeleteResponse.setBranchStatus(BranchStatus.PhaseTwo_RollbackFailed_Unretryable); + return branchDeleteResponse; } else { return null; } diff --git a/server/src/test/java/org/apache/seata/server/lock/LockManagerTest.java b/server/src/test/java/org/apache/seata/server/lock/LockManagerTest.java index 6d6889c8045..c93c992a861 100644 --- a/server/src/test/java/org/apache/seata/server/lock/LockManagerTest.java +++ b/server/src/test/java/org/apache/seata/server/lock/LockManagerTest.java @@ -435,6 +435,27 @@ static Stream globalSessionForLockTestProvider() throws ParseExceptio return Stream.of(Arguments.of(globalSession1, globalSession2)); } + static Stream globalSessionProvider() throws ParseException { + final BranchSession[] branchSessions2 = baseBranchSession("employee", "de:1,2;df:3,4;dg:5,6", "eg:7,8;ef:9,10"); + final BranchSession branchSession3 = branchSessions2[0]; + branchSession3.setTransactionId(123456L); + + final BranchSession branchSession4 = branchSessions2[1]; + branchSession4.setTransactionId(123456L); + + + final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + + GlobalSession globalSession = new GlobalSession("demo-app", DEFAULT_TX_GROUP, "test2", 6000); + globalSession.setXid("xid2:123456"); + globalSession.add(branchSession3); + globalSession.add(branchSession4); + globalSession.setBeginTime(dateFormat.parse("2022-1-1 08:00:00").getTime()); + + return Stream.of(Arguments.of(globalSession)); + } + + /** * Base branch sessions provider object [ ] [ ]. Could assign resource and lock keys. * diff --git a/server/src/test/java/org/apache/seata/server/session/FileSessionManagerTest.java b/server/src/test/java/org/apache/seata/server/session/FileSessionManagerTest.java index 56ce51d343f..cc97e731a6d 100644 --- a/server/src/test/java/org/apache/seata/server/session/FileSessionManagerTest.java +++ b/server/src/test/java/org/apache/seata/server/session/FileSessionManagerTest.java @@ -27,17 +27,22 @@ import javax.annotation.Resource; +import org.apache.seata.common.ConfigurationKeys; import org.apache.seata.common.XID; import org.apache.seata.common.loader.EnhancedServiceLoader; import org.apache.seata.common.result.PageResult; +import org.apache.seata.common.util.CollectionUtils; +import org.apache.seata.common.util.UUIDGenerator; import org.apache.seata.core.model.BranchStatus; import org.apache.seata.core.model.BranchType; import org.apache.seata.core.model.GlobalStatus; import org.apache.seata.core.model.LockStatus; import org.apache.seata.server.console.param.GlobalSessionParam; +import org.apache.seata.server.console.service.BranchSessionService; import org.apache.seata.server.console.service.GlobalSessionService; import org.apache.seata.server.console.vo.GlobalSessionVO; import org.apache.seata.server.storage.file.session.FileSessionManager; +import org.apache.seata.server.store.StoreConfig; import org.apache.seata.server.store.StoreConfig.SessionMode; import org.apache.seata.server.util.StoreUtil; import org.apache.commons.lang.time.DateUtils; @@ -50,7 +55,9 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.ApplicationContext; +import static org.apache.seata.common.DefaultValues.DEFAULT_SESSION_STORE_FILE_DIR; import static org.apache.seata.common.DefaultValues.DEFAULT_TX_GROUP; +import static org.apache.seata.server.session.SessionHolder.CONFIG; /** * The type File based session manager test. @@ -66,6 +73,12 @@ public class FileSessionManagerTest { @Resource(type = GlobalSessionService.class) private GlobalSessionService globalSessionService; + @Resource(type = BranchSessionService.class) + private BranchSessionService branchSessionService; + + private static String sessionStorePath = CONFIG.getConfig(ConfigurationKeys.STORE_FILE_DIR, + DEFAULT_SESSION_STORE_FILE_DIR); + @BeforeAll public static void setUp(ApplicationContext context) { StoreUtil.deleteDataFile(); @@ -284,6 +297,16 @@ public void findGlobalSessionsWithPageResultTest(List globalSessi SessionHolder.init(SessionMode.FILE); try { + SessionHolder.init(StoreConfig.SessionMode.FILE); + final SessionManager sessionManager = SessionHolder.getRootSessionManager(); + // make sure sessionMaanager is empty + Collection sessions = sessionManager.allSessions(); + if (CollectionUtils.isNotEmpty(sessions)) { + // FileSessionManager use ConcurrentHashMap is thread safe + for (GlobalSession session : sessions) { + sessionManager.removeGlobalSession(session); + } + } for (GlobalSession globalSession : globalSessions) { globalSession.begin(); } @@ -407,6 +430,96 @@ public void onStatusChangeTest(GlobalSession globalSession) throws Exception { } } + @ParameterizedTest + @MethodSource("globalSessionForLockTestProvider") + public void stopGlobalSessionTest(List globalSessions) throws Exception { + try { + SessionHolder.init(StoreConfig.SessionMode.FILE); + for (GlobalSession globalSession : globalSessions) { + globalSession.begin(); + } + Assertions.assertThrows(IllegalArgumentException.class, () -> + globalSessionService.stopGlobalRetry(globalSessions.get(0).getXid())); + + GlobalSession globalSession = globalSessions.get(1); + globalSession.changeGlobalStatus(GlobalStatus.CommitRetrying); + String xid = globalSession.getXid(); + globalSessionService.stopGlobalRetry(xid); + Assertions.assertEquals(SessionHolder.findGlobalSession(xid).getStatus(), + GlobalStatus.StopCommitRetry); + + globalSession.changeGlobalStatus(GlobalStatus.RollbackRetrying); + globalSessionService.stopGlobalRetry(xid); + Assertions.assertEquals(SessionHolder.findGlobalSession(xid).getStatus(), + GlobalStatus.StopRollbackRetry); + } finally { + for (GlobalSession globalSession : globalSessions) { + globalSession.setStatus(GlobalStatus.Committed); + globalSession.end(); + } + } + } + + @ParameterizedTest + @MethodSource("globalSessionForLockTestProvider") + public void changeGlobalSessionTest(List globalSessions) throws Exception { + try { + SessionHolder.init(StoreConfig.SessionMode.FILE); + for (GlobalSession globalSession : globalSessions) { + globalSession.begin(); + } + Assertions.assertThrows(IllegalArgumentException.class, () -> + globalSessionService.changeGlobalStatus(globalSessions.get(0).getXid())); + + GlobalSession globalSession = globalSessions.get(1); + globalSession.changeGlobalStatus(GlobalStatus.CommitFailed); + String xid = globalSession.getXid(); + globalSessionService.changeGlobalStatus(xid); + Assertions.assertEquals(SessionHolder.findGlobalSession(xid).getStatus(), + GlobalStatus.CommitRetrying); + + globalSession.changeGlobalStatus(GlobalStatus.RollbackFailed); + globalSessionService.changeGlobalStatus(xid); + Assertions.assertEquals(SessionHolder.findGlobalSession(xid).getStatus(), + GlobalStatus.RollbackRetrying); + } finally { + for (GlobalSession globalSession : globalSessions) { + globalSession.setStatus(GlobalStatus.Committed); + globalSession.end(); + } + } + } + + @ParameterizedTest + @MethodSource("globalSessionForLockTestProvider") + public void startGlobalSessionTest(List globalSessions) throws Exception { + try { + SessionHolder.init(StoreConfig.SessionMode.FILE); + for (GlobalSession globalSession : globalSessions) { + globalSession.begin(); + } + Assertions.assertThrows(IllegalArgumentException.class, () -> + globalSessionService.startGlobalRetry(globalSessions.get(0).getXid())); + + GlobalSession globalSession = globalSessions.get(1); + globalSession.setStatus(GlobalStatus.StopCommitRetry); + String xid = globalSession.getXid(); + globalSessionService.startGlobalRetry(xid); + Assertions.assertEquals(SessionHolder.findGlobalSession(xid).getStatus(), + GlobalStatus.CommitRetrying); + + globalSession.setStatus(GlobalStatus.StopRollbackRetry); + globalSessionService.startGlobalRetry(xid); + Assertions.assertEquals(SessionHolder.findGlobalSession(xid).getStatus(), + GlobalStatus.RollbackRetrying); + } finally { + for (GlobalSession globalSession : globalSessions) { + globalSession.setStatus(GlobalStatus.Committed); + globalSession.end(); + } + } + } + /** * On branch status change test. * @@ -476,6 +589,63 @@ public void onCloseTest(GlobalSession globalSession) throws Exception { } } + @ParameterizedTest + @MethodSource("branchSessionsProvider") + public void stopBranchRetryTest(GlobalSession globalSession) throws Exception { + try { + SessionHolder.init(StoreConfig.SessionMode.FILE); + globalSession.begin(); + // wrong param for xid and branchId + Assertions.assertThrows(IllegalArgumentException.class, + () -> branchSessionService.stopBranchRetry("xid", null)); + Assertions.assertThrows(IllegalArgumentException.class, + () -> branchSessionService.stopBranchRetry(globalSession.getXid(), "test")); + + // wrong status for branch transaction + List branchSessions = globalSession.getBranchSessions(); + Assertions.assertThrows(IllegalArgumentException.class, + () -> branchSessionService.stopBranchRetry(globalSession.getXid(), + String.valueOf(branchSessions.get(0).getBranchId()))); + + // wrong status for global transaction + globalSession.setStatus(GlobalStatus.Begin); + Assertions.assertThrows(IllegalArgumentException.class, + () -> branchSessionService.stopBranchRetry(globalSession.getXid(), + String.valueOf(branchSessions.get(1).getBranchId()))); + + // success stop + globalSession.setStatus(GlobalStatus.CommitRetrying); + branchSessionService.stopBranchRetry(globalSession.getXid(), String.valueOf(branchSessions.get(1).getBranchId())); + GlobalSession newGlobalSession = SessionHolder.findGlobalSession(globalSession.getXid()); + Assertions.assertEquals(BranchStatus.STOP_RETRY, newGlobalSession.getBranchSessions().get(1).getStatus()); + } finally { + globalSession.setStatus(GlobalStatus.Committed); + globalSession.end(); + } + } + + @ParameterizedTest + @MethodSource("branchSessionsProvider") + public void restartBranchFailRetryTest(GlobalSession globalSession) throws Exception { + try { + SessionHolder.init(StoreConfig.SessionMode.FILE); + globalSession.begin(); + List branchSessions = globalSession.getBranchSessions(); + // wrong status for branch transaction + Assertions.assertThrows(IllegalArgumentException.class, + () -> branchSessionService.startBranchRetry(globalSession.getXid(), + String.valueOf(branchSessions.get(0).getBranchId()))); + // success + branchSessionService.startBranchRetry(globalSession.getXid(), + String.valueOf(branchSessions.get(2).getBranchId())); + GlobalSession newGlobalSession = SessionHolder.findGlobalSession(globalSession.getXid()); + Assertions.assertEquals(BranchStatus.Registered, newGlobalSession.getBranchSessions().get(2).getStatus()); + } finally { + globalSession.setStatus(GlobalStatus.Committed); + globalSession.end(); + } + } + /** * On end test. * @@ -559,6 +729,46 @@ static Stream globalSessionsWithPageResultProvider() throws ParseExce ); } + static Stream globalSessionForLockTestProvider() throws ParseException { + BranchSession branchSession1 = new BranchSession(); + branchSession1.setTransactionId(UUIDGenerator.generateUUID()); + branchSession1.setBranchId(1L); + branchSession1.setClientId("c1"); + branchSession1.setResourceGroupId(DEFAULT_TX_GROUP); + branchSession1.setResourceId("department"); + branchSession1.setLockKey("a:1,2"); + branchSession1.setBranchType(BranchType.AT); + branchSession1.setApplicationData("{\"data\":\"test\"}"); + branchSession1.setBranchType(BranchType.AT); + + BranchSession branchSession2 = new BranchSession(); + branchSession2.setTransactionId(UUIDGenerator.generateUUID()); + branchSession2.setBranchId(2L); + branchSession2.setClientId("c1"); + branchSession2.setResourceGroupId(DEFAULT_TX_GROUP); + branchSession2.setResourceId("department"); + branchSession2.setLockKey("e:3,4"); + branchSession2.setBranchType(BranchType.AT); + branchSession2.setApplicationData("{\"data\":\"test\"}"); + branchSession2.setBranchType(BranchType.AT); + + branchSession1.setTransactionId(397215L); + branchSession2.setTransactionId(92482L); + + final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + GlobalSession globalSession1 = new GlobalSession("demo-app", DEFAULT_TX_GROUP, "test1", 6000); + globalSession1.setXid("xid1"); + globalSession1.add(branchSession1); + globalSession1.setBeginTime(dateFormat.parse("2022-1-1 03:00:00").getTime()); + + GlobalSession globalSession2 = new GlobalSession("demo-app", DEFAULT_TX_GROUP, "test2", 6000); + globalSession2.setXid("ddd1"); + globalSession2.add(branchSession2); + globalSession2.setBeginTime(dateFormat.parse("2022-1-1 08:00:00").getTime()); + + return Stream.of(Arguments.of(Arrays.asList(globalSession1, globalSession2))); + } + /** * Branch session provider object [ ] [ ]. * @@ -580,4 +790,36 @@ static Stream branchSessionProvider() { ); } + /** + * Branch sessions provider object [ ] [ ]. + * + * @return the object [ ] [ ] + */ + static Stream branchSessionsProvider() { + GlobalSession globalSession = new GlobalSession("demo-app", DEFAULT_TX_GROUP, "test", 6000); + globalSession.setXid(XID.generateXID(globalSession.getTransactionId())); + globalSession.setStatus(GlobalStatus.CommitRetrying); + BranchSession branchSession = new BranchSession(); + branchSession.setBranchId(1L); + branchSession.setXid(globalSession.getXid()); + branchSession.setResourceGroupId(DEFAULT_TX_GROUP); + branchSession.setStatus(BranchStatus.PhaseOne_Failed); + branchSession.setBranchType(BranchType.AT); + BranchSession branchSession1 = new BranchSession(); + branchSession1.setBranchId(2L); + branchSession1.setXid(globalSession.getXid()); + branchSession1.setResourceGroupId(DEFAULT_TX_GROUP); + branchSession1.setStatus(BranchStatus.Registered); + branchSession1.setBranchType(BranchType.AT); + BranchSession branchSession2 = new BranchSession(); + branchSession2.setBranchId(3L); + branchSession2.setXid(globalSession.getXid()); + branchSession2.setResourceGroupId(DEFAULT_TX_GROUP); + branchSession2.setStatus(BranchStatus.STOP_RETRY); + branchSession2.setBranchType(BranchType.AT); + globalSession.add(branchSession); + globalSession.add(branchSession1); + globalSession.add(branchSession2); + return Stream.of(Arguments.of(globalSession)); + } } diff --git a/server/src/test/java/org/apache/seata/server/store/file/FileTransactionStoreManagerTest.java b/server/src/test/java/org/apache/seata/server/store/file/FileTransactionStoreManagerTest.java index d9c8ec08310..04f61c63fa5 100644 --- a/server/src/test/java/org/apache/seata/server/store/file/FileTransactionStoreManagerTest.java +++ b/server/src/test/java/org/apache/seata/server/store/file/FileTransactionStoreManagerTest.java @@ -66,15 +66,18 @@ public void testBigDataWrite() throws Exception { fileTransactionStoreManager = new FileTransactionStoreManager(seataFile.getAbsolutePath(), null); BranchSession branchSessionA = Mockito.mock(BranchSession.class); GlobalSession global = new GlobalSession(); + global.setGmtModified(System.currentTimeMillis()); Mockito.when(branchSessionA.encode()) .thenReturn(createBigBranchSessionData(global, (byte) 'A')); Mockito.when(branchSessionA.getApplicationData()) .thenReturn(new String(createBigApplicationData((byte) 'A'))); + Mockito.when(branchSessionA.getGmtModified()).thenReturn(global.getGmtModified()); BranchSession branchSessionB = Mockito.mock(BranchSession.class); Mockito.when(branchSessionB.encode()) .thenReturn(createBigBranchSessionData(global, (byte) 'B')); Mockito.when(branchSessionB.getApplicationData()) .thenReturn(new String(createBigApplicationData((byte) 'B'))); + Mockito.when(branchSessionB.getGmtModified()).thenReturn(global.getGmtModified()); Assertions.assertTrue(fileTransactionStoreManager.writeSession(TransactionStoreManager.LogOperation.BRANCH_ADD, branchSessionA)); Assertions.assertTrue(fileTransactionStoreManager.writeSession(TransactionStoreManager.LogOperation.BRANCH_ADD, branchSessionB)); List list = fileTransactionStoreManager.readWriteStore(2000, false); @@ -82,8 +85,10 @@ public void testBigDataWrite() throws Exception { Assertions.assertEquals(2, list.size()); BranchSession loadedBranchSessionA = (BranchSession) list.get(0).getSessionRequest(); Assertions.assertEquals(branchSessionA.getApplicationData(), loadedBranchSessionA.getApplicationData()); + Assertions.assertEquals(branchSessionA.getGmtModified(), loadedBranchSessionA.getGmtModified()); BranchSession loadedBranchSessionB = (BranchSession) list.get(1).getSessionRequest(); Assertions.assertEquals(branchSessionB.getApplicationData(), loadedBranchSessionB.getApplicationData()); + Assertions.assertEquals(branchSessionB.getGmtModified(), loadedBranchSessionB.getGmtModified()); } finally { if (fileTransactionStoreManager != null) { fileTransactionStoreManager.shutdown(); @@ -103,6 +108,7 @@ public void testFindTimeoutAndSave() throws Exception { List timeoutSessions = new ArrayList<>(); for (int i = 0; i < 100; i++) { GlobalSession globalSession = new GlobalSession("", "", "", 60000); + globalSession.setGmtModified(System.currentTimeMillis()); BranchSession branchSessionA = Mockito.mock(BranchSession.class); Mockito.when(branchSessionA.encode()) .thenReturn(createBigBranchSessionData(globalSession, (byte) 'A')); @@ -158,6 +164,7 @@ private byte[] createBigBranchSessionData(GlobalSession global, byte c) { + 4 // xidBytes.size + 1 // statusCode + 1 // lockstatus + + 8 // gmtModified + 1; //branchType String xid = global.getXid(); byte[] xidBytes = null; @@ -182,6 +189,7 @@ private byte[] createBigBranchSessionData(GlobalSession global, byte c) { } byteBuffer.put((byte) 0); byteBuffer.put((byte) 0); + byteBuffer.putLong(global.getGmtModified()); byteBuffer.put((byte) 0); BufferUtils.flip(byteBuffer); byte[] bytes = new byte[byteBuffer.limit()]; diff --git a/spring/src/main/java/org/apache/seata/rm/fence/SpringFenceHandler.java b/spring/src/main/java/org/apache/seata/rm/fence/SpringFenceHandler.java index 88003f6f4fb..3dba7efd078 100644 --- a/spring/src/main/java/org/apache/seata/rm/fence/SpringFenceHandler.java +++ b/spring/src/main/java/org/apache/seata/rm/fence/SpringFenceHandler.java @@ -321,6 +321,11 @@ public static boolean deleteFence(String xid, Long branchId) { }); } + @Override + public boolean deleteFenceByXidAndBranchId(String xid, Long branchId) { + return deleteFence(xid, branchId); + } + /** * Delete Common Fence By Datetime * diff --git a/tcc/src/main/java/org/apache/seata/rm/tcc/RMHandlerTCC.java b/tcc/src/main/java/org/apache/seata/rm/tcc/RMHandlerTCC.java index c7040fa21d0..02ea7254c05 100644 --- a/tcc/src/main/java/org/apache/seata/rm/tcc/RMHandlerTCC.java +++ b/tcc/src/main/java/org/apache/seata/rm/tcc/RMHandlerTCC.java @@ -16,23 +16,54 @@ */ package org.apache.seata.rm.tcc; +import org.apache.seata.core.model.BranchStatus; import org.apache.seata.core.model.BranchType; import org.apache.seata.core.model.ResourceManager; +import org.apache.seata.core.protocol.ResultCode; +import org.apache.seata.core.protocol.transaction.BranchDeleteRequest; +import org.apache.seata.core.protocol.transaction.BranchDeleteResponse; import org.apache.seata.core.protocol.transaction.UndoLogDeleteRequest; +import org.apache.seata.integration.tx.api.fence.DefaultCommonFenceHandler; import org.apache.seata.rm.AbstractRMHandler; import org.apache.seata.rm.DefaultResourceManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * The type Rm handler tcc. * */ public class RMHandlerTCC extends AbstractRMHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(RMHandlerTCC.class); @Override public void handle(UndoLogDeleteRequest request) { //DO nothing } + @Override + public BranchDeleteResponse handle(BranchDeleteRequest request) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Start tcc delete branch fence, xid:{}, branchId:{}", + request.getXid(), request.getBranchId()); + } + BranchDeleteResponse branchDeleteResponse = new BranchDeleteResponse(); + try { + boolean result = DefaultCommonFenceHandler.get(). + deleteFenceByXidAndBranchId(request.getXid(), request.getBranchId()); + ResultCode code = result ? ResultCode.Success : ResultCode.Failed; + branchDeleteResponse.setResultCode(code); + } catch (Exception e) { + LOGGER.error("Delete tcc fence fail, xid:{}, branchId:{}", request.getXid(), request.getBranchId(), e); + branchDeleteResponse.setResultCode(ResultCode.Failed); + } + branchDeleteResponse.setXid(request.getXid()); + branchDeleteResponse.setBranchId(request.getBranchId()); + // this branch status is no importance + branchDeleteResponse.setBranchStatus(BranchStatus.Unknown); + return branchDeleteResponse; + } + @Override protected ResourceManager getResourceManager() { return DefaultResourceManager.get().getResourceManager(BranchType.TCC); From d94180bc2b5befec171c0a502965ad1f7ffa3a6f Mon Sep 17 00:00:00 2001 From: Jingliu Xiong <928124786@qq.com> Date: Fri, 22 Nov 2024 12:21:42 +0800 Subject: [PATCH 02/31] fix: add ts locales --- .../src/main/resources/static/console-fe/src/locales/en-us.ts | 2 ++ .../src/main/resources/static/console-fe/src/locales/zh-cn.ts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/console/src/main/resources/static/console-fe/src/locales/en-us.ts b/console/src/main/resources/static/console-fe/src/locales/en-us.ts index 0dea3a95bed..699e4e0b9f3 100644 --- a/console/src/main/resources/static/console-fe/src/locales/en-us.ts +++ b/console/src/main/resources/static/console-fe/src/locales/en-us.ts @@ -77,6 +77,8 @@ const enUs: ILocale = { inputFilterPlaceholder: 'Please enter filter criteria', resetButtonLabel: 'Reset', searchButtonLabel: 'Search', + operateTitle: 'operate', + deleteGlobalLockTitle: 'Delete global lock', }, }; diff --git a/console/src/main/resources/static/console-fe/src/locales/zh-cn.ts b/console/src/main/resources/static/console-fe/src/locales/zh-cn.ts index 8ff59f86b10..4d522f7a941 100644 --- a/console/src/main/resources/static/console-fe/src/locales/zh-cn.ts +++ b/console/src/main/resources/static/console-fe/src/locales/zh-cn.ts @@ -77,6 +77,8 @@ const zhCn: ILocale = { inputFilterPlaceholder: '请输入筛选条件', resetButtonLabel: '重置', searchButtonLabel: '搜索', + operateTitle: '操作', + deleteGlobalLockTitle: '删除全局锁', }, }; From d5c41d730de56eae37c68f2443140254847050f1 Mon Sep 17 00:00:00 2001 From: Jingliu Xiong <928124786@qq.com> Date: Wed, 27 Nov 2024 22:53:46 +0800 Subject: [PATCH 03/31] feat: add file license --- .../transaction/BranchDeleteResponse.java | 18 ++++++++++++++++-- .../client/RmBranchDeleteProcessor.java | 16 ++++++++++++++++ .../BranchDeleteRequestConvertor.java | 16 ++++++++++++++++ .../BranchDeleteResponseConvertor.java | 16 ++++++++++++++++ .../console/exception/ConsoleException.java | 16 ++++++++++++++++ .../console/impl/AbstractBranchService.java | 16 ++++++++++++++++ .../console/impl/AbstractGlobalService.java | 16 ++++++++++++++++ .../console/impl/AbstractLockService.java | 16 ++++++++++++++++ .../server/console/impl/AbstractService.java | 16 ++++++++++++++++ 9 files changed, 144 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/apache/seata/core/protocol/transaction/BranchDeleteResponse.java b/core/src/main/java/org/apache/seata/core/protocol/transaction/BranchDeleteResponse.java index 4517fc6c526..1ba23f19b11 100644 --- a/core/src/main/java/org/apache/seata/core/protocol/transaction/BranchDeleteResponse.java +++ b/core/src/main/java/org/apache/seata/core/protocol/transaction/BranchDeleteResponse.java @@ -1,11 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.core.protocol.transaction; import org.apache.seata.core.protocol.MessageType; /** * BranchDeleteResponse - * - * */ public class BranchDeleteResponse extends AbstractBranchEndResponse { @Override diff --git a/core/src/main/java/org/apache/seata/core/rpc/processor/client/RmBranchDeleteProcessor.java b/core/src/main/java/org/apache/seata/core/rpc/processor/client/RmBranchDeleteProcessor.java index a0c7f5309fc..d0694d85063 100644 --- a/core/src/main/java/org/apache/seata/core/rpc/processor/client/RmBranchDeleteProcessor.java +++ b/core/src/main/java/org/apache/seata/core/rpc/processor/client/RmBranchDeleteProcessor.java @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.core.rpc.processor.client; import io.netty.channel.ChannelHandlerContext; diff --git a/serializer/seata-serializer-protobuf/src/main/java/org/apache/seata/serializer/protobuf/convertor/BranchDeleteRequestConvertor.java b/serializer/seata-serializer-protobuf/src/main/java/org/apache/seata/serializer/protobuf/convertor/BranchDeleteRequestConvertor.java index 786f0353880..0604304eaa9 100644 --- a/serializer/seata-serializer-protobuf/src/main/java/org/apache/seata/serializer/protobuf/convertor/BranchDeleteRequestConvertor.java +++ b/serializer/seata-serializer-protobuf/src/main/java/org/apache/seata/serializer/protobuf/convertor/BranchDeleteRequestConvertor.java @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.serializer.protobuf.convertor; import org.apache.seata.core.model.BranchType; diff --git a/serializer/seata-serializer-protobuf/src/main/java/org/apache/seata/serializer/protobuf/convertor/BranchDeleteResponseConvertor.java b/serializer/seata-serializer-protobuf/src/main/java/org/apache/seata/serializer/protobuf/convertor/BranchDeleteResponseConvertor.java index 7f3a4501fa4..ab60b257ddb 100644 --- a/serializer/seata-serializer-protobuf/src/main/java/org/apache/seata/serializer/protobuf/convertor/BranchDeleteResponseConvertor.java +++ b/serializer/seata-serializer-protobuf/src/main/java/org/apache/seata/serializer/protobuf/convertor/BranchDeleteResponseConvertor.java @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.serializer.protobuf.convertor; import org.apache.seata.core.exception.TransactionExceptionCode; diff --git a/server/src/main/java/org/apache/seata/server/console/exception/ConsoleException.java b/server/src/main/java/org/apache/seata/server/console/exception/ConsoleException.java index 9f71db64291..50a65268efe 100644 --- a/server/src/main/java/org/apache/seata/server/console/exception/ConsoleException.java +++ b/server/src/main/java/org/apache/seata/server/console/exception/ConsoleException.java @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.server.console.exception; public class ConsoleException extends RuntimeException { diff --git a/server/src/main/java/org/apache/seata/server/console/impl/AbstractBranchService.java b/server/src/main/java/org/apache/seata/server/console/impl/AbstractBranchService.java index 1ca67827176..11f8cb7d7b6 100644 --- a/server/src/main/java/org/apache/seata/server/console/impl/AbstractBranchService.java +++ b/server/src/main/java/org/apache/seata/server/console/impl/AbstractBranchService.java @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.server.console.impl; import org.apache.seata.common.result.SingleResult; diff --git a/server/src/main/java/org/apache/seata/server/console/impl/AbstractGlobalService.java b/server/src/main/java/org/apache/seata/server/console/impl/AbstractGlobalService.java index 156d645043e..3c9552152f3 100644 --- a/server/src/main/java/org/apache/seata/server/console/impl/AbstractGlobalService.java +++ b/server/src/main/java/org/apache/seata/server/console/impl/AbstractGlobalService.java @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.server.console.impl; import org.apache.seata.common.result.SingleResult; diff --git a/server/src/main/java/org/apache/seata/server/console/impl/AbstractLockService.java b/server/src/main/java/org/apache/seata/server/console/impl/AbstractLockService.java index 9f5f3387df6..3b3f7634ae4 100644 --- a/server/src/main/java/org/apache/seata/server/console/impl/AbstractLockService.java +++ b/server/src/main/java/org/apache/seata/server/console/impl/AbstractLockService.java @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.server.console.impl; import org.apache.seata.common.util.StringUtils; diff --git a/server/src/main/java/org/apache/seata/server/console/impl/AbstractService.java b/server/src/main/java/org/apache/seata/server/console/impl/AbstractService.java index 571ef3383eb..8b5dc82b652 100644 --- a/server/src/main/java/org/apache/seata/server/console/impl/AbstractService.java +++ b/server/src/main/java/org/apache/seata/server/console/impl/AbstractService.java @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.server.console.impl; import org.apache.seata.common.util.StringUtils; From 7a068646119dda9551651d8bdeb969328a8565d3 Mon Sep 17 00:00:00 2001 From: Jingliu Xiong <928124786@qq.com> Date: Wed, 27 Nov 2024 23:28:03 +0800 Subject: [PATCH 04/31] fix: fix session unit test --- .../java/org/apache/seata/server/storage/SessionConverter.java | 3 +++ .../seata/server/raft/execute/BranchSessionExecuteTest.java | 2 ++ .../seata/server/raft/execute/GlobalSessionExecuteTest.java | 1 + 3 files changed, 6 insertions(+) diff --git a/server/src/main/java/org/apache/seata/server/storage/SessionConverter.java b/server/src/main/java/org/apache/seata/server/storage/SessionConverter.java index d4898408843..ab5b3f2d7dc 100644 --- a/server/src/main/java/org/apache/seata/server/storage/SessionConverter.java +++ b/server/src/main/java/org/apache/seata/server/storage/SessionConverter.java @@ -17,6 +17,7 @@ package org.apache.seata.server.storage; import java.util.ArrayList; +import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -106,6 +107,7 @@ public static void convertGlobalTransactionDO(GlobalTransactionDO globalTransact globalTransactionDO.setTransactionName(globalSession.getTransactionName()); globalTransactionDO.setTransactionServiceGroup(globalSession.getTransactionServiceGroup()); globalTransactionDO.setApplicationData(globalSession.getApplicationData()); + globalTransactionDO.setGmtModified(new Date(globalSession.getGmtModified())); } public static BranchTransactionDO convertBranchTransactionDO(SessionStorable session) { @@ -139,6 +141,7 @@ public static void convertBranchTransaction(BranchTransactionDO branchTransactio branchTransactionDO.setApplicationData(branchSession.getApplicationData()); branchTransactionDO.setResourceId(branchSession.getResourceId()); branchTransactionDO.setStatus(branchSession.getStatus().getCode()); + branchTransactionDO.setGmtModified(new Date(branchSession.getGmtModified())); if (branchTransactionDO instanceof BranchTransactionDTO) { ((BranchTransactionDTO)branchTransactionDO).setLockKey(branchSession.getLockKey()); } diff --git a/server/src/test/java/org/apache/seata/server/raft/execute/BranchSessionExecuteTest.java b/server/src/test/java/org/apache/seata/server/raft/execute/BranchSessionExecuteTest.java index ade3a25ef9c..55a6779dae9 100644 --- a/server/src/test/java/org/apache/seata/server/raft/execute/BranchSessionExecuteTest.java +++ b/server/src/test/java/org/apache/seata/server/raft/execute/BranchSessionExecuteTest.java @@ -136,6 +136,7 @@ private static GlobalSession mockGlobalSession() { session.setApplicationData("hello, world"); session.setTransactionId(123); session.setBeginTime(System.currentTimeMillis()); + session.setGmtModified(System.currentTimeMillis()); return session; } @@ -150,6 +151,7 @@ private static BranchSession mockBranchSession() { session.setLockKey("test"); session.setBranchType(BranchType.AT); session.setApplicationData("hello, world"); + session.setGmtModified(System.currentTimeMillis()); return session; } diff --git a/server/src/test/java/org/apache/seata/server/raft/execute/GlobalSessionExecuteTest.java b/server/src/test/java/org/apache/seata/server/raft/execute/GlobalSessionExecuteTest.java index 9594eb7de56..80d8d687abc 100644 --- a/server/src/test/java/org/apache/seata/server/raft/execute/GlobalSessionExecuteTest.java +++ b/server/src/test/java/org/apache/seata/server/raft/execute/GlobalSessionExecuteTest.java @@ -127,6 +127,7 @@ private static GlobalSession mockGlobalSession() { session.setApplicationData("hello, world"); session.setTransactionId(123); session.setBeginTime(System.currentTimeMillis()); + session.setGmtModified(System.currentTimeMillis()); return session; } From d44cfb267a1aa98d336baf9eaa2c227865365bae Mon Sep 17 00:00:00 2001 From: Jingliu Xiong <928124786@qq.com> Date: Thu, 28 Nov 2024 20:04:11 +0800 Subject: [PATCH 05/31] fix: fix session unit test --- .../java/org/apache/seata/server/storage/SessionConverter.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/server/src/main/java/org/apache/seata/server/storage/SessionConverter.java b/server/src/main/java/org/apache/seata/server/storage/SessionConverter.java index ab5b3f2d7dc..d4898408843 100644 --- a/server/src/main/java/org/apache/seata/server/storage/SessionConverter.java +++ b/server/src/main/java/org/apache/seata/server/storage/SessionConverter.java @@ -17,7 +17,6 @@ package org.apache.seata.server.storage; import java.util.ArrayList; -import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -107,7 +106,6 @@ public static void convertGlobalTransactionDO(GlobalTransactionDO globalTransact globalTransactionDO.setTransactionName(globalSession.getTransactionName()); globalTransactionDO.setTransactionServiceGroup(globalSession.getTransactionServiceGroup()); globalTransactionDO.setApplicationData(globalSession.getApplicationData()); - globalTransactionDO.setGmtModified(new Date(globalSession.getGmtModified())); } public static BranchTransactionDO convertBranchTransactionDO(SessionStorable session) { @@ -141,7 +139,6 @@ public static void convertBranchTransaction(BranchTransactionDO branchTransactio branchTransactionDO.setApplicationData(branchSession.getApplicationData()); branchTransactionDO.setResourceId(branchSession.getResourceId()); branchTransactionDO.setStatus(branchSession.getStatus().getCode()); - branchTransactionDO.setGmtModified(new Date(branchSession.getGmtModified())); if (branchTransactionDO instanceof BranchTransactionDTO) { ((BranchTransactionDTO)branchTransactionDO).setLockKey(branchSession.getLockKey()); } From 3d076f6d8c9ae77f0851a221025626515b4067d6 Mon Sep 17 00:00:00 2001 From: Jingliu Xiong <928124786@qq.com> Date: Sun, 1 Dec 2024 20:18:34 +0800 Subject: [PATCH 06/31] feat: fix sessionConvert test --- .../org/apache/seata/server/session/BranchSession.java | 6 +++--- .../org/apache/seata/server/session/GlobalSession.java | 6 +++--- .../org/apache/seata/server/storage/SessionConverter.java | 8 ++++++-- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/server/src/main/java/org/apache/seata/server/session/BranchSession.java b/server/src/main/java/org/apache/seata/server/session/BranchSession.java index acb5ade6ee3..9bd19d481ee 100644 --- a/server/src/main/java/org/apache/seata/server/session/BranchSession.java +++ b/server/src/main/java/org/apache/seata/server/session/BranchSession.java @@ -74,7 +74,7 @@ public class BranchSession implements Lockable, Comparable, Sessi private LockStatus lockStatus = Locked; - private Long gmtModified; + private long gmtModified; private final Map> lockHolder; @@ -89,11 +89,11 @@ public BranchSession(BranchType branchType) { this.lockHolder = branchType == BranchType.AT ? new ConcurrentHashMap<>(8) : Collections.emptyMap(); } - public Long getGmtModified() { + public long getGmtModified() { return gmtModified; } - public void setGmtModified(Long gmtModified) { + public void setGmtModified(long gmtModified) { this.gmtModified = gmtModified; } diff --git a/server/src/main/java/org/apache/seata/server/session/GlobalSession.java b/server/src/main/java/org/apache/seata/server/session/GlobalSession.java index b462d55ec66..9f2439e2ade 100644 --- a/server/src/main/java/org/apache/seata/server/session/GlobalSession.java +++ b/server/src/main/java/org/apache/seata/server/session/GlobalSession.java @@ -97,7 +97,7 @@ public class GlobalSession implements SessionLifecycle, SessionStorable { private String applicationData; - private Long gmtModified; + private long gmtModified; private final boolean lazyLoadBranch; @@ -606,11 +606,11 @@ public void setActive(boolean active) { this.active = active; } - public Long getGmtModified() { + public long getGmtModified() { return gmtModified; } - public void setGmtModified(Long gmtModified) { + public void setGmtModified(long gmtModified) { this.gmtModified = gmtModified; } diff --git a/server/src/main/java/org/apache/seata/server/storage/SessionConverter.java b/server/src/main/java/org/apache/seata/server/storage/SessionConverter.java index d4898408843..70ebf933b60 100644 --- a/server/src/main/java/org/apache/seata/server/storage/SessionConverter.java +++ b/server/src/main/java/org/apache/seata/server/storage/SessionConverter.java @@ -55,7 +55,9 @@ public static GlobalSession convertGlobalSession(GlobalTransactionDO globalTrans session.setStatus(GlobalStatus.get(globalTransactionDO.getStatus())); session.setApplicationData(globalTransactionDO.getApplicationData()); session.setBeginTime(globalTransactionDO.getBeginTime()); - session.setGmtModified(globalTransactionDO.getGmtModified().getTime()); + if (globalTransactionDO.getGmtModified() != null) { + session.setGmtModified(globalTransactionDO.getGmtModified().getTime()); + } return session; } @@ -77,7 +79,9 @@ public static BranchSession convertBranchSession(BranchTransactionDO branchTrans branchSession.setClientId(branchTransactionDO.getClientId()); branchSession.setResourceGroupId(branchTransactionDO.getResourceGroupId()); branchSession.setStatus(BranchStatus.get(branchTransactionDO.getStatus())); - branchSession.setGmtModified(branchTransactionDO.getGmtModified().getTime()); + if (branchTransactionDO.getGmtModified() != null) { + branchSession.setGmtModified(branchTransactionDO.getGmtModified().getTime()); + } if (branchTransactionDO instanceof BranchTransactionDTO) { branchSession.setLockKey(((BranchTransactionDTO)branchTransactionDO).getLockKey()); } From 9af570e746c0336c4f35315cc3dd0e1f2f0b4a96 Mon Sep 17 00:00:00 2001 From: Jingliu Xiong <928124786@qq.com> Date: Tue, 10 Dec 2024 21:01:46 +0800 Subject: [PATCH 07/31] fix: fix RMHandlerSagaAnnotation compile --- .../org/apache/seata/saga/rm/RMHandlerSagaAnnotation.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/saga/seata-saga-annotation/src/main/java/org/apache/seata/saga/rm/RMHandlerSagaAnnotation.java b/saga/seata-saga-annotation/src/main/java/org/apache/seata/saga/rm/RMHandlerSagaAnnotation.java index 21dad9e5b47..c785b201ff2 100644 --- a/saga/seata-saga-annotation/src/main/java/org/apache/seata/saga/rm/RMHandlerSagaAnnotation.java +++ b/saga/seata-saga-annotation/src/main/java/org/apache/seata/saga/rm/RMHandlerSagaAnnotation.java @@ -18,6 +18,8 @@ import org.apache.seata.core.model.BranchType; import org.apache.seata.core.model.ResourceManager; +import org.apache.seata.core.protocol.transaction.BranchDeleteRequest; +import org.apache.seata.core.protocol.transaction.BranchDeleteResponse; import org.apache.seata.rm.AbstractRMHandler; import org.apache.seata.rm.DefaultResourceManager; @@ -36,4 +38,8 @@ public BranchType getBranchType() { return BranchType.SAGA_ANNOTATION; } + @Override + public BranchDeleteResponse handle(BranchDeleteRequest request) { + return null; + } } From ff7216eff53e88868c737d3d0803769ad633d6e9 Mon Sep 17 00:00:00 2001 From: Jingliu Xiong <928124786@qq.com> Date: Wed, 11 Dec 2024 22:47:46 +0800 Subject: [PATCH 08/31] fix: fix GlobalStatus comment --- .../java/org/apache/seata/core/model/GlobalStatus.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/org/apache/seata/core/model/GlobalStatus.java b/core/src/main/java/org/apache/seata/core/model/GlobalStatus.java index 846113e2b6f..3a73f0d24f9 100644 --- a/core/src/main/java/org/apache/seata/core/model/GlobalStatus.java +++ b/core/src/main/java/org/apache/seata/core/model/GlobalStatus.java @@ -133,17 +133,17 @@ public enum GlobalStatus { /** * Deleting . */ - // Finally: deleting - Deleting(18, "global transaction is deleting, willing delete soon"), + // Deleting global transaction + Deleting(18, "global transaction is deleting"), /** - * The rollback retry Timeout . + * Stop commit retry . */ // stop commit retry StopCommitRetry(19,"global transaction is retry commit but stop retry now"), /** - * The rollback retry Timeout . + * Stop rollback retry . */ // stop rollback retry StopRollbackRetry(20,"global transaction is retry rollback but stop retry now"); From 573419a5ee1745bcedc058ae3133d264f57125ac Mon Sep 17 00:00:00 2001 From: Jingliu Xiong <928124786@qq.com> Date: Mon, 16 Dec 2024 22:42:52 +0800 Subject: [PATCH 09/31] fix: fix status in ts control --- .../apache/seata/core/model/GlobalStatus.java | 12 ++++++------ .../console/impl/AbstractBranchService.java | 8 ++++---- .../console/impl/AbstractGlobalService.java | 19 +++++++++---------- .../server/console/impl/AbstractService.java | 11 +++++++++-- .../raft/BranchSessionRaftServiceImpl.java | 4 ++++ .../coordinator/DefaultCoordinator.java | 4 ++-- .../seata/server/session/GlobalSession.java | 4 ++-- .../seata/server/session/SessionHolder.java | 4 ++-- .../db/session/DataBaseSessionManager.java | 2 +- .../redis/session/RedisSessionManager.java | 2 +- .../session/FileSessionManagerTest.java | 9 ++++----- 11 files changed, 44 insertions(+), 35 deletions(-) diff --git a/core/src/main/java/org/apache/seata/core/model/GlobalStatus.java b/core/src/main/java/org/apache/seata/core/model/GlobalStatus.java index 3a73f0d24f9..76371437ca4 100644 --- a/core/src/main/java/org/apache/seata/core/model/GlobalStatus.java +++ b/core/src/main/java/org/apache/seata/core/model/GlobalStatus.java @@ -137,16 +137,16 @@ public enum GlobalStatus { Deleting(18, "global transaction is deleting"), /** - * Stop commit retry . + * Stop commit or commit retry . */ - // stop commit retry - StopCommitRetry(19,"global transaction is retry commit but stop retry now"), + // stop commit or commit retry + StopCommitOrCommitRetry(19,"global transaction is commit or retry commit but stop now"), /** - * Stop rollback retry . + * Stop rollback or rollback retry . */ - // stop rollback retry - StopRollbackRetry(20,"global transaction is retry rollback but stop retry now"); + // stop rollback or rollback retry + StopRollbackOrRollbackRetry(20,"global transaction is rollback or retry rollback but stop now"); private final int code; private final String desc; diff --git a/server/src/main/java/org/apache/seata/server/console/impl/AbstractBranchService.java b/server/src/main/java/org/apache/seata/server/console/impl/AbstractBranchService.java index 11f8cb7d7b6..999520eb4b2 100644 --- a/server/src/main/java/org/apache/seata/server/console/impl/AbstractBranchService.java +++ b/server/src/main/java/org/apache/seata/server/console/impl/AbstractBranchService.java @@ -45,8 +45,8 @@ public SingleResult stopBranchRetry(String xid, String branchId) { } GlobalStatus status = globalSession.getStatus(); BranchStatus newStatus = RETRY_STATUS.contains(status) || GlobalStatus.Rollbacking.equals(status) || - GlobalStatus.Committing.equals(status) || GlobalStatus.StopRollbackRetry.equals(status) || - GlobalStatus.StopCommitRetry.equals(status) ? BranchStatus.STOP_RETRY : null; + GlobalStatus.Committing.equals(status) || GlobalStatus.StopRollbackOrRollbackRetry.equals(status) || + GlobalStatus.StopCommitOrCommitRetry.equals(status) ? BranchStatus.STOP_RETRY : null; if (newStatus == null) { throw new IllegalArgumentException("wrong status for global status"); } @@ -94,8 +94,8 @@ public SingleResult deleteBranchSession(String xid, String branchId) { GlobalStatus globalStatus = globalSession.getStatus(); BranchSession branchSession = checkResult.getBranchSession(); if (FAIL_STATUS.contains(globalStatus) || RETRY_STATUS.contains(globalStatus) - || FINISH_STATUS.contains(globalStatus) || GlobalStatus.StopRollbackRetry == globalStatus - || GlobalStatus.StopCommitRetry == globalStatus || GlobalStatus.Deleting == globalStatus) { + || FINISH_STATUS.contains(globalStatus) || GlobalStatus.StopRollbackOrRollbackRetry == globalStatus + || GlobalStatus.StopCommitOrCommitRetry == globalStatus || GlobalStatus.Deleting == globalStatus) { try { boolean deleted = doDeleteBranch(globalSession, branchSession); return deleted ? SingleResult.success() : diff --git a/server/src/main/java/org/apache/seata/server/console/impl/AbstractGlobalService.java b/server/src/main/java/org/apache/seata/server/console/impl/AbstractGlobalService.java index 3c9552152f3..7fc2cb83011 100644 --- a/server/src/main/java/org/apache/seata/server/console/impl/AbstractGlobalService.java +++ b/server/src/main/java/org/apache/seata/server/console/impl/AbstractGlobalService.java @@ -34,8 +34,8 @@ public SingleResult deleteGlobalSession(String xid) { GlobalSession globalSession = checkGlobalSession(xid); GlobalStatus globalStatus = globalSession.getStatus(); if (FAIL_STATUS.contains(globalStatus) || RETRY_STATUS.contains(globalStatus) || FINISH_STATUS.contains(globalStatus) - || GlobalStatus.Deleting.equals(globalStatus) || GlobalStatus.StopCommitRetry.equals(globalStatus) - || GlobalStatus.StopRollbackRetry.equals(globalStatus)) { + || GlobalStatus.Deleting.equals(globalStatus) || GlobalStatus.StopCommitOrCommitRetry.equals(globalStatus) + || GlobalStatus.StopRollbackOrRollbackRetry.equals(globalStatus)) { try { if (!GlobalStatus.Deleting.equals(globalStatus)) { globalSession.changeGlobalStatus(GlobalStatus.Deleting); @@ -60,10 +60,9 @@ public SingleResult deleteGlobalSession(String xid) { public SingleResult stopGlobalRetry(String xid) { GlobalSession globalSession = checkGlobalSession(xid); GlobalStatus globalStatus = globalSession.getStatus(); - GlobalStatus newStatus = RETRY_COMMIT_STATUS.contains(globalStatus) || - GlobalStatus.Committing.equals(globalStatus) ? GlobalStatus.StopCommitRetry : - RETRY_ROLLBACK_STATUS.contains(globalStatus) || - GlobalStatus.Rollbacking.equals(globalStatus) ? GlobalStatus.StopRollbackRetry : null; + GlobalStatus newStatus = COMMIT_ING_STATUS.contains(globalStatus) ? GlobalStatus.StopCommitOrCommitRetry : + RETRY_ROLLBACK_STATUS.contains(globalStatus) || ROLLBACK_ING_STATUS.contains(globalStatus) + ? GlobalStatus.StopRollbackOrRollbackRetry : null; if (newStatus == null) { throw new IllegalArgumentException("current global transaction status is not support stop"); } @@ -79,8 +78,8 @@ public SingleResult stopGlobalRetry(String xid) { public SingleResult startGlobalRetry(String xid) { GlobalSession globalSession = checkGlobalSession(xid); GlobalStatus globalStatus = globalSession.getStatus(); - GlobalStatus newStatus = GlobalStatus.StopCommitRetry.equals(globalStatus) ? GlobalStatus.CommitRetrying : - GlobalStatus.StopRollbackRetry.equals(globalStatus) ? GlobalStatus.RollbackRetrying : null; + GlobalStatus newStatus = GlobalStatus.StopCommitOrCommitRetry.equals(globalStatus) ? GlobalStatus.CommitRetrying : + GlobalStatus.StopRollbackOrRollbackRetry.equals(globalStatus) ? GlobalStatus.RollbackRetrying : null; if (newStatus == null) { throw new IllegalArgumentException("current global transaction status is not support start"); } @@ -99,7 +98,7 @@ public SingleResult sendCommitOrRollback(String xid) { try { boolean res; if (RETRY_COMMIT_STATUS.contains(globalStatus) || GlobalStatus.Committing.equals(globalStatus) - || GlobalStatus.StopCommitRetry.equals(globalStatus)) { + || GlobalStatus.StopCommitOrCommitRetry.equals(globalStatus)) { res = DefaultCoordinator.getInstance().getCore().doGlobalCommit(globalSession, false); if (res && globalSession.hasBranch() && globalSession.hasATBranch()) { globalSession.clean(); @@ -108,7 +107,7 @@ public SingleResult sendCommitOrRollback(String xid) { globalSession.end(); } } else if (RETRY_ROLLBACK_STATUS.contains(globalStatus) || GlobalStatus.Rollbacking.equals(globalStatus) - || GlobalStatus.StopRollbackRetry.equals(globalStatus)) { + || GlobalStatus.StopRollbackOrRollbackRetry.equals(globalStatus)) { res = DefaultCoordinator.getInstance().getCore().doGlobalRollback(globalSession, false); // the record is not deleted if (res && SessionHolder.findGlobalSession(xid) != null) { diff --git a/server/src/main/java/org/apache/seata/server/console/impl/AbstractService.java b/server/src/main/java/org/apache/seata/server/console/impl/AbstractService.java index 8b5dc82b652..158204cf05f 100644 --- a/server/src/main/java/org/apache/seata/server/console/impl/AbstractService.java +++ b/server/src/main/java/org/apache/seata/server/console/impl/AbstractService.java @@ -30,6 +30,7 @@ import org.slf4j.LoggerFactory; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.concurrent.TimeoutException; @@ -44,11 +45,17 @@ public abstract class AbstractService { protected final LockManager lockManager = LockerManagerFactory.getLockManager(); - protected static final List RETRY_COMMIT_STATUS = Arrays.asList(GlobalStatus.CommitRetrying, - GlobalStatus.Committed); + protected static final List RETRY_COMMIT_STATUS = Arrays.asList(GlobalStatus.CommitRetrying); + protected static final List RETRY_ROLLBACK_STATUS = Arrays.asList(GlobalStatus.RollbackRetrying, GlobalStatus.TimeoutRollbackRetrying, GlobalStatus.TimeoutRollbacking); + protected static final List COMMIT_ING_STATUS = Stream.concat(RETRY_COMMIT_STATUS.stream(), + Collections.singletonList(GlobalStatus.Committing).stream()).collect(Collectors.toList()); + + protected static final List ROLLBACK_ING_STATUS = Stream.concat(RETRY_ROLLBACK_STATUS.stream(), + Collections.singletonList(GlobalStatus.Rollbacking).stream()).collect(Collectors.toList()); + protected static final List RETRY_STATUS = Stream.concat(RETRY_COMMIT_STATUS.stream(), RETRY_ROLLBACK_STATUS.stream()).collect(Collectors.toList()); diff --git a/server/src/main/java/org/apache/seata/server/console/impl/raft/BranchSessionRaftServiceImpl.java b/server/src/main/java/org/apache/seata/server/console/impl/raft/BranchSessionRaftServiceImpl.java index 5adcaca207a..4200ef106a4 100644 --- a/server/src/main/java/org/apache/seata/server/console/impl/raft/BranchSessionRaftServiceImpl.java +++ b/server/src/main/java/org/apache/seata/server/console/impl/raft/BranchSessionRaftServiceImpl.java @@ -16,7 +16,11 @@ */ package org.apache.seata.server.console.impl.raft; +import org.apache.seata.common.result.PageResult; +import org.apache.seata.server.console.impl.AbstractBranchService; import org.apache.seata.server.console.impl.file.BranchSessionFileServiceImpl; +import org.apache.seata.server.console.service.BranchSessionService; +import org.apache.seata.server.console.vo.BranchSessionVO; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Component; diff --git a/server/src/main/java/org/apache/seata/server/coordinator/DefaultCoordinator.java b/server/src/main/java/org/apache/seata/server/coordinator/DefaultCoordinator.java index 93efc8b8f43..e37b08fb544 100644 --- a/server/src/main/java/org/apache/seata/server/coordinator/DefaultCoordinator.java +++ b/server/src/main/java/org/apache/seata/server/coordinator/DefaultCoordinator.java @@ -506,8 +506,8 @@ protected void handleAutoRestart() { } }); GlobalStatus globalStatus = globalSession.getStatus(); - GlobalStatus newStatus = GlobalStatus.StopCommitRetry.equals(globalStatus) ? GlobalStatus.CommitRetrying : - GlobalStatus.StopRollbackRetry.equals(globalStatus) ? GlobalStatus.RollbackRetrying : null; + GlobalStatus newStatus = GlobalStatus.StopCommitOrCommitRetry.equals(globalStatus) ? GlobalStatus.CommitRetrying : + GlobalStatus.StopRollbackOrRollbackRetry.equals(globalStatus) ? GlobalStatus.RollbackRetrying : null; try { if (Objects.nonNull(newStatus) && System.currentTimeMillis() - globalSession.getGmtModified() >= AUTO_RETRY_TIME) { if (LOGGER.isDebugEnabled()) { diff --git a/server/src/main/java/org/apache/seata/server/session/GlobalSession.java b/server/src/main/java/org/apache/seata/server/session/GlobalSession.java index 9f2439e2ade..baf2c23bebb 100644 --- a/server/src/main/java/org/apache/seata/server/session/GlobalSession.java +++ b/server/src/main/java/org/apache/seata/server/session/GlobalSession.java @@ -801,7 +801,7 @@ public void asyncCommit() throws TransactionException { } public void queueToRetryCommit() throws TransactionException { - if (this.status == GlobalStatus.StopCommitRetry || this.status == GlobalStatus.StopRollbackRetry) { + if (this.status == GlobalStatus.StopCommitOrCommitRetry || this.status == GlobalStatus.StopRollbackOrRollbackRetry) { return; } changeGlobalStatus(GlobalStatus.CommitRetrying); @@ -809,7 +809,7 @@ public void queueToRetryCommit() throws TransactionException { public void queueToRetryRollback() throws TransactionException { GlobalStatus currentStatus = this.getStatus(); - if (currentStatus == GlobalStatus.StopCommitRetry || currentStatus == GlobalStatus.StopRollbackRetry) { + if (currentStatus == GlobalStatus.StopCommitOrCommitRetry || currentStatus == GlobalStatus.StopRollbackOrRollbackRetry) { return; } GlobalStatus newStatus; diff --git a/server/src/main/java/org/apache/seata/server/session/SessionHolder.java b/server/src/main/java/org/apache/seata/server/session/SessionHolder.java index 255da3803c0..12376a2ccf5 100644 --- a/server/src/main/java/org/apache/seata/server/session/SessionHolder.java +++ b/server/src/main/java/org/apache/seata/server/session/SessionHolder.java @@ -210,8 +210,8 @@ public static void reload(Collection allSessions, SessionMode sto throw new RuntimeException(e); } } - case StopCommitRetry: - case StopRollbackRetry: + case StopCommitOrCommitRetry: + case StopRollbackOrRollbackRetry: case Deleting: break; default: { diff --git a/server/src/main/java/org/apache/seata/server/storage/db/session/DataBaseSessionManager.java b/server/src/main/java/org/apache/seata/server/storage/db/session/DataBaseSessionManager.java index e8a015d2482..4042a561506 100644 --- a/server/src/main/java/org/apache/seata/server/storage/db/session/DataBaseSessionManager.java +++ b/server/src/main/java/org/apache/seata/server/storage/db/session/DataBaseSessionManager.java @@ -139,7 +139,7 @@ public Collection allSessions() { new SessionCondition(GlobalStatus.UnKnown, GlobalStatus.Begin, GlobalStatus.Committing, GlobalStatus.CommitRetrying, GlobalStatus.Rollbacking, GlobalStatus.RollbackRetrying, GlobalStatus.TimeoutRollbacking, GlobalStatus.TimeoutRollbackRetrying, GlobalStatus.AsyncCommitting, - GlobalStatus.StopRollbackRetry, GlobalStatus.StopCommitRetry, GlobalStatus.Deleting)); + GlobalStatus.StopRollbackOrRollbackRetry, GlobalStatus.StopCommitOrCommitRetry, GlobalStatus.Deleting)); } @Override diff --git a/server/src/main/java/org/apache/seata/server/storage/redis/session/RedisSessionManager.java b/server/src/main/java/org/apache/seata/server/storage/redis/session/RedisSessionManager.java index 977a4d72569..4b38752aa0b 100644 --- a/server/src/main/java/org/apache/seata/server/storage/redis/session/RedisSessionManager.java +++ b/server/src/main/java/org/apache/seata/server/storage/redis/session/RedisSessionManager.java @@ -132,7 +132,7 @@ public Collection allSessions() { new SessionCondition(GlobalStatus.UnKnown, GlobalStatus.Begin, GlobalStatus.Committing, GlobalStatus.CommitRetrying, GlobalStatus.Rollbacking, GlobalStatus.RollbackRetrying, GlobalStatus.TimeoutRollbacking, GlobalStatus.TimeoutRollbackRetrying, GlobalStatus.AsyncCommitting, - GlobalStatus.StopRollbackRetry, GlobalStatus.StopCommitRetry, GlobalStatus.Deleting)); + GlobalStatus.StopRollbackOrRollbackRetry, GlobalStatus.StopCommitOrCommitRetry, GlobalStatus.Deleting)); } @Override diff --git a/server/src/test/java/org/apache/seata/server/session/FileSessionManagerTest.java b/server/src/test/java/org/apache/seata/server/session/FileSessionManagerTest.java index cc97e731a6d..c9759eea0d4 100644 --- a/server/src/test/java/org/apache/seata/server/session/FileSessionManagerTest.java +++ b/server/src/test/java/org/apache/seata/server/session/FileSessionManagerTest.java @@ -46,7 +46,6 @@ import org.apache.seata.server.store.StoreConfig.SessionMode; import org.apache.seata.server.util.StoreUtil; import org.apache.commons.lang.time.DateUtils; -import org.apache.seata.server.session.SessionHolder; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.params.ParameterizedTest; @@ -446,12 +445,12 @@ public void stopGlobalSessionTest(List globalSessions) throws Exc String xid = globalSession.getXid(); globalSessionService.stopGlobalRetry(xid); Assertions.assertEquals(SessionHolder.findGlobalSession(xid).getStatus(), - GlobalStatus.StopCommitRetry); + GlobalStatus.StopCommitOrCommitRetry); globalSession.changeGlobalStatus(GlobalStatus.RollbackRetrying); globalSessionService.stopGlobalRetry(xid); Assertions.assertEquals(SessionHolder.findGlobalSession(xid).getStatus(), - GlobalStatus.StopRollbackRetry); + GlobalStatus.StopRollbackOrRollbackRetry); } finally { for (GlobalSession globalSession : globalSessions) { globalSession.setStatus(GlobalStatus.Committed); @@ -502,13 +501,13 @@ public void startGlobalSessionTest(List globalSessions) throws Ex globalSessionService.startGlobalRetry(globalSessions.get(0).getXid())); GlobalSession globalSession = globalSessions.get(1); - globalSession.setStatus(GlobalStatus.StopCommitRetry); + globalSession.setStatus(GlobalStatus.StopCommitOrCommitRetry); String xid = globalSession.getXid(); globalSessionService.startGlobalRetry(xid); Assertions.assertEquals(SessionHolder.findGlobalSession(xid).getStatus(), GlobalStatus.CommitRetrying); - globalSession.setStatus(GlobalStatus.StopRollbackRetry); + globalSession.setStatus(GlobalStatus.StopRollbackOrRollbackRetry); globalSessionService.startGlobalRetry(xid); Assertions.assertEquals(SessionHolder.findGlobalSession(xid).getStatus(), GlobalStatus.RollbackRetrying); From 9b64c079232580d49032191990fd22fb64511cc6 Mon Sep 17 00:00:00 2001 From: Jingliu Xiong <928124786@qq.com> Date: Wed, 18 Dec 2024 21:11:15 +0800 Subject: [PATCH 10/31] fix: fix unused import --- .../console/impl/raft/BranchSessionRaftServiceImpl.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/server/src/main/java/org/apache/seata/server/console/impl/raft/BranchSessionRaftServiceImpl.java b/server/src/main/java/org/apache/seata/server/console/impl/raft/BranchSessionRaftServiceImpl.java index 4200ef106a4..5adcaca207a 100644 --- a/server/src/main/java/org/apache/seata/server/console/impl/raft/BranchSessionRaftServiceImpl.java +++ b/server/src/main/java/org/apache/seata/server/console/impl/raft/BranchSessionRaftServiceImpl.java @@ -16,11 +16,7 @@ */ package org.apache.seata.server.console.impl.raft; -import org.apache.seata.common.result.PageResult; -import org.apache.seata.server.console.impl.AbstractBranchService; import org.apache.seata.server.console.impl.file.BranchSessionFileServiceImpl; -import org.apache.seata.server.console.service.BranchSessionService; -import org.apache.seata.server.console.vo.BranchSessionVO; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Component; From e74ca2a42cb9c8fa3697bff45556608ba30746b8 Mon Sep 17 00:00:00 2001 From: Jingliu Xiong <928124786@qq.com> Date: Fri, 27 Dec 2024 23:32:48 +0800 Subject: [PATCH 11/31] fix: add relate comment --- .../src/main/java/org/apache/seata/rm/RMHandlerAT.java | 1 + .../src/main/java/org/apache/seata/rm/RMHandlerXA.java | 1 + .../java/org/apache/seata/saga/rm/RMHandlerSagaAnnotation.java | 3 ++- .../src/main/java/org/apache/seata/saga/rm/RMHandlerSaga.java | 3 ++- .../org/apache/seata/server/console/impl/AbstractService.java | 1 + .../java/org/apache/seata/server/coordinator/AbstractCore.java | 2 +- .../apache/seata/server/coordinator/DefaultCoordinator.java | 3 +++ .../java/org/apache/seata/server/coordinator/DefaultCore.java | 1 + 8 files changed, 12 insertions(+), 3 deletions(-) diff --git a/rm-datasource/src/main/java/org/apache/seata/rm/RMHandlerAT.java b/rm-datasource/src/main/java/org/apache/seata/rm/RMHandlerAT.java index 1c1d562cf1d..08d231a9448 100644 --- a/rm-datasource/src/main/java/org/apache/seata/rm/RMHandlerAT.java +++ b/rm-datasource/src/main/java/org/apache/seata/rm/RMHandlerAT.java @@ -82,6 +82,7 @@ public BranchDeleteResponse handle(BranchDeleteRequest request) { BranchDeleteResponse branchDeleteResponse = new BranchDeleteResponse(); DataSourceManager dataSourceManager = (DataSourceManager) getResourceManager(); try { + // use commit to delete undo log dataSourceManager.branchCommit(BranchType.AT, request.getXid(), request.getBranchId(), request.getResourceId(), ""); branchDeleteResponse.setResultCode(ResultCode.Success); diff --git a/rm-datasource/src/main/java/org/apache/seata/rm/RMHandlerXA.java b/rm-datasource/src/main/java/org/apache/seata/rm/RMHandlerXA.java index c16eb2f8228..cae8f5bf128 100644 --- a/rm-datasource/src/main/java/org/apache/seata/rm/RMHandlerXA.java +++ b/rm-datasource/src/main/java/org/apache/seata/rm/RMHandlerXA.java @@ -44,6 +44,7 @@ public BranchDeleteResponse handle(BranchDeleteRequest request) { } BranchDeleteResponse branchDeleteResponse = new BranchDeleteResponse(); try { + // use rollback to make date correct in xa mode BranchStatus branchStatus = getResourceManager().branchRollback(request.getBranchType(), request.getXid(), request.getBranchId(), request.getResourceId(), ""); ResultCode code = branchStatus == BranchStatus.PhaseTwo_Rollbacked ? ResultCode.Success : ResultCode.Failed; diff --git a/saga/seata-saga-annotation/src/main/java/org/apache/seata/saga/rm/RMHandlerSagaAnnotation.java b/saga/seata-saga-annotation/src/main/java/org/apache/seata/saga/rm/RMHandlerSagaAnnotation.java index c785b201ff2..eeb0be6611f 100644 --- a/saga/seata-saga-annotation/src/main/java/org/apache/seata/saga/rm/RMHandlerSagaAnnotation.java +++ b/saga/seata-saga-annotation/src/main/java/org/apache/seata/saga/rm/RMHandlerSagaAnnotation.java @@ -16,6 +16,7 @@ */ package org.apache.seata.saga.rm; +import org.apache.seata.common.exception.ShouldNeverHappenException; import org.apache.seata.core.model.BranchType; import org.apache.seata.core.model.ResourceManager; import org.apache.seata.core.protocol.transaction.BranchDeleteRequest; @@ -40,6 +41,6 @@ public BranchType getBranchType() { @Override public BranchDeleteResponse handle(BranchDeleteRequest request) { - return null; + throw new ShouldNeverHappenException("saga mode rm handler not support BranchDeleteRequest"); } } diff --git a/saga/seata-saga-rm/src/main/java/org/apache/seata/saga/rm/RMHandlerSaga.java b/saga/seata-saga-rm/src/main/java/org/apache/seata/saga/rm/RMHandlerSaga.java index 5d4edb63f86..8e97fbd6e91 100644 --- a/saga/seata-saga-rm/src/main/java/org/apache/seata/saga/rm/RMHandlerSaga.java +++ b/saga/seata-saga-rm/src/main/java/org/apache/seata/saga/rm/RMHandlerSaga.java @@ -16,6 +16,7 @@ */ package org.apache.seata.saga.rm; +import org.apache.seata.common.exception.ShouldNeverHappenException; import org.apache.seata.core.model.BranchType; import org.apache.seata.core.model.ResourceManager; import org.apache.seata.core.protocol.transaction.BranchDeleteRequest; @@ -37,7 +38,7 @@ public void handle(UndoLogDeleteRequest request) { @Override public BranchDeleteResponse handle(BranchDeleteRequest request) { - return null; + throw new ShouldNeverHappenException("saga mode rm handler not support BranchDeleteRequest"); } /** diff --git a/server/src/main/java/org/apache/seata/server/console/impl/AbstractService.java b/server/src/main/java/org/apache/seata/server/console/impl/AbstractService.java index 158204cf05f..8480ee8b4f2 100644 --- a/server/src/main/java/org/apache/seata/server/console/impl/AbstractService.java +++ b/server/src/main/java/org/apache/seata/server/console/impl/AbstractService.java @@ -123,6 +123,7 @@ protected boolean doDeleteBranch(GlobalSession globalSession, BranchSession bran LOGGER.debug("Branch delete start, xid:{} branchId:{} branchType:{}", branchSession.getXid(), branchSession.getBranchId(), branchSession.getBranchType()); } + // local transaction failed, not need to do branch del for phase two if (branchSession.getStatus() == BranchStatus.PhaseOne_Failed) { globalSession.removeBranch(branchSession); return true; diff --git a/server/src/main/java/org/apache/seata/server/coordinator/AbstractCore.java b/server/src/main/java/org/apache/seata/server/coordinator/AbstractCore.java index 6330da74344..944082a1cac 100644 --- a/server/src/main/java/org/apache/seata/server/coordinator/AbstractCore.java +++ b/server/src/main/java/org/apache/seata/server/coordinator/AbstractCore.java @@ -267,7 +267,7 @@ public void doGlobalReport(GlobalSession globalSession, String xid, GlobalStatus @Override public Boolean doBranchDelete(GlobalSession globalSession, BranchSession branchSession) throws TransactionException { - return null; + return true; } @Override diff --git a/server/src/main/java/org/apache/seata/server/coordinator/DefaultCoordinator.java b/server/src/main/java/org/apache/seata/server/coordinator/DefaultCoordinator.java index e37b08fb544..cefbfcc10ea 100644 --- a/server/src/main/java/org/apache/seata/server/coordinator/DefaultCoordinator.java +++ b/server/src/main/java/org/apache/seata/server/coordinator/DefaultCoordinator.java @@ -481,6 +481,9 @@ protected void handleAsyncCommitting() { }); } + /** + * find session stop retry to retry again + */ protected void handleAutoRestart() { Collection allSessions = SessionHolder.getRootSessionManager().allSessions(); if (CollectionUtils.isEmpty(allSessions)) { diff --git a/server/src/main/java/org/apache/seata/server/coordinator/DefaultCore.java b/server/src/main/java/org/apache/seata/server/coordinator/DefaultCore.java index e987665dbce..f2947cd3354 100644 --- a/server/src/main/java/org/apache/seata/server/coordinator/DefaultCore.java +++ b/server/src/main/java/org/apache/seata/server/coordinator/DefaultCore.java @@ -147,6 +147,7 @@ public Boolean doBranchDelete(GlobalSession globalSession, BranchSession branchS LOGGER.info("Delete branch XAER_NOTA retry timeout, xid = {} branchId = {}", globalSession.getXid(), branchSession.getBranchId()); return true; } + // branch transaction can not roll back, stop retry and delete if (response.getBranchStatus() == BranchStatus.PhaseTwo_RollbackFailed_Unretryable) { LOGGER.error("Delete branch transaction fail and stop retry, xid = {} branchId = {}", globalSession.getXid(), branchSession.getBranchId()); return true; From 77a707588ba3da083bdb10c58a643bb1d2d90256 Mon Sep 17 00:00:00 2001 From: Jingliu Xiong <928124786@qq.com> Date: Sat, 28 Dec 2024 18:25:34 +0800 Subject: [PATCH 12/31] fix: FileSessionManagerTest conflict solve --- .../server/session/FileSessionManagerTest.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/server/src/test/java/org/apache/seata/server/session/FileSessionManagerTest.java b/server/src/test/java/org/apache/seata/server/session/FileSessionManagerTest.java index 1d327d3a3c6..dc0d742a3e3 100644 --- a/server/src/test/java/org/apache/seata/server/session/FileSessionManagerTest.java +++ b/server/src/test/java/org/apache/seata/server/session/FileSessionManagerTest.java @@ -44,7 +44,6 @@ import org.apache.seata.server.console.vo.GlobalSessionVO; import org.apache.seata.server.storage.file.session.FileSessionManager; import org.apache.seata.server.store.StoreConfig; -import org.apache.seata.server.store.StoreConfig.SessionMode; import org.apache.seata.server.util.StoreUtil; import org.apache.commons.lang.time.DateUtils; import org.junit.jupiter.api.Assertions; @@ -297,7 +296,7 @@ public void findGlobalSessionsWithPageResultTest(List globalSessi SessionHolder.init(SessionMode.FILE); try { - SessionHolder.init(StoreConfig.SessionMode.FILE); + SessionHolder.init(SessionMode.FILE); final SessionManager sessionManager = SessionHolder.getRootSessionManager(); // make sure sessionMaanager is empty Collection sessions = sessionManager.allSessions(); @@ -434,7 +433,7 @@ public void onStatusChangeTest(GlobalSession globalSession) throws Exception { @MethodSource("globalSessionForLockTestProvider") public void stopGlobalSessionTest(List globalSessions) throws Exception { try { - SessionHolder.init(StoreConfig.SessionMode.FILE); + SessionHolder.init(SessionMode.FILE); for (GlobalSession globalSession : globalSessions) { globalSession.begin(); } @@ -464,7 +463,7 @@ public void stopGlobalSessionTest(List globalSessions) throws Exc @MethodSource("globalSessionForLockTestProvider") public void changeGlobalSessionTest(List globalSessions) throws Exception { try { - SessionHolder.init(StoreConfig.SessionMode.FILE); + SessionHolder.init(SessionMode.FILE); for (GlobalSession globalSession : globalSessions) { globalSession.begin(); } @@ -494,7 +493,7 @@ public void changeGlobalSessionTest(List globalSessions) throws E @MethodSource("globalSessionForLockTestProvider") public void startGlobalSessionTest(List globalSessions) throws Exception { try { - SessionHolder.init(StoreConfig.SessionMode.FILE); + SessionHolder.init(SessionMode.FILE); for (GlobalSession globalSession : globalSessions) { globalSession.begin(); } @@ -593,7 +592,7 @@ public void onCloseTest(GlobalSession globalSession) throws Exception { @MethodSource("branchSessionsProvider") public void stopBranchRetryTest(GlobalSession globalSession) throws Exception { try { - SessionHolder.init(StoreConfig.SessionMode.FILE); + SessionHolder.init(SessionMode.FILE); globalSession.begin(); // wrong param for xid and branchId Assertions.assertThrows(IllegalArgumentException.class, @@ -628,7 +627,7 @@ public void stopBranchRetryTest(GlobalSession globalSession) throws Exception { @MethodSource("branchSessionsProvider") public void restartBranchFailRetryTest(GlobalSession globalSession) throws Exception { try { - SessionHolder.init(StoreConfig.SessionMode.FILE); + SessionHolder.init(SessionMode.FILE); globalSession.begin(); List branchSessions = globalSession.getBranchSessions(); // wrong status for branch transaction From ac3d722e307df357910d4591661e63a8eaf19441 Mon Sep 17 00:00:00 2001 From: Jingliu Xiong <928124786@qq.com> Date: Mon, 30 Dec 2024 21:55:46 +0800 Subject: [PATCH 13/31] feat: del stop retry task --- .../seata/common/ConfigurationKeys.java | 10 --- .../org/apache/seata/common/Constants.java | 5 -- .../coordinator/DefaultCoordinator.java | 62 ------------------- 3 files changed, 77 deletions(-) diff --git a/common/src/main/java/org/apache/seata/common/ConfigurationKeys.java b/common/src/main/java/org/apache/seata/common/ConfigurationKeys.java index c3d0f4b4441..840e0298004 100644 --- a/common/src/main/java/org/apache/seata/common/ConfigurationKeys.java +++ b/common/src/main/java/org/apache/seata/common/ConfigurationKeys.java @@ -299,16 +299,6 @@ public interface ConfigurationKeys { */ String DISTRIBUTED_LOCK_DB_TABLE = STORE_DB_PREFIX + "distributedLockTable"; - /** - * the constant AUTO_RESTART_TIME - */ - String AUTO_RESTART_TIME = SERVER_PREFIX + "autoRestartTime"; - - /** - * the constant AUTO_RESTART_PERIOD - */ - String AUTO_RESTART_PERIOD = SERVER_PREFIX + "autoRestartPeriod"; - /** * The constant STORE_DB_DATASOURCE_TYPE. */ diff --git a/common/src/main/java/org/apache/seata/common/Constants.java b/common/src/main/java/org/apache/seata/common/Constants.java index cb2a6bc4fb6..43da1827e05 100644 --- a/common/src/main/java/org/apache/seata/common/Constants.java +++ b/common/src/main/java/org/apache/seata/common/Constants.java @@ -185,11 +185,6 @@ public interface Constants { */ String AUTO_COMMIT = "autoCommit"; - /** - * The constant AUTO_RESTART_SESSION - */ - String AUTO_RESTART_SESSION = "autoRestartSession"; - /** * The constant SKIP_CHECK_LOCK */ diff --git a/server/src/main/java/org/apache/seata/server/coordinator/DefaultCoordinator.java b/server/src/main/java/org/apache/seata/server/coordinator/DefaultCoordinator.java index b54c7f3ef4d..2d9e34e2a16 100644 --- a/server/src/main/java/org/apache/seata/server/coordinator/DefaultCoordinator.java +++ b/server/src/main/java/org/apache/seata/server/coordinator/DefaultCoordinator.java @@ -79,7 +79,6 @@ import org.slf4j.MDC; import static org.apache.seata.common.Constants.ASYNC_COMMITTING; -import static org.apache.seata.common.Constants.AUTO_RESTART_SESSION; import static org.apache.seata.common.Constants.COMMITTING; import static org.apache.seata.common.Constants.RETRY_COMMITTING; import static org.apache.seata.common.Constants.RETRY_ROLLBACKING; @@ -138,17 +137,6 @@ public class DefaultCoordinator extends AbstractTCInboundHandler implements Tran protected static final long UNDO_LOG_DELETE_PERIOD = CONFIG.getLong( ConfigurationKeys.TRANSACTION_UNDO_LOG_DELETE_PERIOD, DEFAULT_UNDO_LOG_DELETE_PERIOD); - /** - * The constant AUTO_RESTART_TIME - */ - protected static final long AUTO_RETRY_TIME = CONFIG.getLong(ConfigurationKeys.AUTO_RESTART_TIME, - DefaultValues.DEFAULT_AUTO_RESTART_TIME); - - /** - * The constant AUTO_RESTART_PERIOD - */ - protected static final long AUTO_RETRY_PERIOD = CONFIG.getLong(ConfigurationKeys.AUTO_RESTART_PERIOD, - DefaultValues.DEFAULT_AUTO_RESTART_PERIOD); /** * The Transaction undo log delay delete period */ @@ -199,9 +187,6 @@ public class DefaultCoordinator extends AbstractTCInboundHandler implements Tran private final ScheduledThreadPoolExecutor syncProcessing = new ScheduledThreadPoolExecutor(1, new NamedThreadFactory(SYNC_PROCESSING, 1)); - private final ScheduledThreadPoolExecutor autoRestartSession = - new ScheduledThreadPoolExecutor(1, new NamedThreadFactory(AUTO_RESTART_SESSION, 1)); - private final GlobalStatus[] retryRollbackingStatuses = new GlobalStatus[] { GlobalStatus.TimeoutRollbacking, GlobalStatus.TimeoutRollbackRetrying, GlobalStatus.RollbackRetrying}; @@ -482,49 +467,6 @@ protected void handleAsyncCommitting() { }); } - /** - * find session stop retry to retry again - */ - protected void handleAutoRestart() { - Collection allSessions = SessionHolder.getRootSessionManager().allSessions(); - if (CollectionUtils.isEmpty(allSessions)) { - return; - } - SessionHelper.forEach(allSessions, globalSession -> { - String xid = globalSession.getXid(); - List branchSessions = globalSession.getBranchSessions(); - branchSessions.forEach(branchSession -> { - if (BranchStatus.STOP_RETRY.equals(branchSession.getStatus()) - && System.currentTimeMillis() - branchSession.getGmtModified() >= AUTO_RETRY_TIME) { - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Auto restart the branch session to retry,xid: {}, branchId: {}", xid, branchSession.getBranchId()); - } - BranchStatus newStatus = BranchStatus.Registered; - branchSession.setStatus(newStatus); - try { - globalSession.changeBranchStatus(branchSession, newStatus); - } catch (Exception e) { - LOGGER.error("Change branch session status fail, xid: {}, branchId:{}", - globalSession.getXid(), branchSession.getBranchId(), e); - } - } - }); - GlobalStatus globalStatus = globalSession.getStatus(); - GlobalStatus newStatus = GlobalStatus.StopCommitOrCommitRetry.equals(globalStatus) ? GlobalStatus.CommitRetrying : - GlobalStatus.StopRollbackOrRollbackRetry.equals(globalStatus) ? GlobalStatus.RollbackRetrying : null; - try { - if (Objects.nonNull(newStatus) && System.currentTimeMillis() - globalSession.getGmtModified() >= AUTO_RETRY_TIME) { - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Auto restart the global session to retry,xid: {}", globalSession.getXid()); - } - globalSession.changeGlobalStatus(newStatus); - } - } catch (Exception e) { - LOGGER.error("Auto restart global session retry fail, xid:{}", globalSession.getXid(), e); - } - }); - } - /** * Undo log delete. */ @@ -689,8 +631,6 @@ public void init() { () -> SessionHolder.distributedLockAndExecute(UNDOLOG_DELETE, this::undoLogDelete), UNDO_LOG_DELAY_DELETE_PERIOD, UNDO_LOG_DELETE_PERIOD, TimeUnit.MILLISECONDS); - autoRestartSession.scheduleAtFixedRate(this::handleAutoRestart, 0, AUTO_RETRY_PERIOD, TimeUnit.MILLISECONDS); - rollbackingSchedule(0); committingSchedule(0); @@ -723,7 +663,6 @@ public void destroy() { asyncCommitting.shutdown(); timeoutCheck.shutdown(); undoLogDelete.shutdown(); - autoRestartSession.shutdown(); if (branchRemoveExecutor != null) { branchRemoveExecutor.shutdown(); } @@ -733,7 +672,6 @@ public void destroy() { asyncCommitting.awaitTermination(TIMED_TASK_SHUTDOWN_MAX_WAIT_MILLS, TimeUnit.MILLISECONDS); timeoutCheck.awaitTermination(TIMED_TASK_SHUTDOWN_MAX_WAIT_MILLS, TimeUnit.MILLISECONDS); undoLogDelete.awaitTermination(TIMED_TASK_SHUTDOWN_MAX_WAIT_MILLS, TimeUnit.MILLISECONDS); - autoRestartSession.awaitTermination(TIMED_TASK_SHUTDOWN_MAX_WAIT_MILLS, TimeUnit.MILLISECONDS); if (branchRemoveExecutor != null) { branchRemoveExecutor.awaitTermination(TIMED_TASK_SHUTDOWN_MAX_WAIT_MILLS, TimeUnit.MILLISECONDS); } From ec68aa609b68c213316a081b60a5c213222f92fc Mon Sep 17 00:00:00 2001 From: Jingliu Xiong <928124786@qq.com> Date: Mon, 30 Dec 2024 22:06:28 +0800 Subject: [PATCH 14/31] fix: del unused import --- .../org/apache/seata/server/coordinator/DefaultCoordinator.java | 1 - 1 file changed, 1 deletion(-) diff --git a/server/src/main/java/org/apache/seata/server/coordinator/DefaultCoordinator.java b/server/src/main/java/org/apache/seata/server/coordinator/DefaultCoordinator.java index 2d9e34e2a16..559ab487a98 100644 --- a/server/src/main/java/org/apache/seata/server/coordinator/DefaultCoordinator.java +++ b/server/src/main/java/org/apache/seata/server/coordinator/DefaultCoordinator.java @@ -37,7 +37,6 @@ import org.apache.seata.core.constants.ConfigurationKeys; import org.apache.seata.core.context.RootContext; import org.apache.seata.core.exception.TransactionException; -import org.apache.seata.core.model.BranchStatus; import org.apache.seata.core.model.GlobalStatus; import org.apache.seata.core.protocol.AbstractMessage; import org.apache.seata.core.protocol.AbstractResultMessage; From 2b905ef7a8136c0cb056e013ed556e8663400d79 Mon Sep 17 00:00:00 2001 From: Jingliu Xiong <928124786@qq.com> Date: Mon, 30 Dec 2024 22:41:59 +0800 Subject: [PATCH 15/31] fix: del gmt modify time in session --- .../apache/seata/server/session/BranchSession.java | 13 ------------- .../apache/seata/server/session/GlobalSession.java | 8 -------- .../seata/server/storage/SessionConverter.java | 6 ------ .../storage/file/session/FileSessionManager.java | 1 - .../server/coordinator/DefaultCoordinatorTest.java | 6 +----- 5 files changed, 1 insertion(+), 33 deletions(-) diff --git a/server/src/main/java/org/apache/seata/server/session/BranchSession.java b/server/src/main/java/org/apache/seata/server/session/BranchSession.java index 9bd19d481ee..137cabc26f1 100644 --- a/server/src/main/java/org/apache/seata/server/session/BranchSession.java +++ b/server/src/main/java/org/apache/seata/server/session/BranchSession.java @@ -74,8 +74,6 @@ public class BranchSession implements Lockable, Comparable, Sessi private LockStatus lockStatus = Locked; - private long gmtModified; - private final Map> lockHolder; private final LockManager lockManager = LockerManagerFactory.getLockManager(); @@ -89,14 +87,6 @@ public BranchSession(BranchType branchType) { this.lockHolder = branchType == BranchType.AT ? new ConcurrentHashMap<>(8) : Collections.emptyMap(); } - public long getGmtModified() { - return gmtModified; - } - - public void setGmtModified(long gmtModified) { - this.gmtModified = gmtModified; - } - /** * Gets application data. * @@ -417,8 +407,6 @@ public byte[] encode() { byteBuffer.put((byte)status.getCode()); byteBuffer.put((byte)lockStatus.getCode()); - gmtModified = System.currentTimeMillis(); - byteBuffer.putLong(gmtModified); BufferUtils.flip(byteBuffer); byte[] result = new byte[byteBuffer.limit()]; byteBuffer.get(result); @@ -494,7 +482,6 @@ public void decode(byte[] a) { this.branchType = BranchType.values()[branchTypeId]; } this.status = BranchStatus.get(byteBuffer.get()); - this.gmtModified = byteBuffer.getLong(); } } diff --git a/server/src/main/java/org/apache/seata/server/session/GlobalSession.java b/server/src/main/java/org/apache/seata/server/session/GlobalSession.java index baf2c23bebb..3d8003d05b8 100644 --- a/server/src/main/java/org/apache/seata/server/session/GlobalSession.java +++ b/server/src/main/java/org/apache/seata/server/session/GlobalSession.java @@ -606,14 +606,6 @@ public void setActive(boolean active) { this.active = active; } - public long getGmtModified() { - return gmtModified; - } - - public void setGmtModified(long gmtModified) { - this.gmtModified = gmtModified; - } - @Override public byte[] encode() { byte[] byApplicationIdBytes = applicationId != null ? applicationId.getBytes() : null; diff --git a/server/src/main/java/org/apache/seata/server/storage/SessionConverter.java b/server/src/main/java/org/apache/seata/server/storage/SessionConverter.java index 70ebf933b60..4b90905a851 100644 --- a/server/src/main/java/org/apache/seata/server/storage/SessionConverter.java +++ b/server/src/main/java/org/apache/seata/server/storage/SessionConverter.java @@ -55,9 +55,6 @@ public static GlobalSession convertGlobalSession(GlobalTransactionDO globalTrans session.setStatus(GlobalStatus.get(globalTransactionDO.getStatus())); session.setApplicationData(globalTransactionDO.getApplicationData()); session.setBeginTime(globalTransactionDO.getBeginTime()); - if (globalTransactionDO.getGmtModified() != null) { - session.setGmtModified(globalTransactionDO.getGmtModified().getTime()); - } return session; } @@ -79,9 +76,6 @@ public static BranchSession convertBranchSession(BranchTransactionDO branchTrans branchSession.setClientId(branchTransactionDO.getClientId()); branchSession.setResourceGroupId(branchTransactionDO.getResourceGroupId()); branchSession.setStatus(BranchStatus.get(branchTransactionDO.getStatus())); - if (branchTransactionDO.getGmtModified() != null) { - branchSession.setGmtModified(branchTransactionDO.getGmtModified().getTime()); - } if (branchTransactionDO instanceof BranchTransactionDTO) { branchSession.setLockKey(((BranchTransactionDTO)branchTransactionDO).getLockKey()); } diff --git a/server/src/main/java/org/apache/seata/server/storage/file/session/FileSessionManager.java b/server/src/main/java/org/apache/seata/server/storage/file/session/FileSessionManager.java index 3186d18ce20..c58f046434e 100644 --- a/server/src/main/java/org/apache/seata/server/storage/file/session/FileSessionManager.java +++ b/server/src/main/java/org/apache/seata/server/storage/file/session/FileSessionManager.java @@ -285,7 +285,6 @@ private void restore(List stores, Set removedGlob } else { if (this.checkSessionStatus(globalSession)) { foundGlobalSession.setStatus(globalSession.getStatus()); - foundGlobalSession.setGmtModified(globalSession.getGmtModified()); } else { sessionMap.remove(globalSession.getXid()); removedGlobalBuffer.add(globalSession.getXid()); diff --git a/server/src/test/java/org/apache/seata/server/coordinator/DefaultCoordinatorTest.java b/server/src/test/java/org/apache/seata/server/coordinator/DefaultCoordinatorTest.java index 7f77a65f5e0..ba59be46a2b 100644 --- a/server/src/test/java/org/apache/seata/server/coordinator/DefaultCoordinatorTest.java +++ b/server/src/test/java/org/apache/seata/server/coordinator/DefaultCoordinatorTest.java @@ -169,13 +169,11 @@ public void test_handleRetryRollbacking() throws TransactionException, Interrupt Long branchId = core.branchRegister(BranchType.AT, "abcd", clientId, xid, applicationData, lockKeys_2); Assertions.assertNotNull(branchId); - GlobalSession globalSession = SessionHolder.findGlobalSession(xid); Thread.sleep(100); defaultCoordinator.timeoutCheck(); - TimeUnit.MILLISECONDS.sleep(globalSession.getGmtModified() - globalSession.getBeginTime()); defaultCoordinator.handleRetryRollbacking(); - globalSession = SessionHolder.findGlobalSession(xid); + GlobalSession globalSession = SessionHolder.findGlobalSession(xid); Assertions.assertNull(globalSession); } @@ -194,7 +192,6 @@ public void test_handleRetryRollbackingTimeOut() throws TransactionException, In ReflectionUtil.modifyStaticFinalField(defaultCoordinator.getClass(), "MAX_ROLLBACK_RETRY_TIMEOUT", 10L); ReflectionUtil.modifyStaticFinalField(defaultCoordinator.getClass(), "ROLLBACK_RETRY_TIMEOUT_UNLOCK_ENABLE", false); TimeUnit.MILLISECONDS.sleep(100); - TimeUnit.MILLISECONDS.sleep(globalSession.getGmtModified() - globalSession.getBeginTime()); globalSession.queueToRetryRollback(); defaultCoordinator.handleRetryRollbacking(); int lockSize = globalSession.getBranchSessions().get(0).getLockHolder().size(); @@ -224,7 +221,6 @@ public void test_handleRetryRollbackingTimeOut_unlock() throws TransactionExcept TimeUnit.MILLISECONDS.sleep(100); globalSession.queueToRetryRollback(); - TimeUnit.MILLISECONDS.sleep(globalSession.getGmtModified() - globalSession.getBeginTime()); defaultCoordinator.handleRetryRollbacking(); int lockSize = globalSession.getBranchSessions().get(0).getLockHolder().size(); From b19306589968bfdb810382cffc0a402cda91d464 Mon Sep 17 00:00:00 2001 From: Jingliu Xiong <928124786@qq.com> Date: Mon, 30 Dec 2024 22:59:46 +0800 Subject: [PATCH 16/31] fix: del gmt modify time in session --- .../store/file/FileTransactionStoreManagerTest.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/server/src/test/java/org/apache/seata/server/store/file/FileTransactionStoreManagerTest.java b/server/src/test/java/org/apache/seata/server/store/file/FileTransactionStoreManagerTest.java index b1478299f17..dca761e1238 100644 --- a/server/src/test/java/org/apache/seata/server/store/file/FileTransactionStoreManagerTest.java +++ b/server/src/test/java/org/apache/seata/server/store/file/FileTransactionStoreManagerTest.java @@ -67,18 +67,15 @@ public void testBigDataWrite() throws Exception { fileTransactionStoreManager = new FileTransactionStoreManager(seataFile.getAbsolutePath(), null); BranchSession branchSessionA = Mockito.mock(BranchSession.class); GlobalSession global = new GlobalSession(); - global.setGmtModified(System.currentTimeMillis()); Mockito.when(branchSessionA.encode()) .thenReturn(createBigBranchSessionData(global, (byte) 'A')); Mockito.when(branchSessionA.getApplicationData()) .thenReturn(new String(createBigApplicationData((byte) 'A'))); - Mockito.when(branchSessionA.getGmtModified()).thenReturn(global.getGmtModified()); BranchSession branchSessionB = Mockito.mock(BranchSession.class); Mockito.when(branchSessionB.encode()) .thenReturn(createBigBranchSessionData(global, (byte) 'B')); Mockito.when(branchSessionB.getApplicationData()) .thenReturn(new String(createBigApplicationData((byte) 'B'))); - Mockito.when(branchSessionB.getGmtModified()).thenReturn(global.getGmtModified()); Assertions.assertTrue(fileTransactionStoreManager.writeSession(TransactionStoreManager.LogOperation.BRANCH_ADD, branchSessionA)); Assertions.assertTrue(fileTransactionStoreManager.writeSession(TransactionStoreManager.LogOperation.BRANCH_ADD, branchSessionB)); List list = fileTransactionStoreManager.readWriteStore(2000, false); @@ -86,10 +83,8 @@ public void testBigDataWrite() throws Exception { Assertions.assertEquals(2, list.size()); BranchSession loadedBranchSessionA = (BranchSession) list.get(0).getSessionRequest(); Assertions.assertEquals(branchSessionA.getApplicationData(), loadedBranchSessionA.getApplicationData()); - Assertions.assertEquals(branchSessionA.getGmtModified(), loadedBranchSessionA.getGmtModified()); BranchSession loadedBranchSessionB = (BranchSession) list.get(1).getSessionRequest(); Assertions.assertEquals(branchSessionB.getApplicationData(), loadedBranchSessionB.getApplicationData()); - Assertions.assertEquals(branchSessionB.getGmtModified(), loadedBranchSessionB.getGmtModified()); } finally { if (fileTransactionStoreManager != null) { fileTransactionStoreManager.shutdown(); @@ -109,7 +104,6 @@ public void testFindTimeoutAndSave() throws Exception { List timeoutSessions = new ArrayList<>(); for (int i = 0; i < 100; i++) { GlobalSession globalSession = new GlobalSession("", "", "", 60000); - globalSession.setGmtModified(System.currentTimeMillis()); BranchSession branchSessionA = Mockito.mock(BranchSession.class); Mockito.when(branchSessionA.encode()) .thenReturn(createBigBranchSessionData(globalSession, (byte) 'A')); @@ -165,7 +159,6 @@ private byte[] createBigBranchSessionData(GlobalSession global, byte c) { + 4 // xidBytes.size + 1 // statusCode + 1 // lockstatus - + 8 // gmtModified + 1; //branchType String xid = global.getXid(); byte[] xidBytes = null; @@ -190,7 +183,6 @@ private byte[] createBigBranchSessionData(GlobalSession global, byte c) { } byteBuffer.put((byte) 0); byteBuffer.put((byte) 0); - byteBuffer.putLong(global.getGmtModified()); byteBuffer.put((byte) 0); BufferUtils.flip(byteBuffer); byte[] bytes = new byte[byteBuffer.limit()]; From 74d1f6753cd4d8e98f4ea26aaca725c0f3185942 Mon Sep 17 00:00:00 2001 From: Jingliu Xiong <928124786@qq.com> Date: Mon, 30 Dec 2024 23:10:19 +0800 Subject: [PATCH 17/31] fix: del gmt modify time in session --- .../seata/server/raft/execute/BranchSessionExecuteTest.java | 2 -- .../seata/server/raft/execute/GlobalSessionExecuteTest.java | 1 - 2 files changed, 3 deletions(-) diff --git a/server/src/test/java/org/apache/seata/server/raft/execute/BranchSessionExecuteTest.java b/server/src/test/java/org/apache/seata/server/raft/execute/BranchSessionExecuteTest.java index 3c4b9c84b8e..df21b9216ff 100644 --- a/server/src/test/java/org/apache/seata/server/raft/execute/BranchSessionExecuteTest.java +++ b/server/src/test/java/org/apache/seata/server/raft/execute/BranchSessionExecuteTest.java @@ -132,7 +132,6 @@ private static GlobalSession mockGlobalSession() { session.setApplicationData("hello, world"); session.setTransactionId(txId); session.setBeginTime(System.currentTimeMillis()); - session.setGmtModified(System.currentTimeMillis()); return session; } @@ -147,7 +146,6 @@ private static BranchSession mockBranchSession(String xid,long transactionId) { session.setLockKey("test"); session.setBranchType(BranchType.AT); session.setApplicationData("hello, world"); - session.setGmtModified(System.currentTimeMillis()); return session; } diff --git a/server/src/test/java/org/apache/seata/server/raft/execute/GlobalSessionExecuteTest.java b/server/src/test/java/org/apache/seata/server/raft/execute/GlobalSessionExecuteTest.java index fc4a3940da4..45d191790ee 100644 --- a/server/src/test/java/org/apache/seata/server/raft/execute/GlobalSessionExecuteTest.java +++ b/server/src/test/java/org/apache/seata/server/raft/execute/GlobalSessionExecuteTest.java @@ -128,7 +128,6 @@ private static GlobalSession mockGlobalSession() { session.setApplicationData("hello, world"); session.setTransactionId(123); session.setBeginTime(System.currentTimeMillis()); - session.setGmtModified(System.currentTimeMillis()); return session; } From ceed9ba49a1d2de9d44a362f10024ded4b55744d Mon Sep 17 00:00:00 2001 From: Jingliu Xiong <928124786@qq.com> Date: Wed, 1 Jan 2025 21:45:18 +0800 Subject: [PATCH 18/31] fix: fix global lock file service and redis service --- .../impl/file/GlobalLockFileServiceImpl.java | 29 ++++++++++++- .../redis/GlobalLockRedisServiceImpl.java | 42 +++++-------------- 2 files changed, 39 insertions(+), 32 deletions(-) diff --git a/server/src/main/java/org/apache/seata/server/console/impl/file/GlobalLockFileServiceImpl.java b/server/src/main/java/org/apache/seata/server/console/impl/file/GlobalLockFileServiceImpl.java index 3480b4c185b..2c745ee78ac 100644 --- a/server/src/main/java/org/apache/seata/server/console/impl/file/GlobalLockFileServiceImpl.java +++ b/server/src/main/java/org/apache/seata/server/console/impl/file/GlobalLockFileServiceImpl.java @@ -26,7 +26,10 @@ import org.apache.seata.common.result.SingleResult; import org.apache.seata.common.util.CollectionUtils; import org.apache.seata.common.util.StringUtils; +import org.apache.seata.core.exception.TransactionException; +import org.apache.seata.server.console.exception.ConsoleException; import org.apache.seata.server.console.impl.AbstractLockService; +import org.apache.seata.server.console.impl.redis.GlobalLockRedisServiceImpl; import org.apache.seata.server.console.param.GlobalLockParam; import org.apache.seata.common.result.PageResult; import org.apache.seata.server.console.vo.GlobalLockVO; @@ -37,6 +40,8 @@ import org.apache.seata.server.session.GlobalSession; import org.apache.seata.server.session.SessionHolder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Component; @@ -52,6 +57,7 @@ @org.springframework.context.annotation.Configuration @ConditionalOnExpression("#{'file'.equals('${lockMode}')}") public class GlobalLockFileServiceImpl extends AbstractLockService implements GlobalLockService { + private static final Logger LOGGER = LoggerFactory.getLogger(GlobalLockRedisServiceImpl.class); @Override public PageResult query(GlobalLockParam param) { @@ -75,7 +81,28 @@ public PageResult query(GlobalLockParam param) { @Override public SingleResult deleteLock(GlobalLockParam param) { - throw new IllegalStateException("Not Support to delete lock in file mode"); + checkDeleteLock(param); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("start to delete global lock,xid:{} branchId:{}", + param.getXid(), param.getBranchId()); + } + + List branchSessions = SessionHolder.getRootSessionManager().findGlobalSession(param.getXid(), + true).getBranchSessions().stream().filter(branchSession -> + branchSession.getBranchId() == Long.parseLong(param.getBranchId())).collect(Collectors.toList()); + + if (branchSessions.size() != 1) { + throw new ConsoleException(new UnsupportedOperationException("branch session size is not one"), + String.format("delete global lock," + + "xid:%s ,branchId:%s", param.getXid(), param.getBranchId())); + } + try { + lockManager.releaseLock(branchSessions.get(0)); + } catch (TransactionException e) { + throw new ConsoleException(e, String.format("delete global lock," + + "xid:%s ,branchId:%s", param.getXid(), param.getBranchId())); + } + return SingleResult.success(); } /** diff --git a/server/src/main/java/org/apache/seata/server/console/impl/redis/GlobalLockRedisServiceImpl.java b/server/src/main/java/org/apache/seata/server/console/impl/redis/GlobalLockRedisServiceImpl.java index 5be160d03f6..e6a46bde47a 100644 --- a/server/src/main/java/org/apache/seata/server/console/impl/redis/GlobalLockRedisServiceImpl.java +++ b/server/src/main/java/org/apache/seata/server/console/impl/redis/GlobalLockRedisServiceImpl.java @@ -20,12 +20,13 @@ import java.util.Arrays; import java.util.List; import java.util.Map; -import java.util.StringJoiner; import org.apache.seata.common.result.SingleResult; import org.apache.seata.common.util.CollectionUtils; -import org.apache.seata.common.util.StringUtils; +import org.apache.seata.core.exception.TransactionException; +import org.apache.seata.server.console.exception.ConsoleException; import org.apache.seata.server.console.impl.AbstractLockService; +import org.apache.seata.server.session.BranchSession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; @@ -85,35 +86,14 @@ public SingleResult deleteLock(GlobalLockParam param) { LOGGER.debug("start to delete global lock,xid:{} branchId:{} row key:{} ", param.getXid(), param.getBranchId(), rowKey); } - try (Jedis jedis = JedisPooledFactory.getJedisInstance()) { - // del the row key - jedis.del(rowKey); - String xidLockKey = buildXidLockKey(param.getXid()); - String rowKeys = jedis.hget(xidLockKey, param.getBranchId()); - if (StringUtils.isNotBlank(rowKeys)) { - // Check whether other locks exist. If so, update it. If not, delete it - if (rowKeys.contains(ROW_LOCK_KEY_SPLIT_CHAR)) { - String[] rowKeyArray = rowKeys.split(ROW_LOCK_KEY_SPLIT_CHAR); - StringJoiner lockKeysString = new StringJoiner(ROW_LOCK_KEY_SPLIT_CHAR); - for (String rk : rowKeyArray) { - if (rk.equalsIgnoreCase(rowKey)) { - continue; - } - lockKeysString.add(rk); - } - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("update global lock key from {} to :{} ",rowKeys, lockKeysString); - } - // update the new lock key - jedis.hset(xidLockKey, param.getBranchId(), lockKeysString.toString()); - } else { - // no other branch session - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("start to delete global lock key:{} ", xidLockKey); - } - jedis.del(xidLockKey); - } - } + BranchSession branchSession = new BranchSession(); + branchSession.setXid(param.getXid()); + branchSession.setBranchId(Long.parseLong(param.getBranchId())); + try { + lockManager.releaseLock(branchSession); + } catch (TransactionException e) { + throw new ConsoleException(e, String.format("delete global lock," + + "xid:%s ,branchId:%s ,row key:%s failed", param.getXid(), param.getBranchId(), rowKey)); } return SingleResult.success(); } From 3a47a2b87082192e21b2043f8a5e5091971b24db Mon Sep 17 00:00:00 2001 From: Jingliu Xiong <928124786@qq.com> Date: Fri, 3 Jan 2025 11:13:19 +0800 Subject: [PATCH 19/31] fix: fix button display --- .../pages/TransactionInfo/TransactionInfo.tsx | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/console/src/main/resources/static/console-fe/src/pages/TransactionInfo/TransactionInfo.tsx b/console/src/main/resources/static/console-fe/src/pages/TransactionInfo/TransactionInfo.tsx index 090a4cc6af5..36c8eeafb47 100644 --- a/console/src/main/resources/static/console-fe/src/pages/TransactionInfo/TransactionInfo.tsx +++ b/console/src/main/resources/static/console-fe/src/pages/TransactionInfo/TransactionInfo.tsx @@ -408,9 +408,12 @@ class TransactionInfo extends React.Component sendGlobalSessionTitle, changeGlobalSessionTitle, } = locale; - let width = getCurrentLanguage() === enUsKey ? '450px' : '350px' + let width = getCurrentLanguage() === enUsKey ? '450px' : '420px' + let height = this.state.globalSessionParam.withBranch ? '120px' : '80px'; return ( - + {/* {when withBranch false, hide 'View branch session' button} */} {this.state.globalSessionParam.withBranch ? ( > {startGlobalSessionTitle} - ) : + ) } + + {record.status == 19 || record.status == 20 ? ( + + {record.status == 13 ? ( - {record.status == 13 ? ( + {record.status == 14 ? (