From 71f5762acc1f02baf65eeef42a4fc7efd5879e35 Mon Sep 17 00:00:00 2001 From: Long Chen Date: Mon, 19 Jun 2023 14:10:02 +0800 Subject: [PATCH 01/11] add http xa implementation --- src/DtmCommon/Constant.cs | 1 + src/DtmCommon/Imp/DbSpecialDelegate.cs | 20 +++-- src/Dtmcli/DtmClient.cs | 12 +-- src/Dtmcli/ServiceCollectionExtensions.cs | 2 + src/Dtmcli/Xa/Xa.cs | 49 ++++++++++++ src/Dtmcli/Xa/XaGlobalTransaction.cs | 53 +++++++++++++ src/Dtmcli/Xa/XaLocalTransaction.cs | 90 +++++++++++++++++++++++ tests/Dtmcli.Tests/DbSpecialTests.cs | 17 +++++ 8 files changed, 231 insertions(+), 13 deletions(-) create mode 100644 src/Dtmcli/Xa/Xa.cs create mode 100644 src/Dtmcli/Xa/XaGlobalTransaction.cs create mode 100644 src/Dtmcli/Xa/XaLocalTransaction.cs diff --git a/src/DtmCommon/Constant.cs b/src/DtmCommon/Constant.cs index 20271ae..71961cb 100644 --- a/src/DtmCommon/Constant.cs +++ b/src/DtmCommon/Constant.cs @@ -7,6 +7,7 @@ public class Constant public static readonly string TYPE_TCC = "tcc"; public static readonly string TYPE_SAGA = "saga"; public static readonly string TYPE_MSG = "msg"; + public static readonly string TYPE_XA = "xa"; public static readonly string ResultFailure = "FAILURE"; public static readonly string ResultSuccess = "SUCCESS"; diff --git a/src/DtmCommon/Imp/DbSpecialDelegate.cs b/src/DtmCommon/Imp/DbSpecialDelegate.cs index a02b563..8a33dbd 100644 --- a/src/DtmCommon/Imp/DbSpecialDelegate.cs +++ b/src/DtmCommon/Imp/DbSpecialDelegate.cs @@ -8,16 +8,26 @@ public class DbSpecialDelegate { private readonly IDbSpecial _special; + private readonly Dictionary _specialDic; + public DbSpecialDelegate(IEnumerable specials, IOptions optionsAccs) { - var dbSpecial = specials.FirstOrDefault(x => x.Name.Equals(optionsAccs.Value.SqlDbType)); - - if (dbSpecial == null) throw new DtmException($"unknown db type '{optionsAccs.Value.SqlDbType}'"); - - _special = dbSpecial; + this._specialDic = specials.ToDictionary((i) => i.Name); + this._specialDic.TryGetValue(optionsAccs.Value.SqlDbType,out _special); + if (_specialDic.TryGetValue(optionsAccs.Value.SqlDbType, out _special) == false) + throw new DtmException($"unknown db type '{optionsAccs.Value.SqlDbType}'"); } public IDbSpecial GetDbSpecial() => _special; + + public IDbSpecial GetDbSpecialByName(string sqlDbType) + { + IDbSpecial special; + if (this._specialDic.TryGetValue(sqlDbType, out special) == false) + throw new DtmException($"unknown db type '{sqlDbType}'"); + + return special; + } } } \ No newline at end of file diff --git a/src/Dtmcli/DtmClient.cs b/src/Dtmcli/DtmClient.cs index dac4e4d..81ce4c4 100644 --- a/src/Dtmcli/DtmClient.cs +++ b/src/Dtmcli/DtmClient.cs @@ -13,7 +13,6 @@ namespace Dtmcli public class DtmClient : IDtmClient { private static readonly char Slash = '/'; - private static readonly string QueryStringFormat = "dtm={0}&gid={1}&trans_type={2}&branch_id={3}&op={4}"; private static readonly string QuestionMark = "?"; private static readonly string And = "&"; @@ -91,13 +90,10 @@ public async Task TransRegisterBranch(TransBase tb, Dictionary a public async Task TransRequestBranch(TransBase tb, HttpMethod method, object body, string branchID, string op, string url, CancellationToken cancellationToken) { - var queryParams = string.Format( - QueryStringFormat, - string.Concat(_dtmOptions.DtmUrl.TrimEnd(Slash), Constant.Request.URLBASE_PREFIX), - tb.Gid, - tb.TransType, - branchID, - op); + var uriPath = string.Concat(_dtmOptions.DtmUrl.TrimEnd(Slash), Constant.Request.URLBASE_PREFIX); + var queryParams = tb.TransType == "xa" ? + $"dtm={uriPath}&gid={tb.Gid}&trans_type={tb.TransType}&branch_id={branchID}&op={op}&phase2_url={url}" : + $"dtm={uriPath}&gid={tb.Gid}&trans_type={tb.TransType}&branch_id={branchID}&op={op}"; var client = _httpClientFactory.CreateClient(Constant.BranchClientHttpName); diff --git a/src/Dtmcli/ServiceCollectionExtensions.cs b/src/Dtmcli/ServiceCollectionExtensions.cs index 5c365ee..543cabd 100644 --- a/src/Dtmcli/ServiceCollectionExtensions.cs +++ b/src/Dtmcli/ServiceCollectionExtensions.cs @@ -92,6 +92,8 @@ private static void AddDtmCore(IServiceCollection services) services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); + services.TryAddSingleton(); + services.TryAddSingleton(); DtmCommon.ServiceCollectionExtensions.AddDtmCommon(services); diff --git a/src/Dtmcli/Xa/Xa.cs b/src/Dtmcli/Xa/Xa.cs new file mode 100644 index 0000000..267df5c --- /dev/null +++ b/src/Dtmcli/Xa/Xa.cs @@ -0,0 +1,49 @@ +using Dtmcli.DtmImp; +using DtmCommon; +using System; +using System.Net.Http; +using System.Text.Json.Serialization; +using System.Threading; +using System.Threading.Tasks; + +namespace Dtmcli +{ + public sealed class Xa : TransBase + { + private readonly IDtmClient _dtmClient; + + internal Xa(IDtmClient dtmHttpClient, string gid) + { + this._dtmClient = dtmHttpClient; + this.Gid = gid; + this.TransType = DtmCommon.Constant.TYPE_XA; + this.BranchIDGen = new BranchIDGen(); + } + + internal Xa(IDtmClient dtmHttpClient) + { + this._dtmClient = dtmHttpClient; + } + + public async Task CallBranch(object body, string url, CancellationToken cancellationToken = default) + { + using var response = await _dtmClient.TransRequestBranch( + this, + HttpMethod.Post, + body, + this.BranchIDGen.NewSubBranchID(), + Constant.Request.BRANCH_ACTION, + url, + cancellationToken).ConfigureAwait(false); + + Exception ex = await Utils.RespAsErrorCompatible(response); + if (null != ex) + throw ex; + + return await response.Content.ReadAsStringAsync(); + } + + [JsonIgnore] + public string Phase2Url { get; set; } + } +} diff --git a/src/Dtmcli/Xa/XaGlobalTransaction.cs b/src/Dtmcli/Xa/XaGlobalTransaction.cs new file mode 100644 index 0000000..58c8ba8 --- /dev/null +++ b/src/Dtmcli/Xa/XaGlobalTransaction.cs @@ -0,0 +1,53 @@ +using DtmCommon; +using Microsoft.Extensions.Logging; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Dtmcli +{ + public sealed class XaGlobalTransaction + { + private readonly IDtmClient _dtmClient; + private readonly ILogger _logger; + + public XaGlobalTransaction(IDtmClient dtmClient, ILoggerFactory factory) + { + this._dtmClient = dtmClient; + this._logger = factory.CreateLogger(); + } + + public async Task Excecute(Func xa_cb, CancellationToken cancellationToken = default) + { + var gid = await _dtmClient.GenGid(cancellationToken); + await this.Excecute(gid, xa_cb, cancellationToken); + return gid; + } + + public async Task Excecute(string gid, Func xa_cb, CancellationToken cancellationToken = default) + { + await Excecute(gid, null, xa_cb, cancellationToken); + } + + public async Task Excecute(string gid, Action custom, Func xa_cb, CancellationToken cancellationToken = default) + { + Xa xa = new(this._dtmClient, gid); + if (null != custom) + custom(xa); + + try + { + //todo : tb unused + await _dtmClient.TransCallDtm(null, xa, Constant.Request.OPERATION_PREPARE, cancellationToken); + await xa_cb(xa); + await _dtmClient.TransCallDtm(null, xa, Constant.Request.OPERATION_SUBMIT, cancellationToken); + } + catch (Exception ex) + { + xa.RollbackReason = ex.Message.Substring(0, ex.Message.Length > 1023 ? 1023 : ex.Message.Length); + _logger.LogError(ex, "prepare or submitting global transaction error"); + await _dtmClient.TransCallDtm(null, xa, Constant.Request.OPERATION_ABORT, cancellationToken); + } + } + } +} diff --git a/src/Dtmcli/Xa/XaLocalTransaction.cs b/src/Dtmcli/Xa/XaLocalTransaction.cs new file mode 100644 index 0000000..82efc98 --- /dev/null +++ b/src/Dtmcli/Xa/XaLocalTransaction.cs @@ -0,0 +1,90 @@ +using Dapper; +using DtmCommon; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.Common; +using System.Threading; +using System.Threading.Tasks; +using static Dtmcli.Constant; + +namespace Dtmcli +{ + public sealed class XaLocalTransaction + { + private readonly DbSpecialDelegate _dbSpecia; + private readonly IDtmClient _dtmClient; + private readonly ILogger _logger; + private readonly DbUtils _utils; + + public XaLocalTransaction(IDtmClient dtmClient, DbSpecialDelegate dbSpecia, DbUtils utils, ILoggerFactory factory) + { + this._dtmClient = dtmClient; + this._dbSpecia = dbSpecia; + this._utils = utils; + this._logger = factory.CreateLogger(); + } + + public async Task Excecute(IDictionary values, DbConnection conn, string dbType, Func xaFunc, CancellationToken token = default) + { + var xa = this.XaFromQuery(values); + var dbSpecial = this._dbSpecia.GetDbSpecialByName(dbType); + if (ConnectionState.Open != conn.State) + { + conn.Open(); + } + + if (DtmCommon.Constant.OpCommit == xa.Op || DtmCommon.Constant.OpRollback == xa.Op) + { + await XaHandlePhase2(xa, conn, dbSpecial); + } + else + { + await HandleLocalTrans(xa, conn, dbSpecial, xaFunc, token); + } + } + + private async Task HandleLocalTrans(Xa xa, DbConnection conn, IDbSpecial dbSpecial, Func outsideAction, CancellationToken token) + { + var xaBranchID = $"{xa.Gid}-{xa.BranchIDGen.BranchID}"; + await conn.ExecuteAsync(dbSpecial.GetXaSQL("start", xaBranchID)); + await this._utils.InsertBarrier(conn, xa.TransType, xa.Gid, xa.BranchIDGen.BranchID, DtmCommon.Constant.OpAction, xa.BranchIDGen.BranchID, DtmCommon.Constant.OpAction); + await outsideAction(conn, xa); + await _dtmClient.TransRegisterBranch(xa, this.BuildRegisterItems(xa), Constant.Request.OPERATION_REGISTERBRANCH, token); + await conn.ExecuteAsync(dbSpecial.GetXaSQL("end", xaBranchID)); + await conn.ExecuteAsync(dbSpecial.GetXaSQL("prepare", xaBranchID)); + } + + private async Task XaHandlePhase2(Xa xa, DbConnection conn, IDbSpecial dbSpecia) + { + var xaBranchID = $"{xa.Gid}-{xa.BranchIDGen.BranchID}"; + var xaCommon = xa.Op == DtmCommon.Constant.OpCommit ? "commit" : "rollback"; + await conn.ExecuteAsync(dbSpecia.GetXaSQL(xaCommon, xaBranchID)); + if (DtmCommon.Constant.OpRollback == xa.Op) + { + await this._utils.InsertBarrier(conn, "xa", xa.Gid, xa.BranchIDGen.BranchID, DtmCommon.Constant.OpAction, xa.BranchIDGen.BranchID, xa.Op); + } + } + + private Xa XaFromQuery(IDictionary values) + { + Xa xa = new(this._dtmClient); + xa.Gid = values[Request.GID]; + xa.TransType = values[Request.TRANS_TYPE]; + xa.Dtm = values.ContainsKey(Request.DTM) ? values[Request.DTM] : string.Empty; + xa.BranchIDGen = new BranchIDGen(values[Request.BRANCH_ID]); + xa.Op = values[Request.OP]; + xa.Phase2Url = values.ContainsKey("phase2_url") ? values["phase2_url"] : string.Empty; + return xa; + } + + private Dictionary BuildRegisterItems(Xa xa) + { + return new Dictionary { + { "branch_id", xa.BranchIDGen.BranchID}, + { "url", xa.Phase2Url } + }; + } + } +} diff --git a/tests/Dtmcli.Tests/DbSpecialTests.cs b/tests/Dtmcli.Tests/DbSpecialTests.cs index e3a9136..bfc21d9 100644 --- a/tests/Dtmcli.Tests/DbSpecialTests.cs +++ b/tests/Dtmcli.Tests/DbSpecialTests.cs @@ -53,5 +53,22 @@ public void Test_Other_DbSpecial() var ex = Assert.Throws(() => provider.GetRequiredService()); Assert.Equal("unknown db type 'other'", ex.Message); } + + [Fact] + public void Test_Specific_DbSpecial() + { + var provider = TestHelper.AddDtmCli(); + var dbSpecialDelegate = provider.GetRequiredService(); + + var mysqlSpecial = dbSpecialDelegate.GetDbSpecialByName("mysql"); + var postgresSpecial = dbSpecialDelegate.GetDbSpecialByName("postgres"); + var sqlserverSpecial = dbSpecialDelegate.GetDbSpecialByName("sqlserver"); + var dtmException = Assert.Throws(() => dbSpecialDelegate.GetDbSpecialByName("other")); + + Assert.IsType(mysqlSpecial); + Assert.IsType(postgresSpecial); + Assert.IsType(sqlserverSpecial); + Assert.Equal("unknown db type 'other'", dtmException.Message); + } } } \ No newline at end of file From 988d115e9b8ec6e093636652ff8ffb85e60c7515 Mon Sep 17 00:00:00 2001 From: Long Chen Date: Mon, 19 Jun 2023 15:25:23 +0800 Subject: [PATCH 02/11] Fix the key conflict caused by repeated registration of DI --- src/DtmCommon/Imp/DbSpecialDelegate.cs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/DtmCommon/Imp/DbSpecialDelegate.cs b/src/DtmCommon/Imp/DbSpecialDelegate.cs index 8a33dbd..9995895 100644 --- a/src/DtmCommon/Imp/DbSpecialDelegate.cs +++ b/src/DtmCommon/Imp/DbSpecialDelegate.cs @@ -12,8 +12,8 @@ public class DbSpecialDelegate public DbSpecialDelegate(IEnumerable specials, IOptions optionsAccs) { - this._specialDic = specials.ToDictionary((i) => i.Name); - this._specialDic.TryGetValue(optionsAccs.Value.SqlDbType,out _special); + this._specialDic = GetSpecialDictionary(specials); + this._specialDic.TryGetValue(optionsAccs.Value.SqlDbType, out _special); if (_specialDic.TryGetValue(optionsAccs.Value.SqlDbType, out _special) == false) throw new DtmException($"unknown db type '{optionsAccs.Value.SqlDbType}'"); } @@ -28,6 +28,18 @@ public IDbSpecial GetDbSpecialByName(string sqlDbType) return special; } + + Dictionary GetSpecialDictionary(IEnumerable specials) + { + Dictionary specialDic = new(); + foreach (var special in specials) + { + if (specialDic.ContainsKey(special.Name) == false) + specialDic.Add(special.Name, special); + } + + return specialDic; + } } } \ No newline at end of file From eb3c11fe7c9105c7a136176cffba72f24f44d492 Mon Sep 17 00:00:00 2001 From: Long Chen Date: Mon, 19 Jun 2023 16:13:54 +0800 Subject: [PATCH 03/11] xa local transaction handle barrier exception --- src/Dtmcli/Xa/XaLocalTransaction.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Dtmcli/Xa/XaLocalTransaction.cs b/src/Dtmcli/Xa/XaLocalTransaction.cs index 82efc98..ac48ddb 100644 --- a/src/Dtmcli/Xa/XaLocalTransaction.cs +++ b/src/Dtmcli/Xa/XaLocalTransaction.cs @@ -45,11 +45,14 @@ public async Task Excecute(IDictionary values, DbConnection conn } } - private async Task HandleLocalTrans(Xa xa, DbConnection conn, IDbSpecial dbSpecial, Func outsideAction, CancellationToken token) + private async Task HandleLocalTrans(Xa xa, DbConnection conn, IDbSpecial dbSpecial, Func outsideAction, CancellationToken token) { var xaBranchID = $"{xa.Gid}-{xa.BranchIDGen.BranchID}"; await conn.ExecuteAsync(dbSpecial.GetXaSQL("start", xaBranchID)); - await this._utils.InsertBarrier(conn, xa.TransType, xa.Gid, xa.BranchIDGen.BranchID, DtmCommon.Constant.OpAction, xa.BranchIDGen.BranchID, DtmCommon.Constant.OpAction); + var (_, ex) = await this._utils.InsertBarrier(conn, xa.TransType, xa.Gid, xa.BranchIDGen.BranchID, DtmCommon.Constant.OpAction, xa.BranchIDGen.BranchID, DtmCommon.Constant.OpAction); + if (ex != null || conn.State != ConnectionState.Open) + throw new DtmOngingException(ex?.Message); + await outsideAction(conn, xa); await _dtmClient.TransRegisterBranch(xa, this.BuildRegisterItems(xa), Constant.Request.OPERATION_REGISTERBRANCH, token); await conn.ExecuteAsync(dbSpecial.GetXaSQL("end", xaBranchID)); @@ -63,7 +66,9 @@ private async Task XaHandlePhase2(Xa xa, DbConnection conn, IDbSpecial dbSpecia) await conn.ExecuteAsync(dbSpecia.GetXaSQL(xaCommon, xaBranchID)); if (DtmCommon.Constant.OpRollback == xa.Op) { - await this._utils.InsertBarrier(conn, "xa", xa.Gid, xa.BranchIDGen.BranchID, DtmCommon.Constant.OpAction, xa.BranchIDGen.BranchID, xa.Op); + var (_, ex) = await this._utils.InsertBarrier(conn, "xa", xa.Gid, xa.BranchIDGen.BranchID, DtmCommon.Constant.OpAction, xa.BranchIDGen.BranchID, xa.Op); + if (ex != null || conn.State != ConnectionState.Open) + throw new DtmOngingException(ex?.Message); } } From f802f9f7ef5232b561771dd4087df89f8b00f4b9 Mon Sep 17 00:00:00 2001 From: Long Chen Date: Mon, 19 Jun 2023 16:50:11 +0800 Subject: [PATCH 04/11] add XA patter README --- README-cn.md | 22 ++++++++++++++++++++++ README.md | 24 ++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/README-cn.md b/README-cn.md index 2d74418..104fc8e 100644 --- a/README-cn.md +++ b/README-cn.md @@ -253,6 +253,28 @@ public class MyBusi } ``` +### XA pattern + +```cs +public class MyBusi +{ + private readonly Dtmcli.XaGlobalTransaction _globalTransaction; + + public MyBusi(Dtmcli.XaGlobalTransaction globalTransaction) + { + this._globalTransaction = globalTransaction; + } + + public async Task DoBusAsync() + { + await _globalTransaction.Excecute(async (Xa xa) => + { + await xa.CallBranch(new TransRequest("1", -30), _settings.BusiUrl + "/XaTransOut", cancellationToken); + await xa.CallBranch(new TransRequest("2", 30), _settings.BusiUrl + "/XaTransIn", cancellationToken); + }, cancellationToken); + } +} +``` ## 可运行的使用示例 diff --git a/README.md b/README.md index d40682b..f10a83b 100644 --- a/README.md +++ b/README.md @@ -260,6 +260,30 @@ public class MyBusi } ``` +### XA pattern + + +```cs +public class MyBusi +{ + private readonly Dtmcli.XaGlobalTransaction _globalTransaction; + + public MyBusi(Dtmcli.XaGlobalTransaction globalTransaction) + { + this._globalTransaction = globalTransaction; + } + + public async Task DoBusAsync() + { + await _globalTransaction.Excecute(async (Xa xa) => + { + await xa.CallBranch(new TransRequest("1", -30), _settings.BusiUrl + "/XaTransOut", cancellationToken); + await xa.CallBranch(new TransRequest("2", 30), _settings.BusiUrl + "/XaTransIn", cancellationToken); + }, cancellationToken); + } +} +``` + ## Complete example From 329b02dceddc4d3033355b5099340542201856cb Mon Sep 17 00:00:00 2001 From: Long Chen Date: Tue, 20 Jun 2023 15:44:04 +0800 Subject: [PATCH 05/11] Ignore repeat commit or rollback with the same id error --- src/Dtmcli/Xa/XaLocalTransaction.cs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/Dtmcli/Xa/XaLocalTransaction.cs b/src/Dtmcli/Xa/XaLocalTransaction.cs index ac48ddb..04a7b44 100644 --- a/src/Dtmcli/Xa/XaLocalTransaction.cs +++ b/src/Dtmcli/Xa/XaLocalTransaction.cs @@ -61,9 +61,19 @@ private async Task HandleLocalTrans(Xa xa, DbConnection conn, IDbSpecial dbSpeci private async Task XaHandlePhase2(Xa xa, DbConnection conn, IDbSpecial dbSpecia) { - var xaBranchID = $"{xa.Gid}-{xa.BranchIDGen.BranchID}"; - var xaCommon = xa.Op == DtmCommon.Constant.OpCommit ? "commit" : "rollback"; - await conn.ExecuteAsync(dbSpecia.GetXaSQL(xaCommon, xaBranchID)); + try + { + var xaBranchID = $"{xa.Gid}-{xa.BranchIDGen.BranchID}"; + var xaCommon = xa.Op == DtmCommon.Constant.OpCommit ? "commit" : "rollback"; + await conn.ExecuteAsync(dbSpecia.GetXaSQL(xaCommon, xaBranchID)); + } + catch (Exception ex) when (ex.Message.Contains("XAER_NOTA")) { } + catch (Exception ex) when (ex.Message.Contains("does not exist")) { } + catch + { + throw; + } + if (DtmCommon.Constant.OpRollback == xa.Op) { var (_, ex) = await this._utils.InsertBarrier(conn, "xa", xa.Gid, xa.BranchIDGen.BranchID, DtmCommon.Constant.OpAction, xa.BranchIDGen.BranchID, xa.Op); From 9e37b8a1ad6b5a05766cef6f7b3b0661dcf5cb95 Mon Sep 17 00:00:00 2001 From: JackBOBO Date: Wed, 21 Jun 2023 09:39:09 +0800 Subject: [PATCH 06/11] Adjust the Connection opening to asynchronous Co-authored-by: Catcher Wong --- src/Dtmcli/Xa/XaLocalTransaction.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Dtmcli/Xa/XaLocalTransaction.cs b/src/Dtmcli/Xa/XaLocalTransaction.cs index 04a7b44..d506fd8 100644 --- a/src/Dtmcli/Xa/XaLocalTransaction.cs +++ b/src/Dtmcli/Xa/XaLocalTransaction.cs @@ -32,7 +32,7 @@ public async Task Excecute(IDictionary values, DbConnection conn var dbSpecial = this._dbSpecia.GetDbSpecialByName(dbType); if (ConnectionState.Open != conn.State) { - conn.Open(); + await conn.OpenAsync(); } if (DtmCommon.Constant.OpCommit == xa.Op || DtmCommon.Constant.OpRollback == xa.Op) From 9b1fb998ebf320b841e173713078035df9a38353 Mon Sep 17 00:00:00 2001 From: JackBOBO Date: Wed, 21 Jun 2023 09:44:55 +0800 Subject: [PATCH 07/11] Adjust the use of "xa" literals to TYPE_XA constants Co-authored-by: Catcher Wong --- src/Dtmcli/DtmClient.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Dtmcli/DtmClient.cs b/src/Dtmcli/DtmClient.cs index 81ce4c4..5537c9f 100644 --- a/src/Dtmcli/DtmClient.cs +++ b/src/Dtmcli/DtmClient.cs @@ -91,7 +91,7 @@ public async Task TransRegisterBranch(TransBase tb, Dictionary a public async Task TransRequestBranch(TransBase tb, HttpMethod method, object body, string branchID, string op, string url, CancellationToken cancellationToken) { var uriPath = string.Concat(_dtmOptions.DtmUrl.TrimEnd(Slash), Constant.Request.URLBASE_PREFIX); - var queryParams = tb.TransType == "xa" ? + var queryParams = tb.TransType == DtmCommon.Constant.TYPE_XA ? $"dtm={uriPath}&gid={tb.Gid}&trans_type={tb.TransType}&branch_id={branchID}&op={op}&phase2_url={url}" : $"dtm={uriPath}&gid={tb.Gid}&trans_type={tb.TransType}&branch_id={branchID}&op={op}"; From dbfd73ed3ad79a0bac3fb71a255defaf4bf89f15 Mon Sep 17 00:00:00 2001 From: Long Chen Date: Wed, 21 Jun 2023 10:28:24 +0800 Subject: [PATCH 08/11] remove _special duplicate get --- src/DtmCommon/Imp/DbSpecialDelegate.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/DtmCommon/Imp/DbSpecialDelegate.cs b/src/DtmCommon/Imp/DbSpecialDelegate.cs index 9995895..78759a8 100644 --- a/src/DtmCommon/Imp/DbSpecialDelegate.cs +++ b/src/DtmCommon/Imp/DbSpecialDelegate.cs @@ -13,8 +13,7 @@ public class DbSpecialDelegate public DbSpecialDelegate(IEnumerable specials, IOptions optionsAccs) { this._specialDic = GetSpecialDictionary(specials); - this._specialDic.TryGetValue(optionsAccs.Value.SqlDbType, out _special); - if (_specialDic.TryGetValue(optionsAccs.Value.SqlDbType, out _special) == false) + if (this._specialDic.TryGetValue(optionsAccs.Value.SqlDbType, out _special) == false) throw new DtmException($"unknown db type '{optionsAccs.Value.SqlDbType}'"); } From dcd3f4baa20d3ad5f31ab8420d41a8a4913d332e Mon Sep 17 00:00:00 2001 From: Long Chen Date: Mon, 26 Jun 2023 15:04:33 +0800 Subject: [PATCH 09/11] Adjust Xa's FromQuery, Excecute function signatures, and add parameter checks --- src/Dtmcli/Constant.cs | 2 + src/Dtmcli/Xa/Xa.cs | 61 ++++++++++++++++++++++++++++ src/Dtmcli/Xa/XaGlobalTransaction.cs | 7 ++-- src/Dtmcli/Xa/XaLocalTransaction.cs | 31 +++++++------- 4 files changed, 82 insertions(+), 19 deletions(-) diff --git a/src/Dtmcli/Constant.cs b/src/Dtmcli/Constant.cs index 9f82389..1a738de 100644 --- a/src/Dtmcli/Constant.cs +++ b/src/Dtmcli/Constant.cs @@ -43,6 +43,8 @@ internal static class Request internal const string OPERATION_REGISTERBRANCH = "registerBranch"; + internal const string PHASE2_URL = "phase2_url"; + /// /// branch type for message, SAGA, XA /// diff --git a/src/Dtmcli/Xa/Xa.cs b/src/Dtmcli/Xa/Xa.cs index 267df5c..f7cd0fc 100644 --- a/src/Dtmcli/Xa/Xa.cs +++ b/src/Dtmcli/Xa/Xa.cs @@ -1,10 +1,12 @@ using Dtmcli.DtmImp; using DtmCommon; using System; +using System.Collections.Generic; using System.Net.Http; using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; +using static Dtmcli.Constant; namespace Dtmcli { @@ -45,5 +47,64 @@ public async Task CallBranch(object body, string url, CancellationToken [JsonIgnore] public string Phase2Url { get; set; } + + +#if NET5_0_OR_GREATER + public static Xa FromQuery(IDtmClient dtmClient, Microsoft.AspNetCore.Http.IQueryCollection quersy) + { + if (quersy.TryGetValue(Request.GID, out var gid) == false || string.IsNullOrEmpty(gid)) + throw new ArgumentNullException(Request.GID); + + if (quersy.TryGetValue(Request.TRANS_TYPE, out var transType) == false || string.IsNullOrEmpty(transType)) + throw new ArgumentNullException(Request.TRANS_TYPE); + + if (quersy.TryGetValue(Request.OP, out var op) == false || string.IsNullOrEmpty(op)) + throw new ArgumentNullException(Request.OP); + + if (quersy.TryGetValue(Request.BRANCH_ID, out var branchID) == false || string.IsNullOrEmpty(branchID)) + throw new ArgumentNullException(Request.BRANCH_ID); + + quersy.TryGetValue(Request.DTM, out var dtm); + quersy.TryGetValue(Request.PHASE2_URL, out var phase2Url); + + return new(dtmClient) + { + Gid = gid, + Dtm = dtm, + Op = op, + TransType = transType, + Phase2Url = phase2Url, + BranchIDGen = new BranchIDGen(branchID), + }; + } +#else + public static Xa FromQuery(IDtmClient dtmClient, IDictionary quersy) + { + if (!quersy.TryGetValue(Request.GID, out var gid) == false || string.IsNullOrEmpty(gid)) + throw new ArgumentNullException(Request.GID); + + if (quersy.TryGetValue(Request.TRANS_TYPE, out var transType) == false || string.IsNullOrEmpty(transType)) + throw new ArgumentNullException(Request.TRANS_TYPE); + + if (quersy.TryGetValue(Request.OP, out var op) == false || string.IsNullOrEmpty(op)) + throw new ArgumentNullException(Request.OP); + + if (quersy.TryGetValue(Request.BRANCH_ID, out var branchID) == false || string.IsNullOrEmpty(branchID)) + throw new ArgumentNullException(Request.BRANCH_ID); + + quersy.TryGetValue(Request.DTM, out var dtm); + quersy.TryGetValue(Request.PHASE2_URL, out var phase2Url); + + return new(dtmClient) + { + Gid = gid, + Dtm = dtm, + Op = op, + TransType = transType, + Phase2Url = phase2Url, + BranchIDGen = new BranchIDGen(branchID), + }; + } +#endif } } diff --git a/src/Dtmcli/Xa/XaGlobalTransaction.cs b/src/Dtmcli/Xa/XaGlobalTransaction.cs index 58c8ba8..0e93170 100644 --- a/src/Dtmcli/Xa/XaGlobalTransaction.cs +++ b/src/Dtmcli/Xa/XaGlobalTransaction.cs @@ -17,7 +17,7 @@ public XaGlobalTransaction(IDtmClient dtmClient, ILoggerFactory factory) this._logger = factory.CreateLogger(); } - public async Task Excecute(Func xa_cb, CancellationToken cancellationToken = default) + public async Task ExcecuteAsync(Func xa_cb, CancellationToken cancellationToken = default) { var gid = await _dtmClient.GenGid(cancellationToken); await this.Excecute(gid, xa_cb, cancellationToken); @@ -26,10 +26,10 @@ public async Task Excecute(Func xa_cb, CancellationToken cance public async Task Excecute(string gid, Func xa_cb, CancellationToken cancellationToken = default) { - await Excecute(gid, null, xa_cb, cancellationToken); + await ExcecuteAsync(gid, null, xa_cb, cancellationToken); } - public async Task Excecute(string gid, Action custom, Func xa_cb, CancellationToken cancellationToken = default) + public async Task ExcecuteAsync(string gid, Action custom, Func xa_cb, CancellationToken cancellationToken = default) { Xa xa = new(this._dtmClient, gid); if (null != custom) @@ -37,7 +37,6 @@ public async Task Excecute(string gid, Action custom, Func xa_cb, try { - //todo : tb unused await _dtmClient.TransCallDtm(null, xa, Constant.Request.OPERATION_PREPARE, cancellationToken); await xa_cb(xa); await _dtmClient.TransCallDtm(null, xa, Constant.Request.OPERATION_SUBMIT, cancellationToken); diff --git a/src/Dtmcli/Xa/XaLocalTransaction.cs b/src/Dtmcli/Xa/XaLocalTransaction.cs index d506fd8..54c9bdf 100644 --- a/src/Dtmcli/Xa/XaLocalTransaction.cs +++ b/src/Dtmcli/Xa/XaLocalTransaction.cs @@ -26,13 +26,26 @@ public XaLocalTransaction(IDtmClient dtmClient, DbSpecialDelegate dbSpecia, DbUt this._logger = factory.CreateLogger(); } - public async Task Excecute(IDictionary values, DbConnection conn, string dbType, Func xaFunc, CancellationToken token = default) +#if NET5_0_OR_GREATER + public async Task ExcecuteAsync(Microsoft.AspNetCore.Http.IQueryCollection quersy, DbConnection conn, string dbType, Func xaFunc, CancellationToken token = default) + { + Xa xa = Xa.FromQuery(this._dtmClient, quersy); + await this.InternalExcecuteAsync(xa, conn, dbType, xaFunc, token); + } +#else + public async Task ExcecuteAsync(IDictionary values, DbConnection conn, string dbType, Func xaFunc, CancellationToken token = default) + { + var xa = Xa.FromQuery(this._dtmClient, values); + await this.InternalExcecuteAsync(xa, conn, dbType, xaFunc, token); + } +#endif + + private async Task InternalExcecuteAsync(Xa xa, DbConnection conn, string dbType, Func xaFunc, CancellationToken token = default) { - var xa = this.XaFromQuery(values); var dbSpecial = this._dbSpecia.GetDbSpecialByName(dbType); if (ConnectionState.Open != conn.State) { - await conn.OpenAsync(); + await conn.OpenAsync(); } if (DtmCommon.Constant.OpCommit == xa.Op || DtmCommon.Constant.OpRollback == xa.Op) @@ -82,18 +95,6 @@ private async Task XaHandlePhase2(Xa xa, DbConnection conn, IDbSpecial dbSpecia) } } - private Xa XaFromQuery(IDictionary values) - { - Xa xa = new(this._dtmClient); - xa.Gid = values[Request.GID]; - xa.TransType = values[Request.TRANS_TYPE]; - xa.Dtm = values.ContainsKey(Request.DTM) ? values[Request.DTM] : string.Empty; - xa.BranchIDGen = new BranchIDGen(values[Request.BRANCH_ID]); - xa.Op = values[Request.OP]; - xa.Phase2Url = values.ContainsKey("phase2_url") ? values["phase2_url"] : string.Empty; - return xa; - } - private Dictionary BuildRegisterItems(Xa xa) { return new Dictionary { From ffce7c46facd32a346e9a14441ee7536cf73a5c3 Mon Sep 17 00:00:00 2001 From: Long Chen Date: Wed, 28 Jun 2023 13:53:28 +0800 Subject: [PATCH 10/11] Change the parameter name quersy to query --- src/Dtmcli/Xa/Xa.cs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Dtmcli/Xa/Xa.cs b/src/Dtmcli/Xa/Xa.cs index f7cd0fc..cbcd475 100644 --- a/src/Dtmcli/Xa/Xa.cs +++ b/src/Dtmcli/Xa/Xa.cs @@ -50,22 +50,22 @@ public async Task CallBranch(object body, string url, CancellationToken #if NET5_0_OR_GREATER - public static Xa FromQuery(IDtmClient dtmClient, Microsoft.AspNetCore.Http.IQueryCollection quersy) + public static Xa FromQuery(IDtmClient dtmClient, Microsoft.AspNetCore.Http.IQueryCollection query) { - if (quersy.TryGetValue(Request.GID, out var gid) == false || string.IsNullOrEmpty(gid)) + if (query.TryGetValue(Request.GID, out var gid) == false || string.IsNullOrEmpty(gid)) throw new ArgumentNullException(Request.GID); - if (quersy.TryGetValue(Request.TRANS_TYPE, out var transType) == false || string.IsNullOrEmpty(transType)) + if (query.TryGetValue(Request.TRANS_TYPE, out var transType) == false || string.IsNullOrEmpty(transType)) throw new ArgumentNullException(Request.TRANS_TYPE); - if (quersy.TryGetValue(Request.OP, out var op) == false || string.IsNullOrEmpty(op)) + if (query.TryGetValue(Request.OP, out var op) == false || string.IsNullOrEmpty(op)) throw new ArgumentNullException(Request.OP); - if (quersy.TryGetValue(Request.BRANCH_ID, out var branchID) == false || string.IsNullOrEmpty(branchID)) + if (query.TryGetValue(Request.BRANCH_ID, out var branchID) == false || string.IsNullOrEmpty(branchID)) throw new ArgumentNullException(Request.BRANCH_ID); - quersy.TryGetValue(Request.DTM, out var dtm); - quersy.TryGetValue(Request.PHASE2_URL, out var phase2Url); + query.TryGetValue(Request.DTM, out var dtm); + query.TryGetValue(Request.PHASE2_URL, out var phase2Url); return new(dtmClient) { @@ -78,22 +78,22 @@ public static Xa FromQuery(IDtmClient dtmClient, Microsoft.AspNetCore.Http.IQuer }; } #else - public static Xa FromQuery(IDtmClient dtmClient, IDictionary quersy) + public static Xa FromQuery(IDtmClient dtmClient, IDictionary query) { - if (!quersy.TryGetValue(Request.GID, out var gid) == false || string.IsNullOrEmpty(gid)) + if (!query.TryGetValue(Request.GID, out var gid) == false || string.IsNullOrEmpty(gid)) throw new ArgumentNullException(Request.GID); - if (quersy.TryGetValue(Request.TRANS_TYPE, out var transType) == false || string.IsNullOrEmpty(transType)) + if (query.TryGetValue(Request.TRANS_TYPE, out var transType) == false || string.IsNullOrEmpty(transType)) throw new ArgumentNullException(Request.TRANS_TYPE); - if (quersy.TryGetValue(Request.OP, out var op) == false || string.IsNullOrEmpty(op)) + if (query.TryGetValue(Request.OP, out var op) == false || string.IsNullOrEmpty(op)) throw new ArgumentNullException(Request.OP); - if (quersy.TryGetValue(Request.BRANCH_ID, out var branchID) == false || string.IsNullOrEmpty(branchID)) + if (query.TryGetValue(Request.BRANCH_ID, out var branchID) == false || string.IsNullOrEmpty(branchID)) throw new ArgumentNullException(Request.BRANCH_ID); - quersy.TryGetValue(Request.DTM, out var dtm); - quersy.TryGetValue(Request.PHASE2_URL, out var phase2Url); + query.TryGetValue(Request.DTM, out var dtm); + query.TryGetValue(Request.PHASE2_URL, out var phase2Url); return new(dtmClient) { From 9207895084988d229b36fefeb23bfad7faf8b497 Mon Sep 17 00:00:00 2001 From: Long Chen Date: Wed, 28 Jun 2023 15:26:40 +0800 Subject: [PATCH 11/11] =?UTF-8?q?Modify=20=E2=80=98Excecute=E2=80=99=20to?= =?UTF-8?q?=20=E2=80=98ExcecuteAsync=E2=80=99=20in=20xa=20pattern=20in=20R?= =?UTF-8?q?EADME?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README-cn.md | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README-cn.md b/README-cn.md index 104fc8e..3e03c3e 100644 --- a/README-cn.md +++ b/README-cn.md @@ -267,7 +267,7 @@ public class MyBusi public async Task DoBusAsync() { - await _globalTransaction.Excecute(async (Xa xa) => + await _globalTransaction.ExcecuteAsync(async (Xa xa) => { await xa.CallBranch(new TransRequest("1", -30), _settings.BusiUrl + "/XaTransOut", cancellationToken); await xa.CallBranch(new TransRequest("2", 30), _settings.BusiUrl + "/XaTransIn", cancellationToken); diff --git a/README.md b/README.md index f10a83b..c182741 100644 --- a/README.md +++ b/README.md @@ -275,7 +275,7 @@ public class MyBusi public async Task DoBusAsync() { - await _globalTransaction.Excecute(async (Xa xa) => + await _globalTransaction.ExcecuteAsync(async (Xa xa) => { await xa.CallBranch(new TransRequest("1", -30), _settings.BusiUrl + "/XaTransOut", cancellationToken); await xa.CallBranch(new TransRequest("2", 30), _settings.BusiUrl + "/XaTransIn", cancellationToken);