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


##########
fe/fe-core/src/main/java/org/apache/doris/datasource/ExternalMetaCacheMgr.java:
##########
@@ -185,6 +186,7 @@ public void invalidateCatalog(long catalogId) {
         // REFRESH must drop ALL of its entries -- else binary-search pruning 
could serve ranges older than
         // the refreshed metadata. Coarse but correct (a rebuild is cheap and 
lazy). Mirrors invalidateTable.
         invalidateSortedPartitionsCache();
+        rowCountCache.invalidateCatalog(catalogId);

Review Comment:
   **[P1] Invalidate row counts when properties replace the source**
   
   This catalog-scope row-count eviction is reached by `onRefreshCache(true)` 
and catalog removal, but ordinary ALTER CATALOG property application calls 
`resetToUninitialized(false, true)` (or `notifyPropertiesUpdated -> 
resetToUninitialized(false)`). That closes and rebuilds the plugin connector 
and clears the DB metadata caches, while `onRefreshCache(false)` skips this 
method; only the special schema-cache-TTL property subsequently removes the 
catalog cache. Changing an endpoint, warehouse, or other source-defining 
property therefore rebuilds the same local catalog/DB/table IDs while retaining 
a count fetched from the old source, so the first post-ALTER lookup can hit it 
without consulting the new connector. Replay uses the same property-application 
path. Treat every property reset that replaces the connector/source as a 
catalog row-count barrier, ordered after source teardown and metadata cleanup, 
and test a non-schema-cache source property on both coordinator and replay.



##########
fe/fe-core/src/main/java/org/apache/doris/datasource/plugin/PluginDrivenExternalCatalog.java:
##########
@@ -748,8 +748,9 @@ public void dropTable(String dbName, String tableName, 
boolean isView, boolean i
             // Uniform with the table branch: drop the connector's own caches 
for this name (harmless no-op
             // for a view, which carries no snapshot pin). Keyed by the REMOTE 
names.
             connector.invalidateTable(dorisTable.getRemoteDbName(), 
dorisTable.getRemoteName());
-            Env.getCurrentEnv().getEditLog().logDropTable(new 
DropInfo(getName(), dbName, tableName));
-            getDbForReplay(dbName).ifPresent(d -> 
d.unregisterTable(tableName));
+            Env.getCurrentEnv().getEditLog().logDropTable(
+                    new DropInfo(getName(), db.getFullName(), 
dorisTable.getName()));
+            getDbForReplay(db.getFullName()).ifPresent(d -> 
d.unregisterTable(dorisTable.getName()));

Review Comment:
   **[P1] Do not gate DROP cleanup on a second DB-cache hit**
   
   The method already retains exact `db` and `dorisTable` identities, but after 
the remote DROP and connector invalidation it routes all local table/row-count 
cleanup through this second cache-only lookup. The bounded top-level DB cache 
can passively evict during the remote operation; its synchronous listener calls 
`resetMetaToUninitialized()`, which now clears the table objects/ID index and 
intentionally preserves row counts. This lookup then returns empty, so the 
leader skips exact eviction even though it never replays its own journal. The 
table branch at line 782 has the same race, and cold/uninitialized follower 
`replayDropTable` similarly widens connector invalidation but lets the base 
optional lookup skip local/row-count cleanup. A same-name recreation then 
reuses the stale key. Make final cleanup independent of shared-cache residency 
using the retained exact IDs (or deterministic logged identities); merely 
calling the evicted DB's `unregisterTable` is insufficient because its 
 index was cleared. Add forced-eviction tests for table/view coordinator DROP 
and cold replay.



##########
fe/fe-core/src/main/java/org/apache/doris/datasource/plugin/PluginDrivenExternalCatalog.java:
##########
@@ -819,7 +821,7 @@ public void renameTable(String dbName, String oldTableName, 
String newTableName)
         // local->remote mapping). Followers propagate this via 
RefreshManager.replayRefreshTable's rename branch.
         connector.invalidateTable(dorisTable.getRemoteDbName(), 
dorisTable.getRemoteName());
         connector.invalidateTable(dorisTable.getRemoteDbName(), newTableName);
-        afterExternalRename(dbName, oldTableName, newTableName);
+        afterExternalRename(db.getFullName(), dorisTable.getName(), 
newTableName);

Review Comment:
   **[P1] Preserve the exact source identity across RENAME**
   
   After the remote rename and both connector invalidations, 
`afterExternalRename` discards the retained `db`/`dorisTable` identities and 
gates old-name cleanup on another `getDbForReplay`. If the bounded DB cache 
passively evicts in that interval, its synchronous listener clears table 
objects/IDs but now deliberately preserves row counts; the helper skips 
unregister/reset, and the leader does not replay its own rename journal. This 
breaks the atomic-swap flow documented immediately above: with count 100 cached 
for `t`, eviction during `RENAME t TO t_arch`, followed by `RENAME t_new TO t`, 
lets the new table reuse deterministic key `t` and inherit 100. Thread the 
retained exact IDs into final bookkeeping and evict the old row-count key 
independently of cache residency (calling the reset DB's unregister alone is 
insufficient after its index was cleared). Add an eviction hook between remote 
rename and local bookkeeping and exercise the two-rename swap with distinct 
counts.



##########
fe/fe-core/src/main/java/org/apache/doris/datasource/plugin/PluginDrivenExternalCatalog.java:
##########
@@ -551,7 +551,7 @@ public boolean createTable(CreateTableInfo createTableInfo) 
throws UserException
                 // the table -- a table created out-of-band (another engine / 
another FE) stays invisible to
                 // SHOW TABLES and to a subsequent SELECT until an unrelated 
refresh. Every successful no-op
                 // must refresh the names, exactly like the created-here path.
-                getDbForReplay(createTableInfo.getDbName()).ifPresent(d -> 
d.resetMetaCacheNames());
+                getDbForReplay(db.getFullName()).ifPresent(d -> 
d.resetMetaCacheNames());

Review Comment:
   **[P1] Make CREATE/REGISTER the final row-count barrier**
   
   This successful `IF NOT EXISTS` convergence only refreshes the table-name 
cache. Plugin table/database IDs are deterministic from local names, so after 
an out-of-band drop+recreate that this FE did not observe, the new incarnation 
reuses the old `ExternalRowCountCache` key and the next `getRowCount()` returns 
the previous object's count. The created-here arm, concurrent-create recovery, 
`replayCreateTable`, and metastore `REGISTER_TABLE`/`REGISTER_DATABASE` paths 
have the same omission: they invalidate connector/name state or publish the 
object, but never make row-count eviction the final barrier. No contract 
requires a REGISTER descriptor or this convergence path to be preceded by a 
successfully applied UNREGISTER on this FE. This is independent of the existing 
Cache B thread. Please evict the exact table key on every successful/no-op 
table CREATE/REGISTER, use DB scope for database CREATE/REGISTER, mirror it in 
replay, and add a preseeded same-name-reuse 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