This is an automated email from the ASF dual-hosted git repository. morningman pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push: new 1e7d2fffce6 [fix](catalog) fix deadlock of catalog and database (#53626) 1e7d2fffce6 is described below commit 1e7d2fffce6bf0d55dfc891afec220ea223849a9 Author: Mingyu Chen (Rayner) <morning...@163.com> AuthorDate: Wed Jul 23 00:09:51 2025 -0700 [fix](catalog) fix deadlock of catalog and database (#53626) ### What problem does this PR solve? Problem Summary: We should strictly follow the order of `synchronized` of ExternalCatalog and ExternalDatabase. First is `ExternalCatalog`, then `ExternalDatabase`. ``` Java stack information for the threads listed above: =================================================== "STATS_FETCH-3": at org.apache.doris.datasource.ExternalCatalog.makeSureInitialized(ExternalCatalog.java:302) - waiting to lock <0x000000060b1c13f0> (a org.apache.doris.datasource.jdbc.JdbcExternalCatalog) at org.apache.doris.datasource.ExternalDatabase.makeSureInitialized(ExternalDatabase.java:163) - locked <0x000000060b1c14b0> (a org.apache.doris.datasource.jdbc.JdbcExternalDatabase) at org.apache.doris.datasource.ExternalDatabase.getTableNullable(ExternalDatabase.java:706) at org.apache.doris.datasource.ExternalDatabase.getTableNullable(ExternalDatabase.java:72) at org.apache.doris.catalog.DatabaseIf.getTableOrException(DatabaseIf.java:154) at org.apache.doris.statistics.util.StatisticsUtil.findTable(StatisticsUtil.java:461) at org.apache.doris.statistics.ColumnStatisticsCacheLoader.doLoad(ColumnStatisticsCacheLoader.java:41) at org.apache.doris.statistics.ColumnStatisticsCacheLoader.doLoad(ColumnStatisticsCacheLoader.java:29) at org.apache.doris.statistics.BasicAsyncCacheLoader.lambda$asyncLoad$0(BasicAsyncCacheLoader.java:39) at org.apache.doris.statistics.BasicAsyncCacheLoader$$Lambda$2610/0x00007f0b1d4a5c00.get(Unknown Source) at java.util.concurrent.CompletableFuture$AsyncSupply.run(java.base@17.0.15/CompletableFuture.java:1768) at java.util.concurrent.ThreadPoolExecutor.runWorker(java.base@17.0.15/ThreadPoolExecutor.java:1136) at java.util.concurrent.ThreadPoolExecutor$Worker.run(java.base@17.0.15/ThreadPoolExecutor.java:635) at java.lang.Thread.run(java.base@17.0.15/Thread.java:840) "mysql-nio-pool-73": at org.apache.doris.datasource.ExternalDatabase.resetToUninitialized(ExternalDatabase.java:135) - waiting to lock <0x000000060b1c14b0> (a org.apache.doris.datasource.jdbc.JdbcExternalDatabase) at org.apache.doris.datasource.ExternalCatalog.refreshOnlyCatalogCache(ExternalCatalog.java:594) at org.apache.doris.datasource.ExternalCatalog.resetToUninitialized(ExternalCatalog.java:579) - locked <0x000000060b1c13f0> (a org.apache.doris.datasource.jdbc.JdbcExternalCatalog) at org.apache.doris.datasource.jdbc.JdbcExternalCatalog.resetToUninitialized(JdbcExternalCatalog.java:132) - locked <0x000000060b1c13f0> (a org.apache.doris.datasource.jdbc.JdbcExternalCatalog) at org.apache.doris.catalog.RefreshManager.refreshCatalogInternal(RefreshManager.java:75) at org.apache.doris.catalog.RefreshManager.handleRefreshCatalog(RefreshManager.java:58) at org.apache.doris.nereids.trees.plans.commands.refresh.RefreshCatalogCommand.run(RefreshCatalogCommand.java:79) at org.apache.doris.qe.StmtExecutor.executeByNereids(StmtExecutor.java:707) at org.apache.doris.qe.StmtExecutor.execute(StmtExecutor.java:545) at org.apache.doris.qe.StmtExecutor.queryRetry(StmtExecutor.java:507) at org.apache.doris.qe.StmtExecutor.execute(StmtExecutor.java:492) at org.apache.doris.qe.ConnectProcessor.executeQuery(ConnectProcessor.java:346) at org.apache.doris.qe.ConnectProcessor.handleQuery(ConnectProcessor.java:246) at org.apache.doris.qe.MysqlConnectProcessor.handleQuery(MysqlConnectProcessor.java:233) at org.apache.doris.qe.MysqlConnectProcessor.dispatch(MysqlConnectProcessor.java:261) at org.apache.doris.qe.MysqlConnectProcessor.processOnce(MysqlConnectProcessor.java:443) at org.apache.doris.mysql.ReadListener.lambda$handleEvent$0(ReadListener.java:52) at org.apache.doris.mysql.ReadListener$$Lambda$1018/0x00007f0b1cbc4000.run(Unknown Source) at java.util.concurrent.ThreadPoolExecutor.runWorker(java.base@17.0.15/ThreadPoolExecutor.java:1136) at java.util.concurrent.ThreadPoolExecutor$Worker.run(java.base@17.0.15/ThreadPoolExecutor.java:635) at java.lang.Thread.run(java.base@17.0.15/Thread.java:840) ``` --- .../apache/doris/datasource/ExternalDatabase.java | 66 ++++++++++++---------- 1 file changed, 35 insertions(+), 31 deletions(-) diff --git a/fe/fe-core/src/main/java/org/apache/doris/datasource/ExternalDatabase.java b/fe/fe-core/src/main/java/org/apache/doris/datasource/ExternalDatabase.java index c440f481034..0f453b1b325 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/datasource/ExternalDatabase.java +++ b/fe/fe-core/src/main/java/org/apache/doris/datasource/ExternalDatabase.java @@ -154,41 +154,45 @@ public abstract class ExternalDatabase<T extends ExternalTable> return initialized; } - public final synchronized void makeSureInitialized() { - if (isInitializing) { - return; - } - isInitializing = true; - try { - extCatalog.makeSureInitialized(); - if (!initialized) { - if (extCatalog.getUseMetaCache().get()) { - buildMetaCache(); - setLastUpdateTime(System.currentTimeMillis()); - } else { - if (!Env.getCurrentEnv().isMaster()) { - // Forward to master and wait the journal to replay. - int waitTimeOut = ConnectContext.get() == null ? 300 : ConnectContext.get().getExecTimeoutS(); - MasterCatalogExecutor remoteExecutor = new MasterCatalogExecutor(waitTimeOut * 1000); - try { - remoteExecutor.forward(extCatalog.getId(), id); - } catch (Exception e) { - Util.logAndThrowRuntimeException(LOG, - String.format("failed to forward init external db %s operation to master", name), - e); + public final void makeSureInitialized() { + // Must call this method before any operation on the database to avoid deadlock of synchronized block + extCatalog.makeSureInitialized(); + synchronized (this) { + if (isInitializing) { + return; + } + isInitializing = true; + try { + if (!initialized) { + if (extCatalog.getUseMetaCache().get()) { + buildMetaCache(); + setLastUpdateTime(System.currentTimeMillis()); + } else { + if (!Env.getCurrentEnv().isMaster()) { + // Forward to master and wait the journal to replay. + int waitTimeOut = ConnectContext.get() == null ? 300 + : ConnectContext.get().getExecTimeoutS(); + MasterCatalogExecutor remoteExecutor = new MasterCatalogExecutor(waitTimeOut * 1000); + try { + remoteExecutor.forward(extCatalog.getId(), id); + } catch (Exception e) { + Util.logAndThrowRuntimeException(LOG, + String.format("failed to forward init external db %s operation to master", + name), e); + } + return; } - return; + init(); } - init(); + initialized = true; } - initialized = true; + } catch (Exception e) { + LOG.warn("failed to init db {}, id {}, isInitializing: {}, initialized: {}", + this.name, this.id, isInitializing, initialized, e); + initialized = false; + } finally { + isInitializing = false; } - } catch (Exception e) { - LOG.warn("failed to init db {}, id {}, isInitializing: {}, initialized: {}", - this.name, this.id, isInitializing, initialized, e); - initialized = false; - } finally { - isInitializing = false; } } --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For additional commands, e-mail: commits-h...@doris.apache.org