From 3af8459337621f0cad2e62a9448e6b5a7e2f83fc Mon Sep 17 00:00:00 2001 From: "Mingyu Chen (Rayner)" Date: Thu, 14 Nov 2024 16:23:43 +0800 Subject: [PATCH] [fix](catalog) rebuild idToCatalog map after replay (#43772) ### What problem does this PR solve? Problem Summary: After deserialized from Gson, the ConcurrentHashMap may become normal non-threadsafe HashMap, This may causing following issue: ``` 2024-11-12 16:43:53,101 ERROR (stateListener|13) [Env.transferToNonMaster():1921] failed to transfer to non-master. java.util.ConcurrentModificationException: null at java.util.LinkedHashMap$LinkedHashIterator.nextNode(LinkedHashMap.java:756) ~[?:?] at java.util.LinkedHashMap$LinkedValueIterator.next(LinkedHashMap.java:783) ~[?:?] at org.apache.doris.datasource.CatalogMgr.registerCatalogRefreshListener(CatalogMgr.java:802) ~[doris-fe.jar:1.2-SNAPSHOT] at org.apache.doris.catalog.Env.postProcessAfterMetadataReplayed(Env.java:1751) ~[doris-fe.jar:1.2-SNAPSHOT] at org.apache.doris.catalog.Env.transferToNonMaster(Env.java:1899) ~[doris-fe.jar:1.2-SNAPSHOT] at org.apache.doris.catalog.Env.access$1300(Env.java:347) ~[doris-fe.jar:1.2-SNAPSHOT] at org.apache.doris.catalog.Env$5.runOneCycle(Env.java:2850) ~[doris-fe.jar:1.2-SNAPSHOT] at org.apache.doris.common.util.Daemon.run(Daemon.java:119) ~[doris-fe.jar:1.2-SNAPSHOT] ``` This PR rebuild the `idToCatalog` and `nameToCatalog` map in `gsonPostProcess()` of `CatalogMgr`, to solve the issue. --- .../org/apache/doris/datasource/CatalogMgr.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/fe/fe-core/src/main/java/org/apache/doris/datasource/CatalogMgr.java b/fe/fe-core/src/main/java/org/apache/doris/datasource/CatalogMgr.java index 729bd8a12fd80b..0ec49052cb83c8 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/datasource/CatalogMgr.java +++ b/fe/fe-core/src/main/java/org/apache/doris/datasource/CatalogMgr.java @@ -93,9 +93,9 @@ public class CatalogMgr implements Writable, GsonPostProcessable { private final MonitoredReentrantReadWriteLock lock = new MonitoredReentrantReadWriteLock(true); @SerializedName(value = "idToCatalog") - private final Map>> idToCatalog = Maps.newConcurrentMap(); + private Map>> idToCatalog = Maps.newConcurrentMap(); // this map will be regenerated from idToCatalog, so not need to persist. - private final Map nameToCatalog = Maps.newConcurrentMap(); + private Map nameToCatalog = Maps.newConcurrentMap(); // Use a separate instance to facilitate access. // internalDataSource still exists in idToCatalog and nameToCatalog @@ -816,10 +816,17 @@ public void write(DataOutput out) throws IOException { @Override public void gsonPostProcess() throws IOException { + // After deserializing from Gson, the concurrent map may become a normal map. + // So here we reconstruct the concurrent map. + Map>> newIdToCatalog = Maps.newConcurrentMap(); + Map newNameToCatalog = Maps.newConcurrentMap(); for (CatalogIf catalog : idToCatalog.values()) { - nameToCatalog.put(catalog.getName(), catalog); + newNameToCatalog.put(catalog.getName(), catalog); + newIdToCatalog.put(catalog.getId(), catalog); // ATTN: can not call catalog.getProperties() here, because ResourceMgr is not replayed yet. } + this.idToCatalog = newIdToCatalog; + this.nameToCatalog = newNameToCatalog; internalCatalog = (InternalCatalog) idToCatalog.get(InternalCatalog.INTERNAL_CATALOG_ID); }