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


##########
fe/fe-connector/fe-connector-paimon/src/main/java/org/apache/doris/connector/paimon/PaimonMetaCacheCatalog.java:
##########
@@ -0,0 +1,355 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.connector.paimon;
+
+import org.apache.doris.connector.cache.CacheSpec;
+import org.apache.doris.connector.cache.CatalogMetaCache;
+import org.apache.doris.connector.cache.JvmSizeUtils;
+import org.apache.doris.connector.cache.MetaCache;
+import org.apache.doris.connector.cache.MetaCacheDefinition;
+import org.apache.doris.connector.cache.MetaCacheSizeEstimators;
+import org.apache.doris.connector.cache.ScopePath;
+
+import org.apache.paimon.catalog.Catalog;
+import org.apache.paimon.catalog.CatalogLoader;
+import org.apache.paimon.catalog.Database;
+import org.apache.paimon.catalog.DelegateCatalog;
+import org.apache.paimon.catalog.Identifier;
+import org.apache.paimon.catalog.PropertyChange;
+import org.apache.paimon.fs.Path;
+import org.apache.paimon.options.CatalogOptions;
+import org.apache.paimon.options.MemorySize;
+import org.apache.paimon.options.Options;
+import org.apache.paimon.privilege.PrivilegedCatalog;
+import org.apache.paimon.schema.SchemaChange;
+import 
org.apache.paimon.shade.caffeine2.com.github.benmanes.caffeine.cache.Caffeine;
+import org.apache.paimon.table.FileStoreTable;
+import org.apache.paimon.table.Table;
+import org.apache.paimon.table.system.SystemTableLoader;
+import org.apache.paimon.utils.SegmentsCache;
+
+import java.time.Duration;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.LongSupplier;
+
+/**
+ * Doris-owned replacement for Paimon's {@code CachingCatalog}. Table and 
database entries live in
+ * {@link CatalogMetaCache}, so a Doris catalog/database/table invalidation 
fences every matching
+ * in-flight load and cached value.
+ *
+ * <p>The cache retains raw table metadata. Paimon's privilege catalog is 
applied outside this
+ * wrapper so every lookup receives a fresh checker instead of caching one 
authorization snapshot.
+ *
+ * <p>The user's {@code paimon.cache-enabled} and access/write expiry settings 
remain authoritative.
+ * The Paimon SDK wrapper itself is disabled because a second hidden table 
cache cannot participate
+ * in Doris invalidation. Under a Doris weight budget, mutable SDK 
snapshot/stats/manifest caches are
+ * not attached: their post-publication growth cannot be reweighed by the 
enclosing budget.
+ */
+final class PaimonMetaCacheCatalog extends DelegateCatalog {
+
+    private static final int DATABASE_CACHE_CAPACITY = 100;
+    static final long TABLE_ENTRY_OVERHEAD_BYTES = JvmSizeUtils.saturatedAdd(
+            JvmSizeUtils.instanceSize(ExpiringValue.class), 
JvmSizeUtils.instanceSize(AtomicLong.class));
+
+    private final CatalogMetaCache metaCache;
+    private final MetaCache<Identifier, ExpiringValue<Table>> tableCache;
+    private final MetaCache<String, ExpiringValue<Database>> databaseCache;
+    private final SegmentsCache<Path> manifestCache;
+    private final long tableExpireAfterAccessNanos;
+    private final long databaseExpireAfterAccessNanos;
+    private final long expireAfterWriteNanos;
+    private final int snapshotMaxNumPerTable;
+    private final boolean attachSdkCaches;
+    private final LongSupplier nanoTime;
+
+    static Catalog tryToCreate(Catalog wrapped, CatalogMetaCache metaCache, 
int tableCacheMaxSize,
+            long tableCacheTtlSecond, Options catalogOptions, boolean 
cacheEnabled,
+            boolean hasEnclosingWeightLimit) {
+        Catalog cached = new PaimonMetaCacheCatalog(wrapped, metaCache, 
tableCacheMaxSize,

Review Comment:
   [P1] Roll back cache registration when catalog decoration fails
   
   This constructor publishes the fixed `paimon-table` and `paimon-database` 
names before `PrivilegedCatalog.tryToCreate` completes. That later call eagerly 
reads the file-based privilege metadata and can fail transiently. 
`ensureCatalog` then leaves `catalog` null and retries on the next statement, 
but the same long-lived `CatalogMetaCache` rejects that retry with `Duplicate 
meta cache name: paimon-table`; the connector cannot recover even after 
storage/credentials recover, and the abandoned raw catalog is not closed. 
Please make construction transactional (or make these caches reusable) and add 
a fail-once late-construction test whose second lazy access succeeds.



##########
fe/fe-connector/fe-connector-paimon/src/main/java/org/apache/doris/connector/paimon/PaimonMetaCacheCatalog.java:
##########
@@ -0,0 +1,355 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.connector.paimon;
+
+import org.apache.doris.connector.cache.CacheSpec;
+import org.apache.doris.connector.cache.CatalogMetaCache;
+import org.apache.doris.connector.cache.JvmSizeUtils;
+import org.apache.doris.connector.cache.MetaCache;
+import org.apache.doris.connector.cache.MetaCacheDefinition;
+import org.apache.doris.connector.cache.MetaCacheSizeEstimators;
+import org.apache.doris.connector.cache.ScopePath;
+
+import org.apache.paimon.catalog.Catalog;
+import org.apache.paimon.catalog.CatalogLoader;
+import org.apache.paimon.catalog.Database;
+import org.apache.paimon.catalog.DelegateCatalog;
+import org.apache.paimon.catalog.Identifier;
+import org.apache.paimon.catalog.PropertyChange;
+import org.apache.paimon.fs.Path;
+import org.apache.paimon.options.CatalogOptions;
+import org.apache.paimon.options.MemorySize;
+import org.apache.paimon.options.Options;
+import org.apache.paimon.privilege.PrivilegedCatalog;
+import org.apache.paimon.schema.SchemaChange;
+import 
org.apache.paimon.shade.caffeine2.com.github.benmanes.caffeine.cache.Caffeine;
+import org.apache.paimon.table.FileStoreTable;
+import org.apache.paimon.table.Table;
+import org.apache.paimon.table.system.SystemTableLoader;
+import org.apache.paimon.utils.SegmentsCache;
+
+import java.time.Duration;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.LongSupplier;
+
+/**
+ * Doris-owned replacement for Paimon's {@code CachingCatalog}. Table and 
database entries live in
+ * {@link CatalogMetaCache}, so a Doris catalog/database/table invalidation 
fences every matching
+ * in-flight load and cached value.
+ *
+ * <p>The cache retains raw table metadata. Paimon's privilege catalog is 
applied outside this
+ * wrapper so every lookup receives a fresh checker instead of caching one 
authorization snapshot.
+ *
+ * <p>The user's {@code paimon.cache-enabled} and access/write expiry settings 
remain authoritative.
+ * The Paimon SDK wrapper itself is disabled because a second hidden table 
cache cannot participate
+ * in Doris invalidation. Under a Doris weight budget, mutable SDK 
snapshot/stats/manifest caches are
+ * not attached: their post-publication growth cannot be reweighed by the 
enclosing budget.
+ */
+final class PaimonMetaCacheCatalog extends DelegateCatalog {
+
+    private static final int DATABASE_CACHE_CAPACITY = 100;
+    static final long TABLE_ENTRY_OVERHEAD_BYTES = JvmSizeUtils.saturatedAdd(
+            JvmSizeUtils.instanceSize(ExpiringValue.class), 
JvmSizeUtils.instanceSize(AtomicLong.class));
+
+    private final CatalogMetaCache metaCache;
+    private final MetaCache<Identifier, ExpiringValue<Table>> tableCache;
+    private final MetaCache<String, ExpiringValue<Database>> databaseCache;
+    private final SegmentsCache<Path> manifestCache;
+    private final long tableExpireAfterAccessNanos;
+    private final long databaseExpireAfterAccessNanos;
+    private final long expireAfterWriteNanos;
+    private final int snapshotMaxNumPerTable;
+    private final boolean attachSdkCaches;
+    private final LongSupplier nanoTime;
+
+    static Catalog tryToCreate(Catalog wrapped, CatalogMetaCache metaCache, 
int tableCacheMaxSize,
+            long tableCacheTtlSecond, Options catalogOptions, boolean 
cacheEnabled,
+            boolean hasEnclosingWeightLimit) {
+        Catalog cached = new PaimonMetaCacheCatalog(wrapped, metaCache, 
tableCacheMaxSize,
+                tableCacheTtlSecond, catalogOptions, cacheEnabled, 
hasEnclosingWeightLimit, System::nanoTime);
+        return PrivilegedCatalog.tryToCreate(cached, catalogOptions);
+    }
+
+    PaimonMetaCacheCatalog(Catalog wrapped, CatalogMetaCache metaCache, int 
tableCacheMaxSize,
+            long tableCacheTtlSecond, Options catalogOptions, boolean 
hasEnclosingWeightLimit,
+            LongSupplier nanoTime) {
+        this(wrapped, metaCache, tableCacheMaxSize, tableCacheTtlSecond, 
catalogOptions,
+                true, hasEnclosingWeightLimit, nanoTime);
+    }
+
+    private PaimonMetaCacheCatalog(Catalog wrapped, CatalogMetaCache 
metaCache, int tableCacheMaxSize,
+            long tableCacheTtlSecond, Options catalogOptions, boolean 
cacheEnabled,
+            boolean hasEnclosingWeightLimit, LongSupplier nanoTime) {
+        super(wrapped);
+        this.metaCache = metaCache;
+        this.nanoTime = nanoTime;
+
+        Duration expireAfterAccess = 
catalogOptions.get(CatalogOptions.CACHE_EXPIRE_AFTER_ACCESS);

Review Comment:
   [P2] Do not parse settings for a disabled cache
   
   When `paimon.cache-enabled=false`, Paimon 1.3.1's 
`CachingCatalog.tryToCreate` returns the raw catalog before reading the 
access/write or per-table cache settings. This replacement still parses the two 
durations here and the snapshot maximum below, even when the cache is disabled 
(or SDK child caches are suppressed by an enclosing weight limit). Because 
these generic `paimon.cache.*` values are not parsed at statement time, an 
existing catalog with a dormant malformed value that was previously ignored now 
fails on its first lazy catalog access. Please only parse each option in the 
branch that consumes it and cover disabled/governed catalogs with unused 
malformed subordinate settings.



##########
fe/fe-connector/fe-connector-paimon/src/main/java/org/apache/doris/connector/paimon/PaimonCacheSizeEstimator.java:
##########
@@ -0,0 +1,152 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.connector.paimon;
+
+import org.apache.doris.connector.cache.JvmSizeUtils;
+import org.apache.doris.connector.cache.MetaCacheSizeEstimate;
+import org.apache.doris.connector.cache.ReflectiveObjectSizeEstimator;
+
+import org.apache.paimon.catalog.Identifier;
+import org.apache.paimon.fs.Path;
+import org.apache.paimon.privilege.PrivilegedFileStoreTable;
+import org.apache.paimon.table.CatalogEnvironment;
+import org.apache.paimon.table.DelegatedFileStoreTable;
+import org.apache.paimon.table.FallbackReadFileStoreTable;
+import org.apache.paimon.table.FileStoreTable;
+import org.apache.paimon.table.FormatTable;
+import org.apache.paimon.table.Table;
+import org.apache.paimon.table.iceberg.IcebergTable;
+import org.apache.paimon.table.lance.LanceTable;
+import org.apache.paimon.table.object.ObjectTable;
+
+import java.net.URI;
+import java.util.Collections;
+import java.util.IdentityHashMap;
+import java.util.Set;
+
+/**
+ * Retained-size formulas for Paimon table-cache entries.
+ *
+ * <p>The table's shallow size includes references to FileIO, catalog loaders, 
and lock factories,
+ * but their graphs are catalog-scoped executable services rather than 
entry-owned metadata. Walking
+ * those graphs both double-counts shared state and reaches strongly 
encapsulated JDK objects. The
+ * estimator therefore expands only immutable metadata owned by the entry.
+ */
+final class PaimonCacheSizeEstimator {
+    private PaimonCacheSizeEstimator() {
+    }
+
+    static MetaCacheSizeEstimate estimateTable(Identifier key, Table table, 
long entryOverheadBytes) {
+        if (table instanceof PrivilegedFileStoreTable) {
+            return MetaCacheSizeEstimate.incomplete(
+                    "authorization decorators must be applied outside the 
metadata cache");
+        }
+        long bytes = add(entryOverheadBytes, 
ReflectiveObjectSizeEstimator.estimateComplete(key));
+        if (table instanceof FileStoreTable) {
+            Set<Object> visited = Collections.newSetFromMap(new 
IdentityHashMap<>());
+            bytes = add(bytes, estimateFileStoreTable((FileStoreTable) table, 
visited));
+        } else {
+            bytes = add(bytes, JvmSizeUtils.instanceSize(table.getClass()));

Review Comment:
   [P1] Account the retained global-system-table state
   
   `sys.all_table_options` reaches this branch because its two-part identifier 
has no `$` system-table suffix. Paimon 1.3.1 builds that table with a retained 
`Map<Identifier, Map<String,String>>` containing every table's options, while 
its `ReadonlyTable.options()` returns an empty map. This code therefore counts 
only the shallow shell and synthesized public metadata, omits the catalog-sized 
retained map, and still marks the estimate complete; one admitted entry can 
exceed the configured global/catalog hard weight limit arbitrarily. Please 
explicitly estimate these global system-table graphs (or reject unsupported 
non-FileStore shapes as incomplete) and add a weighted `sys.all_table_options` 
case whose map exceeds the budget.



##########
fe/fe-connector/fe-connector-paimon/src/main/java/org/apache/doris/connector/paimon/PaimonMetaCacheCatalog.java:
##########
@@ -0,0 +1,355 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.connector.paimon;
+
+import org.apache.doris.connector.cache.CacheSpec;
+import org.apache.doris.connector.cache.CatalogMetaCache;
+import org.apache.doris.connector.cache.JvmSizeUtils;
+import org.apache.doris.connector.cache.MetaCache;
+import org.apache.doris.connector.cache.MetaCacheDefinition;
+import org.apache.doris.connector.cache.MetaCacheSizeEstimators;
+import org.apache.doris.connector.cache.ScopePath;
+
+import org.apache.paimon.catalog.Catalog;
+import org.apache.paimon.catalog.CatalogLoader;
+import org.apache.paimon.catalog.Database;
+import org.apache.paimon.catalog.DelegateCatalog;
+import org.apache.paimon.catalog.Identifier;
+import org.apache.paimon.catalog.PropertyChange;
+import org.apache.paimon.fs.Path;
+import org.apache.paimon.options.CatalogOptions;
+import org.apache.paimon.options.MemorySize;
+import org.apache.paimon.options.Options;
+import org.apache.paimon.privilege.PrivilegedCatalog;
+import org.apache.paimon.schema.SchemaChange;
+import 
org.apache.paimon.shade.caffeine2.com.github.benmanes.caffeine.cache.Caffeine;
+import org.apache.paimon.table.FileStoreTable;
+import org.apache.paimon.table.Table;
+import org.apache.paimon.table.system.SystemTableLoader;
+import org.apache.paimon.utils.SegmentsCache;
+
+import java.time.Duration;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.LongSupplier;
+
+/**
+ * Doris-owned replacement for Paimon's {@code CachingCatalog}. Table and 
database entries live in
+ * {@link CatalogMetaCache}, so a Doris catalog/database/table invalidation 
fences every matching
+ * in-flight load and cached value.
+ *
+ * <p>The cache retains raw table metadata. Paimon's privilege catalog is 
applied outside this
+ * wrapper so every lookup receives a fresh checker instead of caching one 
authorization snapshot.
+ *
+ * <p>The user's {@code paimon.cache-enabled} and access/write expiry settings 
remain authoritative.
+ * The Paimon SDK wrapper itself is disabled because a second hidden table 
cache cannot participate
+ * in Doris invalidation. Under a Doris weight budget, mutable SDK 
snapshot/stats/manifest caches are
+ * not attached: their post-publication growth cannot be reweighed by the 
enclosing budget.
+ */
+final class PaimonMetaCacheCatalog extends DelegateCatalog {
+
+    private static final int DATABASE_CACHE_CAPACITY = 100;
+    static final long TABLE_ENTRY_OVERHEAD_BYTES = JvmSizeUtils.saturatedAdd(
+            JvmSizeUtils.instanceSize(ExpiringValue.class), 
JvmSizeUtils.instanceSize(AtomicLong.class));
+
+    private final CatalogMetaCache metaCache;
+    private final MetaCache<Identifier, ExpiringValue<Table>> tableCache;
+    private final MetaCache<String, ExpiringValue<Database>> databaseCache;
+    private final SegmentsCache<Path> manifestCache;
+    private final long tableExpireAfterAccessNanos;
+    private final long databaseExpireAfterAccessNanos;
+    private final long expireAfterWriteNanos;
+    private final int snapshotMaxNumPerTable;
+    private final boolean attachSdkCaches;
+    private final LongSupplier nanoTime;
+
+    static Catalog tryToCreate(Catalog wrapped, CatalogMetaCache metaCache, 
int tableCacheMaxSize,
+            long tableCacheTtlSecond, Options catalogOptions, boolean 
cacheEnabled,
+            boolean hasEnclosingWeightLimit) {
+        Catalog cached = new PaimonMetaCacheCatalog(wrapped, metaCache, 
tableCacheMaxSize,
+                tableCacheTtlSecond, catalogOptions, cacheEnabled, 
hasEnclosingWeightLimit, System::nanoTime);
+        return PrivilegedCatalog.tryToCreate(cached, catalogOptions);
+    }
+
+    PaimonMetaCacheCatalog(Catalog wrapped, CatalogMetaCache metaCache, int 
tableCacheMaxSize,
+            long tableCacheTtlSecond, Options catalogOptions, boolean 
hasEnclosingWeightLimit,
+            LongSupplier nanoTime) {
+        this(wrapped, metaCache, tableCacheMaxSize, tableCacheTtlSecond, 
catalogOptions,
+                true, hasEnclosingWeightLimit, nanoTime);
+    }
+
+    private PaimonMetaCacheCatalog(Catalog wrapped, CatalogMetaCache 
metaCache, int tableCacheMaxSize,
+            long tableCacheTtlSecond, Options catalogOptions, boolean 
cacheEnabled,
+            boolean hasEnclosingWeightLimit, LongSupplier nanoTime) {
+        super(wrapped);
+        this.metaCache = metaCache;
+        this.nanoTime = nanoTime;
+
+        Duration expireAfterAccess = 
catalogOptions.get(CatalogOptions.CACHE_EXPIRE_AFTER_ACCESS);
+        Duration expireAfterWrite = 
catalogOptions.get(CatalogOptions.CACHE_EXPIRE_AFTER_WRITE);
+        if (cacheEnabled) {
+            requirePositive(expireAfterAccess, 
CatalogOptions.CACHE_EXPIRE_AFTER_ACCESS.key());
+            requirePositive(expireAfterWrite, 
CatalogOptions.CACHE_EXPIRE_AFTER_WRITE.key());
+        }
+        long paimonAccessNanos = cacheEnabled ? 
saturatedNanos(expireAfterAccess) : Long.MAX_VALUE;
+        this.tableExpireAfterAccessNanos = cacheEnabled && tableCacheTtlSecond 
> 0
+                ? Math.min(paimonAccessNanos, 
saturatedNanos(Duration.ofSeconds(tableCacheTtlSecond)))
+                : paimonAccessNanos;
+        this.databaseExpireAfterAccessNanos = paimonAccessNanos;
+        this.expireAfterWriteNanos = cacheEnabled ? 
saturatedNanos(expireAfterWrite) : Long.MAX_VALUE;
+
+        CacheSpec tableSpec = CacheSpec.of(cacheEnabled,
+                cacheEnabled && tableCacheTtlSecond > 0
+                        ? CacheSpec.CACHE_NO_TTL : 
CacheSpec.CACHE_TTL_DISABLE_CACHE,
+                tableCacheMaxSize);
+        this.tableCache = metaCache.create(MetaCacheDefinition
+                .<Identifier, ExpiringValue<Table>>builder("paimon-table", 
tableSpec,
+                        id -> ScopePath.table(id.getDatabaseName(), 
id.getTableName()))
+                .sizeEstimator((id, value) -> 
PaimonCacheSizeEstimator.estimateTable(
+                        id, value.value, TABLE_ENTRY_OVERHEAD_BYTES))
+                .build());
+        CacheSpec dbSpec = CacheSpec.of(cacheEnabled, cacheEnabled
+                ? CacheSpec.CACHE_NO_TTL : CacheSpec.CACHE_TTL_DISABLE_CACHE, 
DATABASE_CACHE_CAPACITY);
+        this.databaseCache = metaCache.create(MetaCacheDefinition
+                .<String, ExpiringValue<Database>>builder("paimon-database", 
dbSpec, ScopePath::database)
+                .sizeEstimator(MetaCacheSizeEstimators.reflective())
+                .build());
+
+        this.attachSdkCaches = cacheEnabled && !hasEnclosingWeightLimit;
+        this.manifestCache = attachSdkCaches ? 
buildManifestCache(catalogOptions) : null;
+        this.snapshotMaxNumPerTable = catalogOptions.get(
+                CatalogOptions.CACHE_SNAPSHOT_MAX_NUM_PER_TABLE);
+    }
+
+    @Override
+    public Table getTable(Identifier identifier) throws TableNotExistException 
{
+        if (identifier.isSystemTable()) {
+            Identifier origin = new Identifier(identifier.getDatabaseName(), 
identifier.getTableName(),
+                    identifier.getBranchName(), null);
+            Table originTable = getTable(origin);
+            if (!(originTable instanceof FileStoreTable)) {
+                return super.getTable(identifier);
+            }
+            Table systemTable = 
SystemTableLoader.load(identifier.getSystemTableName(),
+                    (FileStoreTable) originTable);
+            if (systemTable == null) {
+                throw new TableNotExistException(identifier);
+            }
+            return systemTable;
+        }
+
+        while (true) {
+            long now = nanoTime.getAsLong();
+            ExpiringValue<Table> cached = tableCache.getIfPresent(identifier);
+            if (cached != null) {
+                if (cached.tryAccess(now, tableExpireAfterAccessNanos, 
expireAfterWriteNanos)) {
+                    return cached.value;
+                }
+                tableCache.compareAndSet(identifier, cached, null);
+                continue;
+            }
+            try {
+                return tableCache.get(identifier, ignored -> {

Review Comment:
   [P2] Recheck expiry after the cache get
   
   After this initial miss, another thread can publish the same key before this 
`tableCache.get` runs. `MetaCache#get` then returns that existing value 
directly, but this branch unwraps `.value` without calling `tryAccess`; if the 
caller was paused past the configured access/write TTL, it returns an 
already-expired table. `getDatabase` has the same miss/get window. Please feed 
the value returned by `get` through the same expiry/CAS-removal loop and add a 
barrier-based race test for both caches.



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