seata get table meta error

myzjeezk  于 4个月前  发布在  其他
关注(0)|答案(4)|浏览(64)
  • 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。

希望可以帮助到一些人。

mwecs4sa

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.

snz8szmq

snz8szmq2#

看了1.8和2.0的seata代码也有这个问题
不过我这里倒是不要紧,我们避免跨库使用就好了。

ua4mk5z4

ua4mk5z43#

看了1.8和2.0的seata代码也有这个问题
不过我这里倒是不要紧,我们避免跨库使用就好了。

ok,我们将在社区邮件或会议上对这个问题进行相关讨论
Ok, we will discuss this issue in a community email or meeting

67up9zun

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.

相关问题