diff --git a/src/Dtmcli.Tests/BranchBarrierTests.cs b/src/Dtmcli.Tests/BranchBarrierTests.cs index 73a7165..04ee2b3 100644 --- a/src/Dtmcli.Tests/BranchBarrierTests.cs +++ b/src/Dtmcli.Tests/BranchBarrierTests.cs @@ -9,7 +9,7 @@ using Microsoft.Extensions.Primitives; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.DependencyInjection; -using Dtmcli.DtmImp; +using DtmCommon; namespace Dtmcli.Tests { @@ -57,7 +57,7 @@ public void CreateBranchBarrier_FromQs_Should_ThrowException() var qs = new Microsoft.AspNetCore.Http.QueryCollection(dict); - Assert.Throws(() => _factory.CreateBranchBarrier(qs)); + Assert.Throws(() => _factory.CreateBranchBarrier(qs)); } #endif @@ -162,11 +162,11 @@ public async void Call_Should_Throw_Duplicated_Exception_When_QueryPrepared_At_F var connQ = GetDbConnection(); connQ.Mocks.When(cmd => cmd.CommandText.Contains("insert", StringComparison.Ordinal)).ReturnsScalar(cmd => 1); - connQ.Mocks.When(cmd => cmd.CommandText.Contains("select", StringComparison.OrdinalIgnoreCase)).ReturnsScalar(cmd => Constant.Barrier.MSG_BARRIER_REASON); + connQ.Mocks.When(cmd => cmd.CommandText.Contains("select", StringComparison.OrdinalIgnoreCase)).ReturnsScalar(cmd => DtmCommon.Constant.Barrier.MSG_BARRIER_REASON); // QueryPrepared at first var qRes = await branchBarrier.QueryPrepared(connQ); - Assert.Equal(Constant.ErrFailure, qRes); + Assert.Equal(DtmCommon.Constant.ResultFailure, qRes); var connC = GetDbConnection(); connC.Mocks.When(cmd => cmd.Parameters.AsList().Select(x => x.Value).Contains("msg")).ReturnsScalar(cmd => 0); @@ -174,8 +174,8 @@ public async void Call_Should_Throw_Duplicated_Exception_When_QueryPrepared_At_F var mockBusiCall = new Mock>>(); // Call later - var ex = await Assert.ThrowsAsync(async () => await branchBarrier.Call(connC, mockBusiCall.Object)); - Assert.Equal(Constant.ResultDuplicated, ex.Message); + var ex = await Assert.ThrowsAsync(async () => await branchBarrier.Call(connC, mockBusiCall.Object)); + Assert.Equal(DtmCommon.Constant.ResultDuplicated, ex.Message); mockBusiCall.Verify(x => x.Invoke(It.IsAny()), Times.Never); } } diff --git a/src/Dtmcli.Tests/BranchIDGenTests.cs b/src/Dtmcli.Tests/BranchIDGenTests.cs index a42536f..80026e3 100644 --- a/src/Dtmcli.Tests/BranchIDGenTests.cs +++ b/src/Dtmcli.Tests/BranchIDGenTests.cs @@ -1,4 +1,4 @@ -using Dtmcli.DtmImp; +using DtmCommon; using System; using Xunit; diff --git a/src/Dtmcli.Tests/DbSpecialTests.cs b/src/Dtmcli.Tests/DbSpecialTests.cs index 25250ac..e3a9136 100644 --- a/src/Dtmcli.Tests/DbSpecialTests.cs +++ b/src/Dtmcli.Tests/DbSpecialTests.cs @@ -1,4 +1,4 @@ -using Dtmcli.DtmImp; +using DtmCommon; using Microsoft.Extensions.DependencyInjection; using Xunit; @@ -42,7 +42,7 @@ public void Test_MsSQL_DbSpecial() Assert.IsType(special); Assert.Equal("insert into a(f) values(@f)", special.GetInsertIgnoreTemplate("a(f) values(@f)", "c")); - Assert.Throws(() => special.GetXaSQL("", "")); + Assert.Throws(() => special.GetXaSQL("", "")); } [Fact] @@ -50,7 +50,7 @@ public void Test_Other_DbSpecial() { var provider = TestHelper.AddDtmCli(db: "other"); - var ex = Assert.Throws(() => provider.GetRequiredService()); + var ex = Assert.Throws(() => provider.GetRequiredService()); Assert.Equal("unknown db type 'other'", ex.Message); } } diff --git a/src/Dtmcli.Tests/MsgTests.cs b/src/Dtmcli.Tests/MsgTests.cs index 127d459..a885298 100644 --- a/src/Dtmcli.Tests/MsgTests.cs +++ b/src/Dtmcli.Tests/MsgTests.cs @@ -1,4 +1,5 @@ using Apps72.Dev.Data.DbMocker; +using DtmCommon; using Microsoft.Extensions.DependencyInjection; using Moq; using System; @@ -53,18 +54,17 @@ public async void Submit_Should_Succeed() { "bh2", "456" }, }); - var prepareRes = await msg.Prepare(busi + "/query"); - Assert.True(prepareRes); + await msg.Prepare(busi + "/query"); + await msg.Submit(); - var submitRes = await msg.Submit(); - Assert.True(submitRes); + Assert.True(true); } [Fact] public async void DoAndSubmitDB_Should_Throw_Exception_When_Transbase_InValid() { var dtmClient = new Mock(); - TestHelper.MockTransCallDtm(dtmClient, Constant.Request.OPERATION_PREPARE, true); + TestHelper.MockTransCallDtm(dtmClient, Constant.Request.OPERATION_PREPARE, false); var gid = string.Empty; var msg = new Msg(dtmClient.Object, _branchBarrierFactory, gid); @@ -75,14 +75,14 @@ public async void DoAndSubmitDB_Should_Throw_Exception_When_Transbase_InValid() var db = new MockDbConnection(); - await Assert.ThrowsAsync(async () => await msg.DoAndSubmitDB(busi + "/query", db, x => Task.FromResult(true))); + await Assert.ThrowsAsync(async () => await msg.DoAndSubmitDB(busi + "/query", db, x => Task.FromResult(true))); } [Fact] public async void DoAndSubmitDB_Should_Not_Call_Barrier_When_Prepare_Fail() { var dtmClient = new Mock(); - TestHelper.MockTransCallDtm(dtmClient, Constant.Request.OPERATION_PREPARE, false); + TestHelper.MockTransCallDtm(dtmClient, Constant.Request.OPERATION_PREPARE, true); var gid = "TestMsgNormal"; var msg = new Msg(dtmClient.Object, _branchBarrierFactory, gid); @@ -94,9 +94,7 @@ public async void DoAndSubmitDB_Should_Not_Call_Barrier_When_Prepare_Fail() var db = new MockDbConnection(); var mockBusiCall = new Mock>>(); - var res = await msg.DoAndSubmitDB(busi + "/query", db, x => Task.FromResult(true)); - - Assert.False(res); + await Assert.ThrowsAnyAsync(async () => await msg.DoAndSubmitDB(busi + "/query", db, x => Task.FromResult(true))); mockBusiCall.Verify(x => x.Invoke(It.IsAny()), Times.Never); } @@ -104,8 +102,8 @@ public async void DoAndSubmitDB_Should_Not_Call_Barrier_When_Prepare_Fail() public async void DoAndSubmitDB_Should_Succeed() { var dtmClient = new Mock(); - TestHelper.MockTransCallDtm(dtmClient, Constant.Request.OPERATION_PREPARE, true); - TestHelper.MockTransCallDtm(dtmClient, Constant.Request.OPERATION_SUBMIT, true); + TestHelper.MockTransCallDtm(dtmClient, Constant.Request.OPERATION_PREPARE, false); + TestHelper.MockTransCallDtm(dtmClient, Constant.Request.OPERATION_SUBMIT, false); var gid = "TestMsgNormal"; var msg = new Msg(dtmClient.Object, _branchBarrierFactory, gid); @@ -121,9 +119,8 @@ public async void DoAndSubmitDB_Should_Succeed() var mockBusiCall = new Mock>>(); mockBusiCall.Setup(x => x.Invoke(It.IsAny())).Returns(Task.FromResult(true)); - var res = await msg.DoAndSubmitDB(busi + "/query", db, mockBusiCall.Object); + await msg.DoAndSubmitDB(busi + "/query", db, mockBusiCall.Object); - Assert.True(res); mockBusiCall.Verify(x => x.Invoke(It.IsAny()), Times.Once); } @@ -131,8 +128,8 @@ public async void DoAndSubmitDB_Should_Succeed() public async void DoAndSubmitDB_Should_Abort_When_BusiCall_ThrowExeption_With_ResultFailure() { var dtmClient = new Mock(); - TestHelper.MockTransCallDtm(dtmClient, Constant.Request.OPERATION_PREPARE, true); - TestHelper.MockTransCallDtm(dtmClient, Constant.Request.OPERATION_ABORT, true); + TestHelper.MockTransCallDtm(dtmClient, Constant.Request.OPERATION_PREPARE, false); + TestHelper.MockTransCallDtm(dtmClient, Constant.Request.OPERATION_ABORT, false); var gid = "TestMsgNormal"; var msg = new Msg(dtmClient.Object, _branchBarrierFactory, gid); @@ -146,21 +143,19 @@ public async void DoAndSubmitDB_Should_Abort_When_BusiCall_ThrowExeption_With_Re db.Mocks.When(x => x.CommandText.Contains("select", StringComparison.OrdinalIgnoreCase)).ReturnsScalar(cmd => "rollback"); var mockBusiCall = new Mock>(); - mockBusiCall.Setup(x => x.Invoke(It.IsAny())).Throws(new Exception(Constant.ResultFailure)); - - var res = await msg.DoAndSubmitDB(busi + "/query", db, mockBusiCall.Object); + mockBusiCall.Setup(x => x.Invoke(It.IsAny())).Throws(new DtmFailureException()); - Assert.False(res); - dtmClient.Verify(x => x.TransCallDtm(It.IsAny(), It.IsAny(), Constant.Request.OPERATION_ABORT, It.IsAny()), Times.Once); + await Assert.ThrowsAsync(async () => await msg.DoAndSubmitDB(busi + "/query", db, mockBusiCall.Object)); + dtmClient.Verify(x => x.TransCallDtm(It.IsAny(), It.IsAny(), Constant.Request.OPERATION_ABORT, It.IsAny()), Times.Once); } [Fact] public async void DoAndSubmitDB_Should_QueryPrepared_When_BusiCall_ThrowExeption_Without_ResultFailure() { var dtmClient = new Mock(); - TestHelper.MockTransCallDtm(dtmClient, Constant.Request.OPERATION_PREPARE, true); - TestHelper.MockTransCallDtm(dtmClient, Constant.Request.OPERATION_ABORT, true); - TestHelper.MockTransCallDtm(dtmClient, Constant.Request.OPERATION_SUBMIT, true); + TestHelper.MockTransCallDtm(dtmClient, Constant.Request.OPERATION_PREPARE, false); + TestHelper.MockTransCallDtm(dtmClient, Constant.Request.OPERATION_ABORT, false); + TestHelper.MockTransCallDtm(dtmClient, Constant.Request.OPERATION_SUBMIT, false); TestHelper.MockTransRequestBranch(dtmClient, System.Net.HttpStatusCode.OK); var gid = "TestMsgNormal"; @@ -177,10 +172,8 @@ public async void DoAndSubmitDB_Should_QueryPrepared_When_BusiCall_ThrowExeption var mockBusiCall = new Mock>>(); mockBusiCall.Setup(x => x.Invoke(It.IsAny())).Throws(new Exception("ex")); - var res = await msg.DoAndSubmitDB(busi + "/query", db, mockBusiCall.Object); - - Assert.False(res); - dtmClient.Verify(x => x.TransRequestBranch(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); + await Assert.ThrowsAsync(async () => await msg.DoAndSubmitDB(busi + "/query", db, mockBusiCall.Object)); + dtmClient.Verify(x => x.TransRequestBranch(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); } public class MsgMockHttpMessageHandler : DelegatingHandler @@ -193,7 +186,7 @@ protected override async Task SendAsync(HttpRequestMessage { var str = await request.Content?.ReadAsStringAsync() ?? ""; - var transBase = System.Text.Json.JsonSerializer.Deserialize(str); + var transBase = System.Text.Json.JsonSerializer.Deserialize(str); Assert.Equal("TestMsgNormal", transBase.Gid); Assert.Equal("msg", transBase.TransType); diff --git a/src/Dtmcli.Tests/SagaTests.cs b/src/Dtmcli.Tests/SagaTests.cs index 1f1851d..9d9072a 100644 --- a/src/Dtmcli.Tests/SagaTests.cs +++ b/src/Dtmcli.Tests/SagaTests.cs @@ -1,4 +1,6 @@ -using Moq; +using DtmCommon; +using Moq; +using System; using System.Collections.Generic; using System.Net.Http; using System.Threading; @@ -47,6 +49,24 @@ public async void Submit_Should_Succeed() await sage.Submit(); } + [Fact] + public async void Submit_Should_ThrowException() + { + var dtmClient = new Mock(); + TestHelper.MockTransCallDtm(dtmClient, Constant.Request.OPERATION_SUBMIT, true); + + var gid = "TestSagaNormal"; + var saga = new Saga(dtmClient.Object, gid); + + var busi = "http://localhost:8081/api/busi"; + var req = new { Amount = 30 }; + + saga.Add(string.Concat(busi, "/TransOut"), string.Concat(busi, "/TransOutRevert"), req) + .Add(string.Concat(busi, "/TransIn"), string.Concat(busi, "/TransInRevert"), req); + + await Assert.ThrowsAnyAsync(async () => await saga.Submit()); + } + public class SageMockHttpMessageHandler : DelegatingHandler { public SageMockHttpMessageHandler() @@ -57,7 +77,7 @@ protected override async Task SendAsync(HttpRequestMessage { var str = await request.Content?.ReadAsStringAsync() ?? ""; - var transBase = System.Text.Json.JsonSerializer.Deserialize(str); + var transBase = System.Text.Json.JsonSerializer.Deserialize(str); /* { diff --git a/src/Dtmcli.Tests/ServiceCollectionExtensionsTests.cs b/src/Dtmcli.Tests/ServiceCollectionExtensionsTests.cs index 47f88e8..63071af 100644 --- a/src/Dtmcli.Tests/ServiceCollectionExtensionsTests.cs +++ b/src/Dtmcli.Tests/ServiceCollectionExtensionsTests.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.Configuration; +using DtmCommon; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using System.Collections.Generic; diff --git a/src/Dtmcli.Tests/TccTests.cs b/src/Dtmcli.Tests/TccTests.cs index 19505c5..ce54a73 100644 --- a/src/Dtmcli.Tests/TccTests.cs +++ b/src/Dtmcli.Tests/TccTests.cs @@ -2,6 +2,7 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using DtmCommon; using Microsoft.Extensions.Logging.Abstractions; using Moq; using Xunit; @@ -14,8 +15,8 @@ public class TccTests public async void Execute_Should_Submit() { var dtmClient = new Mock(); - TestHelper.MockTransCallDtm(dtmClient, Constant.Request.OPERATION_PREPARE, true); - TestHelper.MockTransRegisterBranch(dtmClient, Constant.Request.OPERATION_REGISTERBRANCH, true); + TestHelper.MockTransCallDtm(dtmClient, Constant.Request.OPERATION_PREPARE, false); + TestHelper.MockTransRegisterBranch(dtmClient, Constant.Request.OPERATION_REGISTERBRANCH, false); TestHelper.MockTransRequestBranch(dtmClient, System.Net.HttpStatusCode.OK); var gid = "tcc_gid"; @@ -33,9 +34,9 @@ public async void Execute_Should_Submit() public async void Execute_Should_Abort_When_CallBranch_With_Old_Ver_Exception() { var dtmClient = new Mock(); - TestHelper.MockTransCallDtm(dtmClient, Constant.Request.OPERATION_PREPARE, true); - TestHelper.MockTransCallDtm(dtmClient, Constant.Request.OPERATION_ABORT, true); - TestHelper.MockTransRegisterBranch(dtmClient, Constant.Request.OPERATION_REGISTERBRANCH, true); + TestHelper.MockTransCallDtm(dtmClient, Constant.Request.OPERATION_PREPARE, false); + TestHelper.MockTransCallDtm(dtmClient, Constant.Request.OPERATION_ABORT, false); + TestHelper.MockTransRegisterBranch(dtmClient, Constant.Request.OPERATION_REGISTERBRANCH, false); TestHelper.MockTransRequestBranch(dtmClient, System.Net.HttpStatusCode.OK, "FAILURE"); var gid = "tcc_gid"; @@ -47,16 +48,16 @@ public async void Execute_Should_Abort_When_CallBranch_With_Old_Ver_Exception() }); Assert.Empty(res); - dtmClient.Verify(x => x.TransCallDtm(It.IsAny(), It.IsAny(), Constant.Request.OPERATION_ABORT, It.IsAny()), Times.Once); + dtmClient.Verify(x => x.TransCallDtm(It.IsAny(), It.IsAny(), Constant.Request.OPERATION_ABORT, It.IsAny()), Times.Once); } [Fact] public async void Execute_Should_Abort_When_CallBranch_With_New_Ver_Exception() { var dtmClient = new Mock(); - TestHelper.MockTransCallDtm(dtmClient, Constant.Request.OPERATION_PREPARE, true); - TestHelper.MockTransCallDtm(dtmClient, Constant.Request.OPERATION_ABORT, true); - TestHelper.MockTransRegisterBranch(dtmClient, Constant.Request.OPERATION_REGISTERBRANCH, true); + TestHelper.MockTransCallDtm(dtmClient, Constant.Request.OPERATION_PREPARE, false); + TestHelper.MockTransCallDtm(dtmClient, Constant.Request.OPERATION_ABORT, false); + TestHelper.MockTransRegisterBranch(dtmClient, Constant.Request.OPERATION_REGISTERBRANCH, false); TestHelper.MockTransRequestBranch(dtmClient, System.Net.HttpStatusCode.BadRequest); var gid = "tcc_gid"; @@ -68,15 +69,15 @@ public async void Execute_Should_Abort_When_CallBranch_With_New_Ver_Exception() }); Assert.Empty(res); - dtmClient.Verify(x => x.TransCallDtm(It.IsAny(), It.IsAny(), Constant.Request.OPERATION_ABORT, It.IsAny()), Times.Once); + dtmClient.Verify(x => x.TransCallDtm(It.IsAny(), It.IsAny(), Constant.Request.OPERATION_ABORT, It.IsAny()), Times.Once); } [Fact] public async void Set_TransOptions_Should_Succeed() { var dtmClient = new Mock(); - TestHelper.MockTransCallDtm(dtmClient, Constant.Request.OPERATION_PREPARE, true); - TestHelper.MockTransRegisterBranch(dtmClient, Constant.Request.OPERATION_REGISTERBRANCH, true); + TestHelper.MockTransCallDtm(dtmClient, Constant.Request.OPERATION_PREPARE, false); + TestHelper.MockTransRegisterBranch(dtmClient, Constant.Request.OPERATION_REGISTERBRANCH, false); TestHelper.MockTransRequestBranch(dtmClient, System.Net.HttpStatusCode.OK); var gid = "tcc_gid"; diff --git a/src/Dtmcli.Tests/TestHelper.cs b/src/Dtmcli.Tests/TestHelper.cs index 8127c3f..09b555d 100644 --- a/src/Dtmcli.Tests/TestHelper.cs +++ b/src/Dtmcli.Tests/TestHelper.cs @@ -1,7 +1,10 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Net; using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using DtmCommon; using Microsoft.Extensions.DependencyInjection; using Moq; @@ -9,27 +12,43 @@ namespace Dtmcli.Tests { public class TestHelper { - public static void MockTransCallDtm(Mock mock, string op, bool result) + public static void MockTransCallDtm(Mock mock, string op, bool isEx) { - mock - .Setup(x => x.TransCallDtm(It.IsAny(), It.IsAny(), op, It.IsAny())) - .Returns(Task.FromResult(result)); + var setup = mock + .Setup(x => x.TransCallDtm(It.IsAny(), It.IsAny(), op, It.IsAny())); + + if (isEx) + { + setup.Throws(new Exception("")); + } + else + { + setup.Returns(Task.CompletedTask); + } } - public static void MockTransRegisterBranch(Mock mock, string op, bool result) + public static void MockTransRegisterBranch(Mock mock, string op, bool isEx) { - mock - .Setup(x => x.TransRegisterBranch(It.IsAny(), It.IsAny>(), op, It.IsAny())) - .Returns(Task.FromResult(result)); + var setup = mock + .Setup(x => x.TransRegisterBranch(It.IsAny(), It.IsAny>(), op, It.IsAny())); + + if (isEx) + { + setup.Throws(new Exception("")); + } + else + { + setup.Returns(Task.CompletedTask); + } } - public static void MockTransRequestBranch(Mock mock, System.Net.HttpStatusCode statusCode, string content = "content") + public static void MockTransRequestBranch(Mock mock, HttpStatusCode statusCode, string content = "content") { var httpRspMsg = new HttpResponseMessage(statusCode); httpRspMsg.Content = new StringContent(content); mock - .Setup(x => x.TransRequestBranch(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Setup(x => x.TransRequestBranch(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns(Task.FromResult(httpRspMsg)); } diff --git a/src/Dtmcli/Barrier/BranchBarrier.cs b/src/Dtmcli/Barrier/BranchBarrier.cs deleted file mode 100644 index 3fb6d04..0000000 --- a/src/Dtmcli/Barrier/BranchBarrier.cs +++ /dev/null @@ -1,198 +0,0 @@ -using Dapper; -using Dtmcli.DtmImp; -using Microsoft.Extensions.Logging; -using System; -using System.Collections.Generic; -using System.Data.Common; -using System.Threading.Tasks; - -namespace Dtmcli -{ - public class BranchBarrier - { - private static readonly string QueryPreparedSqlFormat = "select reason from {0} where gid=@gid and branch_id=@branch_id and op=@op and barrier_id=@barrier_id"; - private static readonly Dictionary TypeDict = new Dictionary() - { - { "cancel", "try" }, - { "compensate", "action" }, - }; - - public BranchBarrier(string transType, string gid, string branchID, string op, DtmOptions options, DbUtils utils, ILogger logger = null) - { - this.TransType = transType; - this.Gid = gid; - this.BranchID = branchID; - this.Op = op; - this.Logger = logger; - this.DtmOptions = options; - this.DbUtils = utils; - } - - internal DbUtils DbUtils { get; private set; } - - internal DtmOptions DtmOptions { get; private set; } - - internal ILogger Logger { get; private set; } - - public string TransType { get; set; } - - public string Gid { get; set; } - - public string BranchID { get; set; } - - public string Op { get; set; } - - public int BarrierID { get; set; } - - public async Task Call(DbConnection db, Func busiCall) - { - this.BarrierID = this.BarrierID + 1; - var bid = this.BarrierID.ToString().PadLeft(2, '0'); - - // check the connection state - if(db.State != System.Data.ConnectionState.Open) await db.OpenAsync(); - - // All should using async method, but netstandard2.0 do not support. -#if NETSTANDARD2_0 - var tx = db.BeginTransaction(); -#else - var tx = await db.BeginTransactionAsync(); -#endif - - try - { - var originOp = TypeDict.TryGetValue(this.Op, out var ot) ? ot : string.Empty; - - var (originAffected, oErr) = await DbUtils.InsertBarrier(db, this.TransType, this.Gid, this.BranchID, originOp, bid, this.Op, tx); - var (currentAffected, rErr) = await DbUtils.InsertBarrier(db, this.TransType, this.Gid, this.BranchID, this.Op, bid, this.Op, tx); - - Logger?.LogDebug("originAffected: {originAffected} currentAffected: {currentAffected}", originAffected, currentAffected); - - if (IsMsgRejected(rErr, this.Op, currentAffected)) - { - throw new DtmcliException(Constant.ResultDuplicated); - } - - var isNullCompensation = IsNullCompensation(this.Op, originAffected); - var isDuplicateOrPend = IsDuplicateOrPend(currentAffected); - - if (isNullCompensation || isDuplicateOrPend) - { - Logger?.LogInformation("Will not exec busiCall, isNullCompensation={isNullCompensation}, isDuplicateOrPend={isDuplicateOrPend}", isNullCompensation, isDuplicateOrPend); -#if NETSTANDARD2_0 - tx.Commit(); -#else - await tx.CommitAsync(); -#endif - - return; - } - - try - { - await busiCall.Invoke(tx); - } - catch (Exception ex) - { - throw new DtmcliException(ex.Message); - } - -#if NETSTANDARD2_0 - tx.Commit(); -#else - await tx.CommitAsync(); -#endif - } - catch (Exception ex) - { - Logger?.LogError(ex, "Call error, gid={gid}, trans_type={trans_type}", this.Gid, this.TransType); - -#if NETSTANDARD2_0 - tx.Rollback(); -#else - await tx.RollbackAsync(); -#endif - - throw; - } - } - - public async Task QueryPrepared(DbConnection db) - { - try - { - var tmp = await DbUtils.InsertBarrier( - db, - this.TransType, - this.Gid, - Constant.Barrier.MSG_BRANCHID, - Constant.Request.TYPE_MSG, - Constant.Barrier.MSG_BARRIER_ID, - Constant.Barrier.MSG_BARRIER_REASON); - } - catch (Exception ex) - { - Logger?.LogWarning(ex, "Insert Barrier error, gid={gid}", this.Gid); - return ex.Message; - } - - var reason = string.Empty; - - var sql = string.Format(QueryPreparedSqlFormat, DtmOptions.BarrierTableName); - - try - { - reason = await db.QueryFirstOrDefaultAsync( - sql, - new { gid = this.Gid, branch_id = Constant.Barrier.MSG_BRANCHID, op = Constant.Request.TYPE_MSG, barrier_id = Constant.Barrier.MSG_BARRIER_ID }); - - if (reason.Equals(Constant.Barrier.MSG_BARRIER_REASON)) return Constant.ErrFailure; - } - catch (Exception ex) - { - Logger?.LogWarning(ex, "Query Prepared error, gid={gid}", this.Gid); - return ex.Message; - } - - return string.Empty; - } - - /// - /// 空补偿 - /// - /// - /// - /// - private bool IsNullCompensation(string op, int originAffected) - => (op.Equals(Constant.BranchCancel) || op.Equals(Constant.Request.BRANCH_COMPENSATE)) && originAffected > 0; - - /// - /// 这个是重复请求或者悬挂 - /// - /// - /// - private bool IsDuplicateOrPend(int currentAffected) - => currentAffected == 0; - - /// - /// for msg's DoAndSubmit, repeated insert should be rejected. - /// - /// Barrier insert error - /// op - /// currentAffected - /// - private bool IsMsgRejected(string err, string op, int currentAffected) - => string.IsNullOrWhiteSpace(err) && op.Equals(Constant.Request.TYPE_MSG) && currentAffected == 0; - - public bool IsInValid() - { - return string.IsNullOrWhiteSpace(this.TransType) - || string.IsNullOrWhiteSpace(this.Gid) - || string.IsNullOrWhiteSpace(this.BranchID) - || string.IsNullOrWhiteSpace(this.Op); - } - - public override string ToString() - => $"transInfo: {TransType} {Gid} {BranchID} {Op}"; - } -} diff --git a/src/Dtmcli/Barrier/DefaultBranchBarrierFactory.cs b/src/Dtmcli/Barrier/DefaultBranchBarrierFactory.cs index 08e1fb6..8b98f4b 100644 --- a/src/Dtmcli/Barrier/DefaultBranchBarrierFactory.cs +++ b/src/Dtmcli/Barrier/DefaultBranchBarrierFactory.cs @@ -1,4 +1,4 @@ -using Dtmcli.DtmImp; +using DtmCommon; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -23,7 +23,7 @@ public BranchBarrier CreateBranchBarrier(string transType, string gid, string br var ti = new BranchBarrier(transType, gid, branchID, op, _options, _dbUtils, logger); - if (ti.IsInValid()) throw new DtmcliException($"invalid trans info: {ti.ToString()}"); + if (ti.IsInValid()) throw new DtmException($"invalid trans info: {ti.ToString()}"); return ti; } @@ -40,7 +40,7 @@ public BranchBarrier CreateBranchBarrier(Microsoft.AspNetCore.Http.IQueryCollect var ti = new BranchBarrier(transType, gid, branchID, op, _options, _dbUtils, logger); - if (ti.IsInValid()) throw new DtmcliException($"invalid trans info: {ti.ToString()}"); + if (ti.IsInValid()) throw new DtmException($"invalid trans info: {ti.ToString()}"); return ti; } diff --git a/src/Dtmcli/Barrier/IBranchBarrierFactory.cs b/src/Dtmcli/Barrier/IBranchBarrierFactory.cs index 7d19c3b..cd6dd09 100644 --- a/src/Dtmcli/Barrier/IBranchBarrierFactory.cs +++ b/src/Dtmcli/Barrier/IBranchBarrierFactory.cs @@ -1,11 +1,10 @@ -using Microsoft.Extensions.Logging; +using DtmCommon; +using Microsoft.Extensions.Logging; namespace Dtmcli { - public interface IBranchBarrierFactory + public interface IBranchBarrierFactory : IBaseBarrierFactory { - BranchBarrier CreateBranchBarrier(string transType, string gid, string branchID, string op, ILogger logger = null); - #if NET5_0_OR_GREATER BranchBarrier CreateBranchBarrier(Microsoft.AspNetCore.Http.IQueryCollection query, ILogger logger = null); #endif diff --git a/src/Dtmcli/Constant.cs b/src/Dtmcli/Constant.cs index 52d3e57..6da7f01 100644 --- a/src/Dtmcli/Constant.cs +++ b/src/Dtmcli/Constant.cs @@ -4,71 +4,7 @@ internal class Constant { internal static readonly string DtmClientHttpName = "dtmClient"; internal static readonly string BranchClientHttpName = "branchClient"; - - /// - /// status for global/branch trans status. - /// - internal static readonly string StatusPrepared = "prepared"; - - /// - /// status for global trans status. - /// - internal static readonly string StatusSubmitted = "submitted"; - - /// - /// status for global/branch trans status. - /// - internal static readonly string StatusSucceed = "succeed"; - - /// - /// status for global/branch trans status. - /// - internal static readonly string StatusFailed = "failed"; - - /// - /// status for global trans status. - /// - internal static readonly string StatusAborting = "aborting"; - - /// - /// branch type for TCC - /// - internal static readonly string BranchTry = "try"; - - /// - /// branch type for TCC - /// - internal static readonly string BranchConfirm = "confirm"; - - /// - /// branch type for TCC - /// - internal static readonly string BranchCancel = "cancel"; - - /// - /// branch type for XA - /// - internal static readonly string BranchCommit = "commit"; - - /// - /// branch type for XA - /// - internal static readonly string BranchRollback = "rollback"; - - internal static readonly string ErrFailure = "FAILURE"; - - internal static readonly string ResultFailure = "FAILURE"; - internal static readonly string ResultSuccess = "SUCCESS"; - internal static readonly string ResultOngoing = "ONGOING"; - - /// - /// error of DUPLICATED for only msg - /// if QueryPrepared executed before call. then DoAndSubmit return this error - /// - internal static readonly string ResultDuplicated = "DUPLICATED"; - - internal static readonly int FailureStatusCode = 400; - + internal class Request { internal static readonly string CONTENT_TYPE = "application/json"; @@ -91,6 +27,8 @@ internal class Request internal static readonly string OP = "op"; + internal static readonly string DTM = "dtm"; + internal static readonly string CODE = "code"; internal static readonly string MESSAGE = "message"; @@ -115,32 +53,7 @@ internal class Request /// internal static readonly string BRANCH_COMPENSATE = "compensate"; - internal static readonly string TYPE_TCC = "tcc"; - - internal static readonly string TYPE_SAGA = "saga"; - - internal static readonly string TYPE_MSG = "msg"; - internal static readonly string URL_NewGid = "/api/dtmsvr/newGid"; - } - - internal class Barrier - { - internal static readonly string TABLE_NAME = "dtm_barrier.barrier"; - - internal static readonly string DBTYPE_MYSQL = "mysql"; - - internal static readonly string DBTYPE_POSTGRES = "postgres"; - - internal static readonly string DBTYPE_SQLSERVER = "sqlserver"; - - internal static readonly string PG_CONSTRAINT = "uniq_barrier"; - - internal static readonly string MSG_BARRIER_REASON = "rollback"; - - internal static readonly string MSG_BRANCHID = "00"; - - internal static readonly string MSG_BARRIER_ID = "01"; - } + } } } diff --git a/src/Dtmcli/DtmClient.cs b/src/Dtmcli/DtmClient.cs index 625f196..1e5de7f 100644 --- a/src/Dtmcli/DtmClient.cs +++ b/src/Dtmcli/DtmClient.cs @@ -1,10 +1,10 @@ -using Dtmcli.DtmImp; +using DtmCommon; using Microsoft.Extensions.Options; -using System; using System.Collections.Generic; -using System.Net; using System.Net.Http; +using System.Net.Http.Headers; using System.Text.Json; +using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; @@ -33,43 +33,28 @@ public DtmClient(IHttpClientFactory httpClientFactory, IOptions opti public async Task GenGid(CancellationToken cancellationToken) { var client = _httpClientFactory.CreateClient(Constant.DtmClientHttpName); - var response = await client.GetAsync($"{_dtmOptions.DtmUrl.TrimEnd(Slash)}{Constant.Request.URL_NewGid}", cancellationToken).ConfigureAwait(false); - - if (response.StatusCode != HttpStatusCode.OK) - { - throw new Exception($"bad http response status: {response.StatusCode}"); - } - var content = await response.Content.ReadAsStringAsync(); + var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + DtmImp.Utils.CheckStatus(response.StatusCode, content); var dtmgid = JsonSerializer.Deserialize(content, _jsonOptions); return dtmgid.Gid; } - private void CheckStatus(HttpStatusCode status, DtmResult dtmResult) - { - if (status != HttpStatusCode.OK || dtmResult.Success != true) - { - throw new Exception($"http response status: {status}, Message :{ dtmResult.Message }"); - } - } - - public async Task TransCallDtm(TransBase tb, object body, string operation, CancellationToken cancellationToken) + public async Task TransCallDtm(TransBase tb, object body, string operation, CancellationToken cancellationToken) { var url = string.Concat(_dtmOptions.DtmUrl.TrimEnd(Slash), Constant.Request.URLBASE_PREFIX, operation); var content = new StringContent(JsonSerializer.Serialize(body, _jsonOptions)); - content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(Constant.Request.CONTENT_TYPE); + content.Headers.ContentType = new MediaTypeHeaderValue(Constant.Request.CONTENT_TYPE); var client = _httpClientFactory.CreateClient(Constant.DtmClientHttpName); var response = await client.PostAsync(url, content, cancellationToken).ConfigureAwait(false); var dtmcontent = await response.Content.ReadAsStringAsync().ConfigureAwait(false); - var dtmResult = JsonSerializer.Deserialize(dtmcontent, _jsonOptions); - CheckStatus(response.StatusCode, dtmResult); - return dtmResult.Success; + DtmImp.Utils.CheckStatus(response.StatusCode, dtmcontent); } - public async Task TransRegisterBranch(TransBase tb, Dictionary added, string operation, CancellationToken cancellationToken) + public async Task TransRegisterBranch(TransBase tb, Dictionary added, string operation, CancellationToken cancellationToken) { var dict = new Dictionary { @@ -82,7 +67,7 @@ public async Task TransRegisterBranch(TransBase tb, Dictionary TransRequestBranch(TransBase tb, HttpMethod method, object body, string branchID, string op, string url, CancellationToken cancellationToken) @@ -109,12 +94,37 @@ public async Task TransRequestBranch(TransBase tb, HttpMeth if (body != null) { var content = new StringContent(JsonSerializer.Serialize(body, _jsonOptions)); - content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(Constant.Request.CONTENT_TYPE); + content.Headers.ContentType = new MediaTypeHeaderValue(Constant.Request.CONTENT_TYPE); httpRequestMsg.Content = content; } var response = await client.SendAsync(httpRequestMsg, cancellationToken).ConfigureAwait(false); return response; } + +#if NET5_0_OR_GREATER + public TransBase TransBaseFromQuery(Microsoft.AspNetCore.Http.IQueryCollection query) + { + _ = query.TryGetValue(Constant.Request.BRANCH_ID, out var branchId); + _ = query.TryGetValue(Constant.Request.GID, out var gid); + _ = query.TryGetValue(Constant.Request.OP, out var op); + _ = query.TryGetValue(Constant.Request.TRANS_TYPE, out var transType); + _ = query.TryGetValue(Constant.Request.DTM, out var dtm); + + var tb = TransBase.NewTransBase(gid, transType, dtm, branchId); + tb.Op = op; + + return tb; + } +#endif + + public class DtmGid + { + [JsonPropertyName("gid")] + public string Gid { get; set; } + + [JsonPropertyName("dtm_result")] + public string Dtm_Result { get; set; } + } } } diff --git a/src/Dtmcli/DtmImp/BranchIDGen.cs b/src/Dtmcli/DtmImp/BranchIDGen.cs deleted file mode 100644 index 156f9a1..0000000 --- a/src/Dtmcli/DtmImp/BranchIDGen.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; - -namespace Dtmcli.DtmImp -{ - public class BranchIDGen - { - private static readonly int MAX_BRANCH_ID = 99; - - private static readonly int LENGTH = 20; - - public string BranchID { get; private set; } - public int SubBranchID { get; private set; } - - public BranchIDGen(string branchID = "") - { - this.BranchID = branchID; - this.SubBranchID = 0; - } - - public string NewSubBranchID() - { - if (this.SubBranchID >= MAX_BRANCH_ID) - { - throw new ArgumentException("branch id is larger than 99"); - } - if (this.BranchID.Length > LENGTH) - { - throw new ArgumentException("total branch id is longer than 20"); - } - this.SubBranchID = this.SubBranchID + 1; - - return CurrentSubBranchID(); - } - - public string CurrentSubBranchID() - => string.Concat(this.BranchID, this.SubBranchID.ToString().PadLeft(2, '0')); - } -} diff --git a/src/Dtmcli/DtmImp/DbUtils.cs b/src/Dtmcli/DtmImp/DbUtils.cs deleted file mode 100644 index d01212c..0000000 --- a/src/Dtmcli/DtmImp/DbUtils.cs +++ /dev/null @@ -1,48 +0,0 @@ -using Dapper; -using Microsoft.Extensions.Options; -using System; -using System.Data.Common; -using System.Threading.Tasks; - -namespace Dtmcli.DtmImp -{ - public class DbUtils - { - private readonly DtmOptions _options; - private readonly DbSpecialDelegate _specialDelegate; - - public DbUtils(IOptions optionsAccs, DbSpecialDelegate specialDelegate) - { - _options = optionsAccs.Value; - _specialDelegate = specialDelegate; - } - - public async Task<(int, string)> InsertBarrier(DbConnection db, string transType, string gid, string branchID, string op, string barrierID, string reason, DbTransaction tx = null) - { - if (db == null) return (-1, string.Empty); - if (string.IsNullOrWhiteSpace(op)) return (0, string.Empty); - - int result = 0; - string err = string.Empty; - - try - { - var str = string.Concat(_options.BarrierTableName, "(trans_type, gid, branch_id, op, barrier_id, reason) values(@trans_type,@gid,@branch_id,@op,@barrier_id,@reason)"); - var sql = _specialDelegate.GetDbSpecial().GetInsertIgnoreTemplate(str, Constant.Barrier.PG_CONSTRAINT); - - sql = _specialDelegate.GetDbSpecial().GetPlaceHoldSQL(sql); - - result = await db.ExecuteAsync( - sql, - new { trans_type = transType, gid = gid, branch_id = branchID, op = op, barrier_id = barrierID, reason = reason }, - transaction: tx); - } - catch (Exception ex) - { - err = ex.Message; - } - - return (result, err); - } - } -} diff --git a/src/Dtmcli/DtmImp/IDbSpecial.cs b/src/Dtmcli/DtmImp/IDbSpecial.cs deleted file mode 100644 index 39dcd34..0000000 --- a/src/Dtmcli/DtmImp/IDbSpecial.cs +++ /dev/null @@ -1,125 +0,0 @@ -using Microsoft.Extensions.Options; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Dtmcli.DtmImp -{ - public interface IDbSpecial - { - string Name { get; } - - string GetPlaceHoldSQL(string sql); - - string GetInsertIgnoreTemplate(string tableAndValues, string pgConstraint); - - string GetXaSQL(string command, string xid); - } - - public class MysqlDBSpecial : IDbSpecial - { - public string Name => "mysql"; - - public string GetInsertIgnoreTemplate(string tableAndValues, string pgConstraint) - => string.Format("insert ignore into {0}", tableAndValues); - - public string GetPlaceHoldSQL(string sql) - => sql; - - public string GetXaSQL(string command, string xid) - => string.Format("xa {0} '{1}'", command, xid); - } - - public class PostgresDBSpecial : IDbSpecial - { - public string Name => "postgres"; - - public string GetInsertIgnoreTemplate(string tableAndValues, string pgConstraint) - => string.Format("insert into {0} on conflict ON CONSTRAINT {1} do nothing", tableAndValues, pgConstraint); - - public string GetPlaceHoldSQL(string sql) - => sql; - - public string GetXaSQL(string command, string xid) - { - var dict = new System.Collections.Generic.Dictionary - { - { "end", "" }, - { "start", "begin" }, - { "prepare", $"prepare transaction '{xid}'" }, - { "commit", $"commit prepared '{xid}'" }, - { "rollback", $"rollback prepared '{xid}'" }, - }; - - return dict.TryGetValue(command, out var sql) ? sql : string.Empty; - } - } - - public class SqlServerDBSpecial : IDbSpecial - { - public string Name => "sqlserver"; - - /* - -IF NOT EXISTS(SELECT * FROM sys.databases WHERE name = 'dtm_barrier') -BEGIN - CREATE DATABASE dtm_barrier - USE dtm_barrier -END - -GO - -IF EXISTS (SELECT * FROM sysobjects WHERE id = object_id(N’[dbo].[barrier]’) and OBJECTPROPERTY(id, N’IsUserTable’) = 1) -BEGIN - DROP TABLE [dbo].[barrier] -END - -GO - -CREATE TABLE [dbo].[barrier] -( - [id] bigint NOT NULL IDENTITY(1,1) PRIMARY KEY, - [trans_type] varchar(45) NOT NULL DEFAULT(''), - [gid] varchar(128) NOT NULL DEFAULT(''), - [branch_id] varchar(128) NOT NULL DEFAULT(''), - [op] varchar(45) NOT NULL DEFAULT(''), - [barrier_id] varchar(45) NOT NULL DEFAULT(''), - [reason] varchar(45) NOT NULL DEFAULT(''), - [create_time] datetime NOT NULL DEFAULT(getdate()) , - [update_time] datetime NOT NULL DEFAULT(getdate()) -) - -GO - -CREATE UNIQUE INDEX[ix_uniq_barrier] ON[dbo].[barrier] - ([gid] ASC, [branch_id] ASC, [op] ASC, [barrier_id] ASC) -WITH(IGNORE_DUP_KEY = ON) - -GO - */ - public string GetInsertIgnoreTemplate(string tableAndValues, string pgConstraint) - => string.Format("insert into {0}", tableAndValues); - - public string GetPlaceHoldSQL(string sql) - => sql; - - public string GetXaSQL(string command, string xid) - => throw new DtmcliException("not support xa now!!!"); - } - - public class DbSpecialDelegate - { - private readonly IDbSpecial _special; - - public DbSpecialDelegate(IEnumerable specials, IOptions optionsAccs) - { - var dbSpecial = specials.FirstOrDefault(x => x.Name.Equals(optionsAccs.Value.DBType)); - - if (dbSpecial == null) throw new DtmcliException($"unknown db type '{optionsAccs.Value.DBType}'"); - - _special = dbSpecial; - } - - public IDbSpecial GetDbSpecial() => _special; - } -} diff --git a/src/Dtmcli/DtmImp/TransBase.cs b/src/Dtmcli/DtmImp/TransBase.cs deleted file mode 100644 index bef7f27..0000000 --- a/src/Dtmcli/DtmImp/TransBase.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System.Collections.Generic; -using System.Text.Json.Serialization; - -namespace Dtmcli.DtmImp -{ - public class TransBase - { - [JsonPropertyName("gid")] - public string Gid { get; set; } - - [JsonPropertyName("trans_type")] - public string TransType { get; set; } - - [JsonPropertyName("custom_data")] - public string CustomData { get; set; } - - [JsonPropertyName("wait_result")] - public bool WaitResult { get; set; } - - [JsonPropertyName("timeout_to_fail")] - public long TimeoutToFail { get; set; } - - [JsonPropertyName("retry_interval")] - public long RetryInterval { get; set; } - - [JsonPropertyName("passthrough_headers")] - public List PassthroughHeaders { get; set; } - - [JsonPropertyName("branch_headers")] - public Dictionary BranchHeaders { get; set; } - - /// - /// use in MSG/SAGA - /// - [JsonPropertyName("steps")] - public List> Steps { get; set; } - - /// - /// used in MSG/SAGA - /// - [JsonPropertyName("payloads")] - public List Payloads { get; set; } - - [JsonIgnore] - public string BinPayloads { get; set; } - - /// - /// used in XA/TCC - /// - [JsonIgnore] - public BranchIDGen BranchIDGen { get; set; } - - /// - /// used in XA/TCC - /// - [JsonIgnore] - public string Op { get; set; } - - /// - /// used in MSG - /// - [JsonPropertyName("query_prepared")] - public string QueryPrepared { get; set; } - - public static TransBase NewTransBase(string gid, string transType, string branchID) - { - return new TransBase - { - Gid = gid, - TransType = transType, - BranchIDGen = new BranchIDGen(branchID), - }; - } - } -} diff --git a/src/Dtmcli/DtmImp/Utils.cs b/src/Dtmcli/DtmImp/Utils.cs new file mode 100644 index 0000000..416eb20 --- /dev/null +++ b/src/Dtmcli/DtmImp/Utils.cs @@ -0,0 +1,43 @@ +using DtmCommon; +using System; +using System.Net; +using System.Net.Http; +using System.Threading.Tasks; + +namespace Dtmcli.DtmImp +{ + public static class Utils + { + private static readonly int StatusTooEarly = 425; + private static readonly string CheckStatusMsgFormat = "http response status: {status}, Message :{dtmResult}"; + + public static async Task RespAsErrorCompatible(HttpResponseMessage resp) + { + var str = await resp.Content?.ReadAsStringAsync() ?? string.Empty; + + // System.Net.HttpStatusCode do not contain StatusTooEarly + if ((int)resp.StatusCode == StatusTooEarly || str.Contains(DtmCommon.Constant.ResultOngoing)) + { + return new DtmException(DtmCommon.Constant.ResultOngoing); + } + else if (resp.StatusCode == HttpStatusCode.Conflict || str.Contains(DtmCommon.Constant.ResultFailure)) + { + return new DtmException(DtmCommon.Constant.ResultFailure); + } + else if (resp.StatusCode != HttpStatusCode.OK) + { + return new Exception(str); + } + + return null; + } + + public static void CheckStatus(HttpStatusCode status, string dtmResult) + { + if (status != HttpStatusCode.OK || dtmResult.Contains(DtmCommon.Constant.ResultFailure)) + { + throw new DtmException(string.Format(CheckStatusMsgFormat, status, dtmResult)); + } + } + } +} diff --git a/src/Dtmcli/DtmOptions.cs b/src/Dtmcli/DtmOptions.cs deleted file mode 100644 index e8e01a5..0000000 --- a/src/Dtmcli/DtmOptions.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; - -namespace Dtmcli -{ - public class DtmOptions - { - public string DtmUrl { get; set; } - - /// - /// dtm server http request timeout in milliseconds, default 100,000 milliseconds(100s) - /// - public int DtmHttpTimeout { get; set; } = 100 * 1000; - - /// - /// branch http request timeout in milliseconds, default 100,000 milliseconds(100s) - /// - public int BranchHttpTimeout { get; set; } = 100 * 1000; - - public string DBType { get; set; } = "mysql"; - - public string BarrierTableName { get; set; } = "dtm_barrier.barrier"; - } -} \ No newline at end of file diff --git a/src/Dtmcli/Dtmcli.csproj b/src/Dtmcli/Dtmcli.csproj index 6ee60df..b1323f4 100644 --- a/src/Dtmcli/Dtmcli.csproj +++ b/src/Dtmcli/Dtmcli.csproj @@ -1,7 +1,7 @@ - netstandard2.0;netstandard2.1;net5.0;net6.0 + netstandard2.1;net5.0;net6.0 https://github.com/dtm-labs/dtmcli-csharp https://github.com/dtm-labs/dtmcli-csharp Dtmcli @@ -16,29 +16,15 @@ - - + - - - - + - - - - - - - - - - - - + + diff --git a/src/Dtmcli/DtmcliException.cs b/src/Dtmcli/DtmcliException.cs deleted file mode 100644 index a2d317a..0000000 --- a/src/Dtmcli/DtmcliException.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace Dtmcli -{ - public class DtmcliException : Exception - { - public DtmcliException(string message) - : base(message) - { - } - } -} diff --git a/src/Dtmcli/IDtmClient.cs b/src/Dtmcli/IDtmClient.cs index 519672a..32d261c 100644 --- a/src/Dtmcli/IDtmClient.cs +++ b/src/Dtmcli/IDtmClient.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using DtmCommon; +using System.Collections.Generic; using System.Net.Http; using System.Threading; using System.Threading.Tasks; @@ -9,10 +10,14 @@ public interface IDtmClient { Task GenGid(CancellationToken cancellationToken); - Task TransCallDtm(DtmImp.TransBase tb, object body, string operation, CancellationToken cancellationToken); + Task TransCallDtm(TransBase tb, object body, string operation, CancellationToken cancellationToken); - Task TransRegisterBranch(DtmImp.TransBase tb, Dictionary added, string operation, CancellationToken cancellationToken); + Task TransRegisterBranch(TransBase tb, Dictionary added, string operation, CancellationToken cancellationToken); - Task TransRequestBranch(DtmImp.TransBase tb, HttpMethod method, object body, string branchID, string op, string url, CancellationToken cancellationToken); + Task TransRequestBranch(TransBase tb, HttpMethod method, object body, string branchID, string op, string url, CancellationToken cancellationToken); + +#if NET5_0_OR_GREATER + TransBase TransBaseFromQuery(Microsoft.AspNetCore.Http.IQueryCollection query); +#endif } } diff --git a/src/Dtmcli/Msg/Msg.cs b/src/Dtmcli/Msg/Msg.cs index 12ab7fd..9594b85 100644 --- a/src/Dtmcli/Msg/Msg.cs +++ b/src/Dtmcli/Msg/Msg.cs @@ -1,4 +1,5 @@ -using System; +using DtmCommon; +using System; using System.Collections.Generic; using System.Data.Common; using System.Net.Http; @@ -10,7 +11,7 @@ namespace Dtmcli { public class Msg { - private readonly DtmImp.TransBase _transBase; + private readonly TransBase _transBase; private readonly IDtmClient _dtmClient; private readonly IBranchBarrierFactory _branchBarrierFactory; @@ -18,7 +19,7 @@ public Msg(IDtmClient dtmHttpClient, IBranchBarrierFactory branchBarrierFactory, { this._dtmClient = dtmHttpClient; this._branchBarrierFactory = branchBarrierFactory; - this._transBase = DtmImp.TransBase.NewTransBase(gid, Constant.Request.TYPE_MSG, string.Empty); + this._transBase = TransBase.NewTransBase(gid, DtmCommon.Constant.TYPE_MSG, string.Empty, string.Empty); } public Msg Add(string action, object postData) @@ -31,35 +32,33 @@ public Msg Add(string action, object postData) return this; } - public async Task Prepare(string queryPrepared, CancellationToken cancellationToken = default) + public async Task Prepare(string queryPrepared, CancellationToken cancellationToken = default) { this._transBase.QueryPrepared = !string.IsNullOrWhiteSpace(queryPrepared)? queryPrepared : this._transBase.QueryPrepared; - return await this._dtmClient.TransCallDtm(this._transBase, this._transBase, Constant.Request.OPERATION_PREPARE, cancellationToken); + await this._dtmClient.TransCallDtm(this._transBase, this._transBase, Constant.Request.OPERATION_PREPARE, cancellationToken); } - public async Task Submit(CancellationToken cancellationToken = default) + public async Task Submit(CancellationToken cancellationToken = default) { - return await this._dtmClient.TransCallDtm(this._transBase, this._transBase, Constant.Request.OPERATION_SUBMIT, cancellationToken); + await this._dtmClient.TransCallDtm(this._transBase, this._transBase, Constant.Request.OPERATION_SUBMIT, cancellationToken); } - public async Task DoAndSubmitDB(string queryPrepared, DbConnection db, Func busiCall, CancellationToken cancellationToken = default) + public async Task DoAndSubmitDB(string queryPrepared, DbConnection db, Func busiCall, CancellationToken cancellationToken = default) { - return await this.DoAndSubmit(queryPrepared, async bb => + await this.DoAndSubmit(queryPrepared, async bb => { await bb.Call(db, busiCall); }, cancellationToken); } - public async Task DoAndSubmit(string queryPrepared, Func busiCall, CancellationToken cancellationToken = default) + public async Task DoAndSubmit(string queryPrepared, Func busiCall, CancellationToken cancellationToken = default) { - var bb = _branchBarrierFactory.CreateBranchBarrier(this._transBase.TransType, this._transBase.Gid, Constant.Barrier.MSG_BRANCHID, Constant.Request.TYPE_MSG); + var bb = _branchBarrierFactory.CreateBranchBarrier(this._transBase.TransType, this._transBase.Gid, DtmCommon.Constant.Barrier.MSG_BRANCHID, DtmCommon.Constant.TYPE_MSG); - if (bb.IsInValid()) throw new DtmcliException($"invalid trans info: {bb.ToString()}"); + if (bb.IsInValid()) throw new DtmException($"invalid trans info: {bb.ToString()}"); - var flag = await this.Prepare(queryPrepared, cancellationToken); - - if (!flag) return false; + await this.Prepare(queryPrepared, cancellationToken); Exception errb = null; @@ -73,46 +72,23 @@ public async Task DoAndSubmit(string queryPrepared, Func RespAsErrorCompatible(HttpResponseMessage resp) - { - var str = await resp.Content?.ReadAsStringAsync() ?? string.Empty; - - // System.Net.HttpStatusCode do not contain StatusTooEarly - if ((int)resp.StatusCode == 425 || str.Contains(Constant.ResultOngoing)) - { - return new DtmcliException(Constant.ResultOngoing); - } - else if (resp.StatusCode == System.Net.HttpStatusCode.Conflict || str.Contains(Constant.ResultFailure)) - { - return new DtmcliException(Constant.ResultFailure); - } - else if (resp.StatusCode != System.Net.HttpStatusCode.OK) - { - return new Exception(str); + await this.Submit(cancellationToken); } - return null; + if (errb != null) throw errb; } /// diff --git a/src/Dtmcli/Saga/Saga.cs b/src/Dtmcli/Saga/Saga.cs index 9062c7e..5f88d3a 100644 --- a/src/Dtmcli/Saga/Saga.cs +++ b/src/Dtmcli/Saga/Saga.cs @@ -1,10 +1,9 @@ -using System.Collections.Generic; +using DtmCommon; +using System.Collections.Generic; using System.Text.Json; using System.Threading; using System.Threading.Tasks; -[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Dtmcli.Tests")] - namespace Dtmcli { public class Saga @@ -12,13 +11,13 @@ public class Saga private bool _concurrent = false; private Dictionary> _orders = new Dictionary>(); - private readonly DtmImp.TransBase _transBase; + private readonly TransBase _transBase; private readonly IDtmClient _dtmClient; public Saga(IDtmClient dtmHttpClient, string gid) { this._dtmClient = dtmHttpClient; - this._transBase = DtmImp.TransBase.NewTransBase(gid, Constant.Request.TYPE_SAGA, string.Empty); + this._transBase = TransBase.NewTransBase(gid, DtmCommon.Constant.TYPE_SAGA, string.Empty, string.Empty); } public Saga Add(string action, string compensate, object postData) @@ -43,17 +42,17 @@ public Saga EnableConcurrent() return this; } - public async Task Submit(CancellationToken cancellationToken = default) + public async Task Submit(CancellationToken cancellationToken = default) { if (this._concurrent) { this._transBase.CustomData = JsonSerializer.Serialize(new { orders = this._orders, concurrent = this._concurrent }); } - return await _dtmClient.TransCallDtm(this._transBase, this._transBase, Constant.Request.OPERATION_SUBMIT, cancellationToken).ConfigureAwait(false); + await _dtmClient.TransCallDtm(this._transBase, this._transBase, Constant.Request.OPERATION_SUBMIT, cancellationToken).ConfigureAwait(false); } - internal DtmImp.TransBase GetTransBase() => this._transBase; + internal TransBase GetTransBase() => this._transBase; /// /// Enable wait result for trans diff --git a/src/Dtmcli/ServiceCollectionExtensions.cs b/src/Dtmcli/ServiceCollectionExtensions.cs index 0fd8fad..7730ebe 100644 --- a/src/Dtmcli/ServiceCollectionExtensions.cs +++ b/src/Dtmcli/ServiceCollectionExtensions.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.Configuration; +using DtmCommon; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using System; @@ -25,13 +26,13 @@ public static IServiceCollection AddDtmcli(this IServiceCollection services, Act public static IServiceCollection AddDtmcli(this IServiceCollection services, IConfiguration configuration, string sectionName = "dtm") { services.Configure(configuration.GetSection(sectionName)); - - var op=configuration.GetSection(sectionName).Get() ?? new DtmOptions(); + + var op = configuration.GetSection(sectionName).Get() ?? new DtmOptions(); return AddDtmcliCore(services, op); } - private static IServiceCollection AddDtmcliCore(IServiceCollection services,DtmOptions options) + private static IServiceCollection AddDtmcliCore(IServiceCollection services, DtmOptions options) { AddHttpClient(services, options); AddDtmCore(services); @@ -39,16 +40,16 @@ private static IServiceCollection AddDtmcliCore(IServiceCollection services,DtmO return services; } - private static void AddHttpClient(IServiceCollection services,DtmOptions options) + private static void AddHttpClient(IServiceCollection services, DtmOptions options) { services.AddHttpClient(Constant.DtmClientHttpName, client => { client.DefaultRequestHeaders.Add("Accept", "application/json"); - client.Timeout = TimeSpan.FromMilliseconds(options.DtmHttpTimeout); + client.Timeout = TimeSpan.FromMilliseconds(options.DtmTimeout); }); services.AddHttpClient(Constant.BranchClientHttpName, client => { - client.Timeout = TimeSpan.FromMilliseconds(options.BranchHttpTimeout); + client.Timeout = TimeSpan.FromMilliseconds(options.BranchTimeout); }); } @@ -59,12 +60,7 @@ private static void AddDtmCore(IServiceCollection services) services.AddSingleton(); services.AddSingleton(); - // barrier database relate - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); + DtmCommon.ServiceCollectionExtensions.AddDtmCommon(services); // barrier factory services.AddSingleton(); diff --git a/src/Dtmcli/Tcc/DtmGid.cs b/src/Dtmcli/Tcc/DtmGid.cs deleted file mode 100644 index d291875..0000000 --- a/src/Dtmcli/Tcc/DtmGid.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Dtmcli -{ - public class DtmGid - { - [JsonPropertyName("gid")] - public string Gid { get; set; } - - [JsonPropertyName("dtm_result")] - public string Dtm_Result { get; set; } - } -} diff --git a/src/Dtmcli/Tcc/DtmResult.cs b/src/Dtmcli/Tcc/DtmResult.cs deleted file mode 100644 index 5e0fe60..0000000 --- a/src/Dtmcli/Tcc/DtmResult.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Dtmcli -{ - public class DtmResult - { - [JsonPropertyName("dtm_result")] - public string Dtm_Result { get; set; } - - [JsonPropertyName("message")] - public string Message { get; set; } - - public bool Success - { - get - { - return Dtm_Result.ToUpper() == "SUCCESS"; - } - } - } -} diff --git a/src/Dtmcli/Tcc/Tcc.cs b/src/Dtmcli/Tcc/Tcc.cs index 6e06968..f7ba4c3 100644 --- a/src/Dtmcli/Tcc/Tcc.cs +++ b/src/Dtmcli/Tcc/Tcc.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using DtmCommon; +using System.Collections.Generic; using System.Text.Json; using System.Threading; using System.Threading.Tasks; @@ -7,10 +8,12 @@ namespace Dtmcli { public class Tcc { - private readonly DtmImp.TransBase _transBase; + private static readonly int FailureStatusCode = 400; + + private readonly TransBase _transBase; private readonly IDtmClient _dtmClient; - public Tcc(IDtmClient dtmHttpClient, DtmImp.TransBase transBase) + public Tcc(IDtmClient dtmHttpClient, TransBase transBase) { this._dtmClient = dtmHttpClient; this._transBase = transBase; @@ -33,25 +36,25 @@ public async Task CallBranch(object body, string tryUrl, string confirmU System.Net.Http.HttpMethod.Post, body, branchId, - Constant.BranchTry, + Constant.Request.TRY, tryUrl, cancellationToken).ConfigureAwait(false); var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false); // call branch error should throw exception - var isOldVerException = response.StatusCode == System.Net.HttpStatusCode.OK && content.Contains(Constant.ErrFailure); - var isNewVerException = (int)response.StatusCode >= Constant.FailureStatusCode; + var isOldVerException = response.StatusCode == System.Net.HttpStatusCode.OK && content.Contains(DtmCommon.Constant.ResultFailure); + var isNewVerException = (int)response.StatusCode >= FailureStatusCode; if (isOldVerException || isNewVerException) { - throw new DtmcliException("An exception occurred when CallBranch"); + throw new DtmException("An exception occurred when CallBranch"); } return content; } - internal DtmImp.TransBase GetTransBase() => _transBase; + internal TransBase GetTransBase() => _transBase; /// /// Enable wait result for trans diff --git a/src/Dtmcli/Tcc/TccGlobalTransaction.cs b/src/Dtmcli/Tcc/TccGlobalTransaction.cs index de4eeec..7e6f982 100644 --- a/src/Dtmcli/Tcc/TccGlobalTransaction.cs +++ b/src/Dtmcli/Tcc/TccGlobalTransaction.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.Logging; +using DtmCommon; +using Microsoft.Extensions.Logging; using System; using System.Threading; using System.Threading.Tasks; @@ -30,24 +31,21 @@ public async Task Excecute(string gid, Func tcc_cb, Cancellat public async Task Excecute(string gid, Action custom, Func tcc_cb, CancellationToken cancellationToken = default) { - var tcc = new Tcc(this.dtmClient, DtmImp.TransBase.NewTransBase(gid, Constant.Request.TYPE_TCC, "")); + var tcc = new Tcc(this.dtmClient, TransBase.NewTransBase(gid, DtmCommon.Constant.TYPE_TCC, "", "")); custom(tcc); try { - var prepare = await dtmClient.TransCallDtm(tcc.GetTransBase(), tcc.GetTransBase(), Constant.Request.OPERATION_PREPARE, cancellationToken); - logger.LogDebug("prepare result gid={gid}, res={res}", gid, prepare); + await dtmClient.TransCallDtm(tcc.GetTransBase(), tcc.GetTransBase(), Constant.Request.OPERATION_PREPARE, cancellationToken); await tcc_cb(tcc); - var submit = await dtmClient.TransCallDtm(tcc.GetTransBase(), tcc.GetTransBase(), Constant.Request.OPERATION_SUBMIT, cancellationToken); - logger.LogDebug("submit result gid={gid}, res={res}", gid, submit); + await dtmClient.TransCallDtm(tcc.GetTransBase(), tcc.GetTransBase(), Constant.Request.OPERATION_SUBMIT, cancellationToken); } catch (Exception ex) { logger.LogError(ex, "submitting or abort global transaction error"); - var abort = await dtmClient.TransCallDtm(tcc.GetTransBase(), tcc.GetTransBase(), Constant.Request.OPERATION_ABORT, cancellationToken); - logger.LogDebug("abort result gid={gid}, res={res}", gid, abort); + await dtmClient.TransCallDtm(tcc.GetTransBase(), tcc.GetTransBase(), Constant.Request.OPERATION_ABORT, cancellationToken); return string.Empty; } return gid;