-
Notifications
You must be signed in to change notification settings - Fork 3.9k
branch-4.1: [fix](fe) Prevent stale external name publication after refresh #67417
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: branch-4.1
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -81,7 +81,7 @@ public abstract class ExternalDatabase<T extends ExternalTable> | |
| @SerializedName(value = "initialized") | ||
| protected boolean initialized = false; | ||
| // table name lower case -> table name | ||
| private Map<String, String> lowerCaseToTableName = Maps.newConcurrentMap(); | ||
| private volatile Map<String, String> lowerCaseToTableName = Maps.newConcurrentMap(); | ||
| @SerializedName(value = "lastUpdateTime") | ||
| protected long lastUpdateTime; | ||
| protected final InitDatabaseLog.Type dbLogType; | ||
|
|
@@ -170,6 +170,9 @@ private void buildMetaCache() { | |
| OptionalLong.of(Config.external_cache_refresh_time_minutes * 60L), | ||
| Math.max(Config.max_meta_object_cache_num, 1), | ||
| ignored -> listTableNames(), | ||
| this::updateLowerCaseToTableName, | ||
| (remoteName, localName) -> lowerCaseToTableName.put(remoteName.toLowerCase(), remoteName), | ||
| localName -> lowerCaseToTableName.remove(localName.toLowerCase()), | ||
| localTableName -> Optional.ofNullable( | ||
| buildTableForInit(null, localTableName, | ||
| Util.genIdByName(extCatalog.getName(), name, localTableName), | ||
|
|
@@ -180,18 +183,15 @@ private void buildMetaCache() { | |
|
|
||
| private List<Pair<String, String>> listTableNames() { | ||
| List<Pair<String, String>> tableNames; | ||
| lowerCaseToTableName.clear(); | ||
| if (name.equals(InfoSchemaDb.DATABASE_NAME)) { | ||
| tableNames = ExternalInfoSchemaDatabase.listTableNames().stream() | ||
| .map(tableName -> { | ||
| lowerCaseToTableName.put(tableName.toLowerCase(), tableName); | ||
| return Pair.of(tableName, tableName); | ||
| }) | ||
| .collect(Collectors.toList()); | ||
| } else if (name.equals(MysqlDb.DATABASE_NAME)) { | ||
| tableNames = ExternalMysqlDatabase.listTableNames().stream() | ||
| .map(tableName -> { | ||
| lowerCaseToTableName.put(tableName.toLowerCase(), tableName); | ||
| return Pair.of(tableName, tableName); | ||
| }) | ||
| .collect(Collectors.toList()); | ||
|
|
@@ -218,7 +218,6 @@ private List<Pair<String, String>> listTableNames() { | |
| // Mode 2: preserve original remote case for display | ||
| localTableName = tableName; | ||
| } | ||
| lowerCaseToTableName.put(tableName.toLowerCase(), tableName); | ||
| return Pair.of(tableName, localTableName); | ||
| }).collect(Collectors.toList()); | ||
| } | ||
|
|
@@ -257,6 +256,12 @@ private List<Pair<String, String>> listTableNames() { | |
| return tableNames; | ||
| } | ||
|
|
||
| private void updateLowerCaseToTableName(List<Pair<String, String>> names) { | ||
| Map<String, String> updated = Maps.newConcurrentMap(); | ||
| names.forEach(pair -> updated.put(pair.key().toLowerCase(), pair.key())); | ||
| lowerCaseToTableName = updated; | ||
| } | ||
|
|
||
| public T buildTableForInit(String remoteTableName, String localTableName, long tblId, | ||
| ExternalCatalog catalog, ExternalDatabase db, boolean checkExists) { | ||
|
|
||
|
|
@@ -432,15 +437,10 @@ public DatabaseProperty getDbProperties() { | |
| public boolean isTableExist(String tableName) { | ||
| String remoteTblName = tableName; | ||
| if (this.isTableNamesCaseInsensitive()) { | ||
| metaCache.listNames(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P1] Initialize the database before reading its names cache On a cold object-cache miss, a database returned by |
||
| remoteTblName = lowerCaseToTableName.get(tableName.toLowerCase()); | ||
| if (remoteTblName == null) { | ||
| // Here we need to execute listTableNames() once to fill in lowerCaseToTableName | ||
| // to prevent lowerCaseToTableName from being empty in some cases | ||
| listTableNames(); | ||
| remoteTblName = lowerCaseToTableName.get(tableName.toLowerCase()); | ||
| if (remoteTblName == null) { | ||
| return false; | ||
| } | ||
| return false; | ||
| } | ||
| } | ||
| return extCatalog.tableExist(ConnectContext.get().getSessionContext(), remoteName, remoteTblName); | ||
|
|
@@ -509,26 +509,21 @@ private String getLocalTableName(String tableName, boolean isReplay) { | |
| finalName = tableName.toLowerCase(); | ||
| } | ||
| if (this.isTableNamesCaseInsensitive()) { | ||
| if (!isReplay) { | ||
| metaCache.listNames(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P2] Use the known event entry before requiring a full listing On a cold mode-2 cache, |
||
| } | ||
| finalName = lowerCaseToTableName.get(tableName.toLowerCase()); | ||
| if (finalName == null) { | ||
| if (isReplay) { | ||
| if (LOG.isDebugEnabled()) { | ||
| if (LOG.isDebugEnabled()) { | ||
| if (isReplay) { | ||
| LOG.debug("failed to get final table name from: {}.{}.{}, is replay = true", | ||
| getCatalog().getName(), getFullName(), tableName); | ||
| } | ||
| return null; | ||
| } | ||
| // Here we need to execute listTableNames() once to fill in lowerCaseToTableName | ||
| // to prevent lowerCaseToTableName from being empty in some cases | ||
| listTableNames(); | ||
| finalName = lowerCaseToTableName.get(tableName.toLowerCase()); | ||
| if (finalName == null) { | ||
| if (LOG.isDebugEnabled()) { | ||
| } else { | ||
| LOG.debug("failed to get final table name from: {}.{}.{}", | ||
| getCatalog().getName(), getFullName(), tableName); | ||
| } | ||
| return null; | ||
| } | ||
| return null; | ||
| } | ||
| } | ||
| if (LOG.isDebugEnabled()) { | ||
|
|
@@ -579,7 +574,6 @@ public void unregisterTable(String tableName) { | |
| if (isInitialized()) { | ||
| metaCache.invalidate(dorisTable.getName(), | ||
| Util.genIdByName(extCatalog.getName(), name, dorisTable.getName())); | ||
| lowerCaseToTableName.remove(dorisTable.getName().toLowerCase()); | ||
| } | ||
|
|
||
| Env.getCurrentEnv().getExtMetaCacheMgr().invalidateTableCache(dorisTable); | ||
|
|
@@ -602,7 +596,6 @@ public boolean registerTable(TableIf tableIf) { | |
| String localName = extCatalog.fromRemoteTableName(this.remoteName, tableName); | ||
| metaCache.updateCache(tableName, localName, (T) tableIf, | ||
| Util.genIdByName(extCatalog.getName(), name, localName)); | ||
| lowerCaseToTableName.put(tableName.toLowerCase(), tableName); | ||
| } | ||
| setLastUpdateTime(System.currentTimeMillis()); | ||
| return true; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[P1] Keep the routing-map clear atomic with names invalidation
resetToUninitialized()clearslowerCaseToDatabaseNameunder the catalog monitor, releases that monitor, and only afterward invalidatesmetaCachethroughonRefreshCache(). A concurrentgetDbNullable()can reinitialize in that gap; this call then hits the still-complete old names entry without republishing the cleared map, so a following differently-cased mode-2 lookup misses. This is separate from the stale-loader thread because no load runs in this interleaving. Please order the map clear and names invalidation under the same initialization fence (or republish one atomic snapshot), and add a paused reset test.