Skip to content

Commit ba8f9f7

Browse files
authored
gPRC unit test&sample: TransactionScope dispose exception (#90)
* test(Dtmgrpc.IntegrationTests): add MySql QueryPrepared Demo - add MySql QueryPrepared Demo in IntegrationTests - Add new test case for MsgGrpc.DoAndSubmit to verify database transactions * test(Dtmgrpc.IntegrationTests): add exception handling and status check in MsgGrpcTest cover Grpc Msg.DoAndSubmit
1 parent 2dd50e7 commit ba8f9f7

File tree

4 files changed

+115
-1
lines changed

4 files changed

+115
-1
lines changed

tests/BusiGrpcService/Services/BusiApiService.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Text.Json;
66
using Dapper;
77
using System.Data.Common;
8+
using DtmCommon;
89
using DtmSERedisBarrier;
910

1011
namespace BusiGrpcService.Services
@@ -140,6 +141,18 @@ public override async Task<BusiReply> QueryPrepared(BusiReq request, ServerCallC
140141
throw Dtmgrpc.DtmGImp.Utils.DtmError2GrpcError(ex);
141142
}
142143

144+
// real mysql query prepared demo, just copy it!
145+
public override async Task<Empty> QueryPreparedMySqlReal(BusiReq request, ServerCallContext context)
146+
{
147+
BranchBarrier barrier = _barrierFactory.CreateBranchBarrier(context);
148+
string result = await barrier.QueryPrepared(this.GetBarrierConn());
149+
150+
Exception ex = Dtmgrpc.DtmGImp.Utils.String2DtmError(result);
151+
if (ex != null)
152+
throw Dtmgrpc.DtmGImp.Utils.DtmError2GrpcError(ex);
153+
return new Empty();
154+
}
155+
143156
public override async Task<Empty> TransInRedis(BusiReq request, ServerCallContext context)
144157
{
145158
_logger.LogInformation("TransInRedis req={req}", JsonSerializer.Serialize(request));

tests/Dtmgrpc.IntegrationTests/Dtmgrpc.IntegrationTests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
2020
<PrivateAssets>all</PrivateAssets>
2121
</PackageReference>
22+
<PackageReference Include="MySqlConnector" Version="$(MySqlConnectorPackageVersion)" />
2223
</ItemGroup>
2324

2425
<ItemGroup>

tests/Dtmgrpc.IntegrationTests/MsgGrpcTest.cs

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
using Microsoft.Extensions.DependencyInjection;
22
using System;
3+
using System.Data.Common;
34
using System.Threading.Tasks;
5+
using System.Transactions;
6+
using Dapper;
7+
using Grpc.Core;
8+
using MySqlConnector;
49
using Xunit;
510

611
namespace Dtmgrpc.IntegrationTests
@@ -27,5 +32,100 @@ public async Task Submit_Should_Succeed()
2732
var status = await ITTestHelper.GetTranStatus(gid);
2833
Assert.Equal("succeed", status);
2934
}
35+
36+
[Fact]
37+
public async Task DoAndSubmit_Should_DbTrans_Exception()
38+
{
39+
var provider = ITTestHelper.AddDtmGrpc();
40+
var transFactory = provider.GetRequiredService<IDtmTransFactory>();
41+
42+
var gid = "msgTestGid" + Guid.NewGuid().ToString();
43+
var msg = transFactory.NewMsgGrpc(gid);
44+
var req = ITTestHelper.GenBusiReq(false, false);
45+
var busiGrpc = ITTestHelper.BuisgRPCUrl;
46+
47+
msg.Add(busiGrpc + "/busi.Busi/TransIn", req);
48+
// do TransOut local, then TransIn with DTM.
49+
await Assert.ThrowsAsync<System.InvalidOperationException>(async () =>
50+
{
51+
// System.InvalidOperationException: A TransactionScope must be disposed on the same thread that it was created.
52+
//
53+
// System.InvalidOperationException
54+
// A TransactionScope must be disposed on the same thread that it was created.
55+
// at Dtmgrpc.MsgGrpc.DoAndSubmit(String queryPrepared, Func`2 busiCall, CancellationToken cancellationToken) in /home/yunjin/Data/projects/github/dtm-labs/client-csharp/src/Dtmgrpc/Msg/MsgGrpc.cs:line 110
56+
57+
await msg.DoAndSubmit(busiGrpc + "/busi.Busi/QueryPreparedMySqlReal", async branchBarrier =>
58+
{
59+
MySqlConnection conn = getBarrierMySqlConnection();
60+
await branchBarrier.Call(conn, () =>
61+
{
62+
Task task = this.LocalAdjustBalance(conn, TransOutUID, -req.Amount, "SUCCESS");
63+
return task;
64+
},
65+
TransactionScopeOption.Required,
66+
IsolationLevel.ReadCommitted
67+
// , default TransactionScopeAsyncFlowOption.Suppress
68+
);
69+
});
70+
});
71+
72+
await Task.Delay(4000);
73+
var status = await ITTestHelper.GetTranStatus(gid);
74+
// The exception did not affect the local transaction commit
75+
Assert.Equal("succeed", status);
76+
}
77+
78+
[Fact]
79+
public async Task DoAndSubmit_Should_Succeed()
80+
{
81+
var provider = ITTestHelper.AddDtmGrpc();
82+
var transFactory = provider.GetRequiredService<IDtmTransFactory>();
83+
84+
var gid = "msgTestGid" + Guid.NewGuid().ToString();
85+
var msg = transFactory.NewMsgGrpc(gid);
86+
var req = ITTestHelper.GenBusiReq(false, false);
87+
var busiGrpc = ITTestHelper.BuisgRPCUrl;
88+
89+
msg.Add(busiGrpc + "/busi.Busi/TransIn", req);
90+
// do TransOut local, then TransIn with DTM.
91+
92+
await msg.DoAndSubmit(busiGrpc + "/busi.Busi/QueryPreparedMySqlReal", async branchBarrier =>
93+
{
94+
MySqlConnection conn = getBarrierMySqlConnection();
95+
await branchBarrier.Call(conn, () =>
96+
{
97+
Task task = this.LocalAdjustBalance(conn, TransOutUID, -req.Amount, "SUCCESS");
98+
return task;
99+
},
100+
TransactionScopeOption.Required,
101+
IsolationLevel.ReadCommitted,
102+
TransactionScopeAsyncFlowOption.Enabled);
103+
});
104+
105+
await Task.Delay(2000);
106+
var status = await ITTestHelper.GetTranStatus(gid);
107+
Assert.Equal("succeed", status);
108+
}
109+
110+
private static readonly int TransOutUID = 1;
111+
112+
private static readonly int TransInUID = 2;
113+
114+
private MySqlConnection getBarrierMySqlConnection() => new("Server=localhost;port=3306;User ID=root;Password=123456;Database=dtm_barrier");
115+
116+
private async Task LocalAdjustBalance(DbConnection conn, int uid, long amount, string result)
117+
{
118+
// _logger.LogInformation("AdjustBalanceLocal uid={uid}, amount={amount}, result={result}", uid, amount, result);
119+
120+
if (result.Equals("FAILURE"))
121+
{
122+
throw new RpcException(new Status(StatusCode.Aborted, "FAILURE"));
123+
}
124+
125+
await conn.ExecuteAsync(
126+
sql: "update dtm_busi.user_account set balance = balance + @balance where user_id = @user_id",
127+
param: new { balance = amount, user_id = uid }
128+
);
129+
}
30130
}
31131
}

tests/protos/busi.proto

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,6 @@ service Busi {
4545
rpc TransOutRevertRedis(BusiReq) returns (google.protobuf.Empty) {}
4646

4747
rpc QueryPrepared(BusiReq) returns (BusiReply) {}
48-
rpc QueryPreparedB(BusiReq) returns (google.protobuf.Empty) {}
48+
rpc QueryPreparedMySqlReal(BusiReq) returns (google.protobuf.Empty) {}
4949
rpc QueryPreparedRedis(BusiReq) returns (google.protobuf.Empty) {}
5050
}

0 commit comments

Comments
 (0)