CalvinKirs commented on code in PR #66717:
URL: https://github.com/apache/doris/pull/66717#discussion_r3836819904


##########
fe/fe-core/src/main/java/org/apache/doris/datasource/iceberg/IcebergExternalMetaCache.java:
##########
@@ -216,27 +472,85 @@ private SchemaCacheValue 
loadSchemaCacheValue(IcebergSchemaCacheKey key) {
                         key.getNameMapping().getLocalTblName(), 
key.getSchemaId()));
     }
 
-    private IcebergSnapshotCacheValue loadSnapshotProjection(ExternalTable 
dorisTable, Table icebergTable) {
+    private SchemaCacheValue loadSchemaCacheValue(IcebergSchemaCacheKey key, 
Table retainedTable) {
+        ExternalTable dorisTable = findExternalTable(key.getNameMapping(), 
ENGINE);
+        dorisTable.setUpdateTime(System.currentTimeMillis());
+        boolean isView = dorisTable instanceof IcebergExternalTable
+                && ((IcebergExternalTable) dorisTable).isView();
+        SchemaCacheValue value = IcebergUtils.loadSchemaCacheValue(
+                dorisTable, key.getSchemaId(), isView, 
retainedTable).orElseThrow(() ->
+                new CacheException("failed to load iceberg schema cache value 
for: %s.%s.%s, schemaId: %s",
+                        null, key.getNameMapping().getCtlId(), 
key.getNameMapping().getLocalDbName(),
+                        key.getNameMapping().getLocalTblName(), 
key.getSchemaId()));
+        // Contextual miss loaders bypass the default-loader schema validator; 
ambiguous
+        // case-insensitive column names must be rejected on this path too.
+        value.validateSchema();
+        return value;
+    }
+
+    private void retireTableGeneration(NameMapping nameMapping,
+            @Nullable IcebergTableCacheValue previousValue, 
IcebergTableCacheValue currentValue) {
+        if (previousValue != null && 
previousValue.isSameOperationalGeneration(currentValue)) {

Review Comment:
   Fixed in the follow-up commit: the captured execution context is now part of 
the Iceberg operational generation - isSameOperationalGeneration compares 
authenticator identity, and hit-side sharesOperationalResources revalidation 
compares the table generation context against the projection captured context - 
so an auth-only ALTER followed by a same-UUID/metadata refresh with equivalent 
FileIO retires the old-context projection on replacement, and a hit rebuilds it 
bound to the new context instead of serving a permanently unplannable value. 
The planning fence also now applies only when a frozen handle is actually 
planned, so count-mode values are unaffected. Regression 
testAuthOnlyAlterRetiresProjectionsOfTheOldContext covers A1 -> auth-only A2 -> 
same-metadata refresh at both the equality and replacement-retirement levels.



##########
fe/fe-core/src/main/java/org/apache/doris/datasource/metacache/AbstractExternalMetaCache.java:
##########
@@ -222,8 +330,35 @@ protected final ExternalTable 
findExternalTable(NameMapping nameMapping, String
                         nameMapping.getLocalTblName(), engineNameForError));
     }
 
+    // A contended cache-policy handoff resolves within this window; see 
requireCatalogEntryGroup.
+    private static final long PREPARE_RETRY_WINDOW_NANOS = 2_000_000_000L;
+    private static final long PREPARE_RETRY_SLEEP_MS = 50L;
+
     private CatalogEntryGroup requireCatalogEntryGroup(long catalogId) {
         CatalogEntryGroup group = catalogEntries.get(catalogId);
+        if (group == null && catalogPreparer != null) {
+            // The caller prepared the catalog before capturing this engine, 
but a cache-policy
+            // ALTER retired the group in between. Re-prepare under the 
lifecycle fence so the
+            // lookup observes the new policy instead of failing a valid 
catalog. The preparer
+            // never blocks on the fence (a nested default loader may hold a 
Caffeine bin lock
+            // that retirement itself needs), so a contended handoff is 
absorbed with a bounded
+            // sleep-and-retry: the ALTER finishes within the window, or the 
lookup fails as
+            // before without any deadlock.
+            long deadlineNanos = System.nanoTime() + 
PREPARE_RETRY_WINDOW_NANOS;
+            while (true) {
+                catalogPreparer.accept(catalogId);

Review Comment:
   Fixed in the follow-up commit: onCatalogPermanentlyRemoved now records a 
tombstone in AbstractExternalMetaCache (catalog ids are never reused; 
defensively cleared if the id is ever re-initialized), and 
requireCatalogEntryGroup consults it before and during the bounded retry, so a 
lookup after DROP CATALOG fails immediately with a dropped-catalog message 
instead of sleeping through the two-second window, while rename and 
contended-ALTER handoffs (no tombstone) keep the existing bounded retry. Both 
engine overrides of the hook call super so the tombstone is engine-wide. 
Latency-bounded regression testPermanentlyDroppedCatalogFailsLookupsImmediately 
covers the terminal drop (well under the window, preparer not spun) and the 
transient-absence retry that still re-prepares.



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