Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

get table meta error #6685

Closed
1 task done
fanfanyimeng opened this issue Jul 19, 2024 · 4 comments · Fixed by #6759
Closed
1 task done

get table meta error #6685

fanfanyimeng opened this issue Jul 19, 2024 · 4 comments · Fixed by #6759
Assignees

Comments

@fanfanyimeng
Copy link

fanfanyimeng commented Jul 19, 2024

  • 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。

希望可以帮助到一些人。

@funky-eyes
Copy link
Contributor

你可以尝试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.

@fanfanyimeng
Copy link
Author

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

@funky-eyes
Copy link
Contributor

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

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

@GoodBoyCoder
Copy link
Contributor

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants