[ INFO ] [2021-05-31 10:25:08.575] [thread: rpcDispatch_RMROLE_1_7_16] [io.seata.core.rpc.processor.client.RmBranchRollbackProcessor - 56] - [msg: rm handle branch rollback process:xid=192.168.3.66:8091:142575713164201984,branchId=142575715471069185,branchType=AT,resourceId=jdbc:oracle:thin:@192.168.3.152:49161:XE/YUE,applicationData=null]
[ INFO ] [2021-05-31 10:25:08.575] [thread: rpcDispatch_RMROLE_1_7_16] [io.seata.rm.AbstractRMHandler - 123] - [msg: Branch Rollbacking: 192.168.3.66:8091:142575713164201984 142575715471069185 jdbc:oracle:thin:@192.168.3.152:49161:XE/YUE]
[ INFO ] [2021-05-31 10:25:08.581] [thread: rpcDispatch_RMROLE_1_7_16] [io.seata.rm.datasource.undo.AbstractUndoExecutor - 259] - [msg: Field not equals, name CREATE_TIME, old value 2021-05-31 10:25:02.0, new value 2021-05-31 10:25:02.736]
[ INFO ] [2021-05-31 10:25:08.584] [thread: rpcDispatch_RMROLE_1_7_16] [io.seata.rm.datasource.DataSourceManager - 41] - [msg: branchRollback failed. branchType:[AT], xid:[192.168.3.66:8091:142575713164201984], branchId:[142575715471069185], resourceId:[jdbc:oracle:thin:@192.168.3.152:49161:XE/YUE], applicationData:[null]. reason:[Branch session rollback failed and try again later xid = 192.168.3.66:8091:142575713164201984 branchId = 142575715471069185 Has dirty records when undo.]]
[ INFO ] [2021-05-31 10:25:08.585] [thread: rpcDispatch_RMROLE_1_7_16] [io.seata.rm.AbstractRMHandler - 131] - [msg: Branch Rollbacked result: PhaseTwo_RollbackFailed_Retryable]
其实报错很明显:
AbstractUndoExecutor - 259] - [msg: Field not equals, name CREATE_TIME, old value 2021-05-31 10:25:02.0, new value 2021-05-31 10:25:02.736]
AbstractUndoExecutor 在 事务回滚的时候会比较 之前的镜像和之后的镜像, 如果字段值不匹配, 就会会滚失败. 偏偏遇到 oracle.sql.TIMESTAMP序列化损失精度, 所以不相等.
protected boolean dataValidationAndGoOn(Connection conn) throws SQLException {
TableRecords beforeRecords = sqlUndoLog.getBeforeImage();
TableRecords afterRecords = sqlUndoLog.getAfterImage();
// Compare current data with before data
// No need undo if the before data snapshot is equivalent to the after data snapshot.
Result<Boolean> beforeEqualsAfterResult = DataCompareUtils.isRecordsEquals(beforeRecords, afterRecords);
if (beforeEqualsAfterResult.getResult()) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Stop rollback because there is no data change " +
"between the before data snapshot and the after data snapshot.");
}
// no need continue undo.
return false;
}
// Validate if data is dirty.
TableRecords currentRecords = queryCurrentRecords(conn);
// compare with current data and after image.
Result<Boolean> afterEqualsCurrentResult = DataCompareUtils.isRecordsEquals(afterRecords, currentRecords);
if (!afterEqualsCurrentResult.getResult()) {
// If current data is not equivalent to the after data, then compare the current data with the before
// data, too. No need continue to undo if current data is equivalent to the before data snapshot
Result<Boolean> beforeEqualsCurrentResult = DataCompareUtils.isRecordsEquals(beforeRecords, currentRecords);
if (beforeEqualsCurrentResult.getResult()) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Stop rollback because there is no data change " +
"between the before data snapshot and the current data snapshot.");
}
// no need continue undo.
return false;
} else {
if (LOGGER.isInfoEnabled()) {
if (StringUtils.isNotBlank(afterEqualsCurrentResult.getErrMsg())) {
LOGGER.info(afterEqualsCurrentResult.getErrMsg(), afterEqualsCurrentResult.getErrMsgParams());
}
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("check dirty datas failed, old and new data are not equal," +
"tableName:[" + sqlUndoLog.getTableName() + "]," +
"oldRows:[" + JSON.toJSONString(afterRecords.getRows()) + "]," +
"newRows:[" + JSON.toJSONString(currentRecords.getRows()) + "].");
}
throw new SQLException("Has dirty records when undo.");
}
}
return true;
}
DataCompareUtils.isRecordsEquals(afterRecords, currentRecords); 主要是这个类在比较.
找到了 动刀子, 扩展支持 oracle.sql.TIMESTAMP, 抹去精度, 转化为19位日期.
这是一种方式, 也可以搞一下 重写一个 UndoLogParser, 另一种方式, 你们自行搞吧.