github-actions[bot] commented on code in PR #67417:
URL: https://github.com/apache/doris/pull/67417#discussion_r3920895240


##########
fe/fe-core/src/main/java/org/apache/doris/datasource/metacache/MetaCache.java:
##########
@@ -74,24 +131,131 @@ public MetaCache(String name,
                 maxSize,
                 true,
                 null);
-        namesCache = namesCacheFactory.buildCache(namesCacheLoader, executor);
+        namesCache = namesCacheFactory.buildCache();
         // Use sync removal listener to prevent deadlock (removal listener 
calls invalidateAll)
         // NOTE: This cache should NOT use refreshAfterWrite, as it would 
become synchronous
         metaObjCache = 
objCacheFactory.buildCacheWithSyncRemovalListener(metaObjCacheLoader, 
removalListener);
     }
 
     public List<String> listNames() {
-        return 
Objects.requireNonNull(namesCache.get("")).stream().map(Pair::value).collect(Collectors.toList());
+        return 
getNames().stream().map(Pair::value).collect(Collectors.toList());
+    }
+
+    private List<Pair<String, String>> getNames() {
+        int maxRetries = 3;
+        for (int attempt = 0; attempt < maxRetries + 1; attempt++) {
+            NamesCacheValue value = namesCache.getIfPresent("");
+            if (value == null || !value.complete) {
+                value = loadNames(false);
+                if (value == null) {
+                    continue;
+                }
+            }
+            boolean current;
+            synchronized (namesMutationLock) {
+                current = value.generation == namesGeneration.get();
+            }
+            if (current) {
+                scheduleNamesRefresh(value);
+                return value.names;
+            }
+            if (attempt == maxRetries - 1) {
+                return value.names;
+            }
+        }
+        NamesCacheValue value = namesCache.getIfPresent("");
+        return value != null ? value.names : Lists.newArrayList();
+    }
+
+    private NamesCacheValue loadNames(boolean forceRefresh) {
+        long loadGeneration;
+        List<Pair<String, String>> incompleteNames;
+        synchronized (namesDedupLock) {
+            synchronized (namesMutationLock) {
+                NamesCacheValue cached = namesCache.getIfPresent("");
+                if (!forceRefresh && cached != null && cached.complete
+                        && cached.generation == namesGeneration.get()) {
+                    return cached;
+                }
+                loadGeneration = namesGeneration.get();
+                if (!forceRefresh && activeLoadGeneration.get() == 
loadGeneration) {

Review Comment:
   [P1] Join the current generation's active load
   
   If caller A is paused in the cold `namesCacheLoader.load()`, caller B 
reaches this branch with the same generation and gets `null` on all four 
immediate retries. Since A has not published yet, B then returns `[]` (or a 
`complete=false` event overlay) from the terminal fallback. That makes 
concurrent database/table listings empty and can make mode-2 lookups or 
`isTableExist()` reject valid objects even though the load succeeds. Please 
keep a per-generation in-flight result that same-generation callers join, while 
still allowing a newer post-invalidation generation to load independently, and 
add a two-cold-reader test. This is distinct from the existing mutation-churn 
and obsolete-generation threads because it requires no mutation or generation 
change.



##########
fe/fe-core/src/main/java/org/apache/doris/datasource/metacache/MetaCache.java:
##########
@@ -127,26 +291,40 @@ public Optional<T> getMetaObjById(long id) {
 
     public void updateCache(String remoteName, String localName, T obj, long 
id) {
         metaObjCache.put(localName, Optional.of(obj));
-        namesCache.asMap().compute("", (k, v) -> {
-            if (v == null) {
-                return Lists.newArrayList(Pair.of(remoteName, localName));
+        synchronized (namesMutationLock) {
+            long generation = namesGeneration.incrementAndGet();
+            NameMutation mutation = NameMutation.update(remoteName, localName);
+            NamesCacheValue current = namesCache.getIfPresent("");
+            if (current != null) {
+                List<Pair<String, String>> names = 
Lists.newArrayList(current.names);

Review Comment:
   [P2] Keep incremental events out of full-snapshot publication
   
   For a warm complete namespace, every `updateCache()` now clones and scans 
the entire names list, then `publishNames()` rebuilds the entire database/table 
routing map before releasing `namesMutationLock`, which every `getNames()` 
reader also needs. A batch of HMS create events therefore becomes O(events 
times namespace size) allocation/work and stalls metadata readers; the base 
path appended the event entry and updated the routing map incrementally. Please 
keep event mutations in a keyed/O(1)-amortized structure and use the targeted 
callback, reserving full snapshot publication for loader completion/reset, with 
a large-namespace repeated-event test.



##########
fe/fe-core/src/main/java/org/apache/doris/datasource/metacache/MetaCache.java:
##########
@@ -127,26 +291,40 @@ public Optional<T> getMetaObjById(long id) {
 
     public void updateCache(String remoteName, String localName, T obj, long 
id) {
         metaObjCache.put(localName, Optional.of(obj));
-        namesCache.asMap().compute("", (k, v) -> {
-            if (v == null) {
-                return Lists.newArrayList(Pair.of(remoteName, localName));
+        synchronized (namesMutationLock) {
+            long generation = namesGeneration.incrementAndGet();
+            NameMutation mutation = NameMutation.update(remoteName, localName);
+            NamesCacheValue current = namesCache.getIfPresent("");
+            if (current != null) {
+                List<Pair<String, String>> names = 
Lists.newArrayList(current.names);
+                mutation.apply(names);
+                NamesCacheValue updated = new NamesCacheValue(generation, 
names, current.complete);
+                namesCache.put("", updated);
+                publishNames(updated);

Review Comment:
   [P1] Do not bulk-publish an incomplete names overlay
   
   After a previously complete names entry expires, the separate case-routing 
map still contains the old names. The first event update takes the `current == 
null` branch, caches a `complete=false` singleton, and correctly merges that 
name into the retained map. A second update reaches this call with another 
`complete=false` value, so the bulk callbacks replace the entire routing map 
with only the two overlay entries and drop every older database/table mapping. 
Replay/cache-only mode-2 lookups then miss those objects, and a normal catalog 
lookup also misses them if enumeration fails. Please use the targeted 
update/invalidation callbacks while `current.complete` is false and reserve 
whole-map replacement for a validated complete load/reset; add an expiry plus 
two-mutation test.



##########
fe/fe-core/src/main/java/org/apache/doris/datasource/metacache/MetaCache.java:
##########
@@ -180,6 +369,48 @@ public void addObjForTest(long id, String name, T db) {
      * Should only be used after creating new database/table
      */
     public void resetNames() {
-        namesCache.invalidateAll();
+        synchronized (namesMutationLock) {
+            minimumLoadGeneration = namesGeneration.incrementAndGet();
+            namesCache.invalidateAll();
+            namesCacheUpdateAction.accept(Lists.newArrayList());

Review Comment:
   [P1] Preserve replay routing on a names-only reset
   
   `resetNames()` deliberately leaves `metaObjCache` intact, but this new 
callback erases the mode-2 routing map that name-based replay needs to reach 
those cached objects. On a follower, replaying a rename unregisters the old 
table and calls this reset; if the next edit-log record refreshes an unrelated 
cached table B by name, `getTableForReplay("B")` performs no remote listing, 
cannot resolve B through the empty map, and returns before invalidating its 
stale metadata. The base names-only reset retained that mapping. Please keep 
routing for retained objects until they are invalidated, or make cache-only 
replay resolve them without this map, and add a mode-2 rename-reset followed by 
unrelated refresh replay test.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to