924060929 commented on code in PR #69632:
URL: https://github.com/apache/doris/pull/69632#discussion_r4386013105


##########
fe/fe-core/src/main/java/org/apache/doris/datasource/metacache/MetaCache.java:
##########
@@ -662,6 +687,103 @@ public void invalidate(String localName, long id) {
         }
     }
 
+    /** Fences old objects while retaining unrelated numeric routes for fresh 
reload after wide retirement. */
+    public void invalidateLookup(String localName, boolean allObjects) {
+        metaObjLifecycleLock.writeLock().lock();
+        try {
+            synchronized (namesMutationLock) {
+                invalidateNames();
+                metaObjGeneration.incrementAndGet();
+                lookupObservationGeneration++;
+                if (allObjects) {
+                    metaObjCache.invalidateAll();
+                } else {
+                    metaObjCache.invalidate(localName);
+                }
+                // Only the target's routes retire. Surviving siblings must 
reload by ID without
+                // requiring a foreground named lookup to register their 
identities again.
+                String foldedName = allObjects ? 
localName.toLowerCase(Locale.ROOT) : localName;
+                idToName.entrySet().removeIf(entry -> allObjects
+                        ? 
entry.getValue().toLowerCase(Locale.ROOT).equals(foldedName)
+                        : entry.getValue().equals(localName));
+            }
+        } finally {
+            metaObjLifecycleLock.writeLock().unlock();
+        }
+    }
+
+    /** Blocks missing-result repair during a metadata-only refresh without 
evicting names or objects. */
+    public Runnable beginMetadataRefresh() {
+        metaObjLifecycleLock.writeLock().lock();
+        try {
+            metadataRefreshes++;
+            lookupObservationGeneration++;
+        } finally {
+            metaObjLifecycleLock.writeLock().unlock();
+        }
+        return () -> {
+            metaObjLifecycleLock.writeLock().lock();
+            try {
+                metadataRefreshes--;
+                lookupObservationGeneration++;
+            } finally {
+                metaObjLifecycleLock.writeLock().unlock();
+            }
+        };
+    }
+
+    /** Captures a complete names snapshot and matching resident object; never 
loads remote metadata. */
+    public Optional<Runnable> observeLookup(String localName, String 
remoteName, T expected,
+            boolean allObjects, Runnable invalidateMetadata) {
+        metaObjLifecycleLock.readLock().lock();
+        try {
+            synchronized (namesMutationLock) {
+                NamesCacheValue names = namesCache.getIfPresent("");
+                Optional<T> value = metaObjCache.getIfPresent(localName);
+                Pair<String, String> pair = names == null ? null : 
names.names.get(localName);
+                // A complete refresh may already omit a remotely dropped 
table while its object/ID
+                // remains hot. The retained object still authorizes repair; a 
conflicting live name does not.
+                if (metadataRefreshes != 0 || names == null || !names.complete
+                        || (pair != null && !pair.key().equals(remoteName)) || 
value == null || !value.isPresent()
+                        || value.get() != expected) {
+                    return Optional.empty();
+                }
+                long objectGeneration = metaObjGeneration.get();
+                long observationGeneration = lookupObservationGeneration;
+                return Optional.of(() -> {
+                    metaObjLifecycleLock.writeLock().lock();
+                    try {
+                        synchronized (namesMutationLock) {
+                            if (metadataRefreshes != 0 || objectGeneration != 
metaObjGeneration.get()
+                                    || observationGeneration != 
lookupObservationGeneration
+                                    || namesCache.getIfPresent("") != names
+                                    || metaObjCache.getIfPresent(localName) != 
value) {
+                                return;
+                            }
+                            // Case-folded catalogs can retain Foo's old 
object after a names refresh
+                            // has published a new FOO identity. Do not widen 
that old miss to the DB.
+                            if (pair == null && allObjects && 
names.names.values().stream().anyMatch(current ->
+                                    current.value().equalsIgnoreCase(localName)

Review Comment:
   Fixed in 56085d500821. Unified affected-ID selection, replacement protection 
and ID retirement through one private ROOT-locale comparison-key helper. Cache 
slots, displayed names and ID formats remain unchanged. Both directions 
reproduced before the fix: I/dotless-i incorrectly suppressed cleanup, while 
dotted-I/i-plus-combining-dot incorrectly permitted cleanup of a replacement. 
Four new contract tests cover six name pairs across sensitive, ASCII-folded and 
Unicode-folded modes, asserting matching, authorization and final numeric 
lookup together.
   
   Validation: 128 focused FE cases passed; FE build and Checkstyle passed. 
Both Unicode tests failed on the old branch-4.2 source before the fix. Remote 
SDK responses are mocked; no live-service integration claim.



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