- I have searched the issues of this repository and believe that this is not a duplicate.
Ⅰ. Issue Description
seata 1.6.1(看了1.8和2.0的seata代码也有这个问题)
seata 自动刷新 TableMetaCache 时报错 “get table meta error”
Ⅱ. Describe what happened
jdbc:mysql://x.x.x.x:3306/A
数据库链接字串中配置的数据为A,此时有一个标注@GlobalTransaction的方法被调用,该方法操作了B数据库的table_1,即B.table_1,插入数据.
这时 io.seata.rm.datasource.sql.struct.cache.AbstractTableMetaCache#getTableMeta 虽然正常运行 ,等到当io.seata.rm.datasource.sql.struct.cache.AbstractTableMetaCache#refresh 刷新时 就会报错了。
“get table meta error”
io.seata.rm.datasource.sql.struct.cache.AbstractTableMetaCache-tableMetaChecker_1_1-get table meta error:Table 'qmgr.qo_ord_anomalous' doesn't exist-N/A-java.sql.SQLSyntaxErrorException: Table 'A.table_1' doesn't exist
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:120)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:97)
at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122)
at com.mysql.cj.jdbc.StatementImpl.executeQuery$original$sEQlPt7U(StatementImpl.java:1200)
at com.mysql.cj.jdbc.StatementImpl.executeQuery$original$sEQlPt7U$accessor$fo53R1GS(StatementImpl.java)
at com.mysql.cj.jdbc.StatementImpl$auxiliary$PTwarWz7.call(Unknown Source)
at org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstMethodsInter.intercept(InstMethodsInter.java:86)
at com.mysql.cj.jdbc.StatementImpl.executeQuery(StatementImpl.java)
at com.alibaba.druid.pool.DruidPooledStatement.executeQuery(DruidPooledStatement.java:296)
at io.seata.rm.datasource.sql.struct.cache.MysqlTableMetaCache.fetchSchema(MysqlTableMetaCache.java:82)
at io.seata.rm.datasource.sql.struct.cache.AbstractTableMetaCache.refresh(AbstractTableMetaCache.java:82)
最终排查因
直接原因:
1.4.2 代码是没有问题,是因为:client.rm.tableMetaCheckEnable 这个值默认为false,不会开启自动刷新cache
1.6.x client.rm.tableMetaCheckEnable(io.seata.rm.datasource.DataSourceProxy#ENABLE_TABLE_META_CHECKER_ENABLE) 默认值就变成了 true
根本原因:
io.seata.rm.datasource.sql.struct.cache.AbstractTableMetaCache#getTableMeta
public TableMeta getTableMeta(final Connection connection, final String tableName, String resourceId) {
if (StringUtils.isNullOrEmpty(tableName)) {
throw new IllegalArgumentException("TableMeta cannot be fetched without tableName");
}
TableMeta tmeta;
final String key = getCacheKey(connection, tableName, resourceId);
tmeta = TABLE_META_CACHE.get(key, mappingFunction -> {
try {
return fetchSchema(connection, tableName);
} catch (SQLException e) {
LOGGER.error("get table meta of the table `{}` error: {}", tableName, e.getMessage(), e);
return null;
}
});
if (tmeta == null) {
throw new ShouldNeverHappenException(String.format("[xid:%s]get table meta failed," +
" please check whether the table `%s` exists.", RootContext.getXID(), tableName));
}
**tableName 参数为 B.table_1,而缓存中 tmeta.tableName的值变成了table_1**
return tmeta;
}
public void refresh(final Connection connection, String resourceId) {
ConcurrentMap<String, TableMeta> tableMetaMap = TABLE_META_CACHE.asMap();
for (Map.Entry<String, TableMeta> entry : tableMetaMap.entrySet()) {
String key = getCacheKey(connection, entry.getValue().getTableName(), resourceId);
if (entry.getKey().equals(key)) {
try {
**这里就取到了没有库前缀的tableName:table_1,在链接字符串 jdbc:mysql://x.x.x.x:3306/A 下访问 table_1 于是就报 A.table_1 不能存在 **
TableMeta tableMeta = fetchSchema(connection, entry.getValue().getTableName());
if (!tableMeta.equals(entry.getValue())) {
TABLE_META_CACHE.put(entry.getKey(), tableMeta);
LOGGER.info("table meta change was found, update table meta cache automatically.");
}
} catch (SQLException e) {
LOGGER.error("get table meta error:{}", e.getMessage(), e);
}
}
}
}
最后
可能seata没有考虑 在同一个程序中跨库访问,但是实际开发过程中由于各种原因会出现这种情况。
在seata团队没有修复之前,如果有这个不太要紧的错误,看着太烦可以设置client.rm.tableMetaCheckEnable = false。
希望可以帮助到一些人。
4条答案
按热度按时间mwecs4sa1#
你可以尝试1.8或者2.0吗?这两个版本上是被动刷新,不是主动刷新,而2.2开始会恢复主动和被动两种方式
Can you try 1.8 or 2.0? These two versions are passive refreshes, not active refreshes, and 2.2 will restore active and passive methods from now on.
snz8szmq2#
看了1.8和2.0的seata代码也有这个问题
不过我这里倒是不要紧,我们避免跨库使用就好了。
ua4mk5z43#
看了1.8和2.0的seata代码也有这个问题
不过我这里倒是不要紧,我们避免跨库使用就好了。
ok,我们将在社区邮件或会议上对这个问题进行相关讨论
Ok, we will discuss this issue in a community email or meeting
67up9zun4#
I will fix it!
Tip: 由于表元数据缓存Key为单一表名,可能也存在跨库相同表名的情况下元数据获取错误的情况。
Tip: Since the table metadata cache key is a single table name, metadata retrieval errors may occur when the same table name is used across databases.