-
Notifications
You must be signed in to change notification settings - Fork 997
Description
问题场景
使用csharp的sdk开发MSG模式, 抄sample中QueryPrepared代码实现,当配置的Barrier表连接串有问题(如数据库账号没有写权现),DTM状态误变成commited.
[HttpGet("msg-queryprepared")]
public async Task<IActionResult> MsgMySqlQueryPrepared(CancellationToken cancellationToken)
{
var bb = _factory.CreateBranchBarrier(Request.Query);
_logger.LogInformation("bb {0}", bb);
using (MySqlConnection conn = GetConn())
{
var res = await bb.QueryPrepared(conn);
return Ok(new { dtm_result = res });
}
}
问题分析
v1.9.1时候,body不包含dtmcli三个参量ResultSuccess、ResultFailure、ResultOngoing的QueryPrepared会当成getting result failed
,如http200 body:{ dtm_result: "MySql connection timeout, balabala... " }
,走touchCronTime。
func (t *TransGlobal) mayQueryPrepared() {
if !t.needProcess() || t.Status == dtmcli.StatusSubmitted {
return
}
body, err := t.getURLResult(t.QueryPrepared, "00", "msg", nil)
if strings.Contains(body, dtmcli.ResultSuccess) {
t.changeStatus(dtmcli.StatusSubmitted)
} else if strings.Contains(body, dtmcli.ResultFailure) {
t.changeStatus(dtmcli.StatusFailed)
} else if strings.Contains(body, dtmcli.ResultOngoing) {
t.touchCronTime(cronReset)
} else {
logger.Errorf("getting result failed for %s. error: %v body %s", t.QueryPrepared, err, body)
t.touchCronTime(cronBackoff)
}
}
v1.10.0后使用新的规则升级指南 1.9.x 升级到 1.10.xSDK与服务端判断 SUCCESS http:状态码 200 && 结果不包含 FAILURE|ONGOING; grpc:err == nil
,上面http200示例会当成正常,走设置成Submitted。
QueryPrepared的调用链
- func (t *TransGlobal) mayQueryPrepared(ctx context.Context)
- func (t *TransGlobal) getURLResult(ctx context.Context, uri string, branchID, op string, branchPayload []byte) error
- func (t *TransGlobal) getHTTPResult(uri string, branchID, op string, branchPayload []byte) error
- func HTTPResp2DtmError(resp *resty.Response) error {
// HTTPResp2DtmError translate a resty response to error
// compatible with version < v1.10
func HTTPResp2DtmError(resp *resty.Response) error {
code := resp.StatusCode()
str := resp.String()
if code == http.StatusTooEarly || strings.Contains(str, ResultOngoing) {
return ErrorMessage2Error(str, ErrOngoing)
} else if code == http.StatusConflict || strings.Contains(str, ResultFailure) {
return ErrorMessage2Error(str, ErrFailure)
} else if code != http.StatusOK {
return errors.New(str)
}
return nil
}
因此,1.10.0并没有完全的兼容之前的QueryPrepared返回值。 c# 这边QueryPrepared方法只封装了BranchBarrier实现,没封装到http状态码级别。Sample中也一直按照v1.10.0之前的协议以http200靠body区分状态。
dtmcli-csharp-sample/DtmSample/DtmSample/Controllers/MsgTestController.cs at main · dtm-labs/dtmcli-csharp-sample
[HttpGet("msg-queryprepared")]
public async Task<IActionResult> MsgMySqlQueryPrepared(CancellationToken cancellationToken)
{
var bb = _factory.CreateBranchBarrier(Request.Query);
_logger.LogInformation("bb {0}", bb);
using (MySqlConnection conn = GetConn())
{
var res = await bb.QueryPrepared(conn);
return Ok(new { dtm_result = res });
}
}
建议
v1.10.0发布到现在的时间跨度已经很大,已经无法做到两面都兼容,建议:
- 修改升级指南升级指南 1.9.x 升级到 1.10.x, 描述好这次breaking changes
0e59ccb ("change to new error protocal ok", 2022-01-10)
。双向兼容仅限go sdk,其他需要自己核实新版本的SDK可以和新旧版本的协议兼容;新版本的服务器能够与新旧版本的协议兼容。因此理论上可以随意升级。
- 开发文档里明确接口调用返回值规则
- .net这边,封装QueryPrepared服务到http状态码的转换,并修改Sample, 避免更多人抄错
- 其他语言的SDK也有必要核实有没有这种状况,正常跑不会出现问题