Skip to content

Commit c15972b

Browse files
authored
[BugFix] Relax DB lock to intensive path in qe/ read-only paths (#73067)
Signed-off-by: Kevin Cai <kevin.cai@celerdata.com>
1 parent 4c8f9c6 commit c15972b

2 files changed

Lines changed: 105 additions & 58 deletions

File tree

fe/fe-core/src/main/java/com/starrocks/qe/ConnectProcessor.java

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -775,17 +775,37 @@ private void handleFieldList() throws IOException {
775775
ctx.getState().setError("Unknown database(" + ctx.getDatabase() + ")");
776776
return;
777777
}
778-
Locker locker = new Locker();
779-
locker.lockDatabase(db.getId(), LockType.READ);
778+
// Internal catalog: lookup resolves to Database.nameToTable (a ConcurrentHashMap,
779+
// lock-free safe). External catalog: lookup routes through ConnectorMetadata, whose
780+
// concurrency is connector-managed. No FE write path takes the LockManager lock on
781+
// external db ids, so the intensive lock acquired below is harmless overhead on
782+
// external catalogs and the unlocked lookup changes nothing for them.
783+
Table table;
780784
try {
781-
// we should get table through metadata manager
782-
Table table = ctx.getGlobalStateMgr().getMetadataMgr().getTable(
785+
table = ctx.getGlobalStateMgr().getMetadataMgr().getTable(
783786
ctx, ctx.getCurrentCatalog(), ctx.getDatabase(), tableName);
784-
if (table == null) {
787+
} catch (StarRocksConnectorException e) {
788+
LOG.error("errors happened when getting table {}", tableName, e);
789+
ctx.getState().setEof();
790+
return;
791+
}
792+
if (table == null) {
793+
ctx.getState().setError("Unknown table(" + tableName + ")");
794+
return;
795+
}
796+
797+
Locker locker = new Locker();
798+
locker.lockTableWithIntensiveDbLock(db.getId(), table.getId(), LockType.READ);
799+
try {
800+
// Revalidate by name to detect concurrent DROP/RENAME between unlocked lookup
801+
// and lock acquisition. The table id is stable, so id-based revalidation would
802+
// miss a RENAME that re-binds the name to a different table. Internal-catalog
803+
// only: LocalMetastore doesn't track connector tables, so a re-fetch for
804+
// external catalogs would always return null and falsely fail.
805+
if (db.getCatalogName() == null && db.getTable(tableName) != table) {
785806
ctx.getState().setError("Unknown table(" + tableName + ")");
786807
return;
787808
}
788-
789809
MysqlSerializer serializer = ctx.getSerializer();
790810
MysqlChannel channel = ctx.getMysqlChannel();
791811

@@ -800,7 +820,7 @@ private void handleFieldList() throws IOException {
800820
} catch (StarRocksConnectorException e) {
801821
LOG.error("errors happened when getting table {}", tableName, e);
802822
} finally {
803-
locker.unLockDatabase(db.getId(), LockType.READ);
823+
locker.unLockTableWithIntensiveDbLock(db.getId(), table.getId(), LockType.READ);
804824
}
805825
ctx.getState().setEof();
806826
}

fe/fe-core/src/main/java/com/starrocks/qe/ShowExecutor.java

Lines changed: 78 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -848,49 +848,70 @@ private ShowResultSet showCreateInternalCatalogTable(ShowCreateTableStmt showStm
848848
Database db = GlobalStateMgr.getCurrentState().getLocalMetastore().getDb(showStmt.getDb());
849849
MetaUtils.checkDbNullAndReport(db, showStmt.getDb());
850850
List<List<String>> rows = Lists.newArrayList();
851-
Locker locker = new Locker();
852-
locker.lockDatabase(db.getId(), LockType.READ);
853-
try {
854-
TableName tableName = new TableName(showStmt.getCatalogName(), showStmt.getDb(), showStmt.getTable());
855-
Table table = MetaUtils.getSessionAwareTable(connectContext, db, tableName);
856-
if (table == null) {
857-
if (showStmt.getType() != ShowCreateTableStmt.CreateTableType.MATERIALIZED_VIEW) {
858-
ErrorReport.reportSemanticException(ErrorCode.ERR_BAD_TABLE_ERROR, showStmt.getTable());
859-
} else {
860-
// For Sync Materialized View, it is a mv index inside OLAP table,
861-
// so we can not get it from database.
862-
for (Table tbl : GlobalStateMgr.getCurrentState().getLocalMetastore().getTables(db.getId())) {
863-
if (tbl.getType() == Table.TableType.OLAP) {
864-
OlapTable olapTable = (OlapTable) tbl;
865-
List<MaterializedIndexMeta> visibleMaterializedViews =
866-
olapTable.getVisibleIndexMetas();
867-
for (MaterializedIndexMeta mvMeta : visibleMaterializedViews) {
868-
if (olapTable.getIndexNameByMetaId(mvMeta.getIndexMetaId()).equals(showStmt.getTable())) {
869-
if (mvMeta.getOriginStmt() == null) {
870-
String mvName = olapTable.getIndexNameByMetaId(mvMeta.getIndexMetaId());
871-
rows.add(Lists.newArrayList(showStmt.getTable(),
872-
ShowMaterializedViewStatus.buildCreateMVSql(olapTable,
873-
mvName, mvMeta), "utf8", "utf8_general_ci"));
874-
} else {
875-
rows.add(Lists.newArrayList(showStmt.getTable(), mvMeta.getOriginStmt(),
876-
"utf8", "utf8_general_ci"));
877-
}
878-
879-
ShowResultSetMetaData showResultSetMetaData = ShowResultSetMetaData.builder()
880-
.addColumn(new Column("Materialized View",
881-
TypeFactory.createVarcharType(20)))
882-
.addColumn(new Column("Create Materialized View",
883-
TypeFactory.createVarcharType(30)))
884-
.build();
885-
return new ShowResultSet(showResultSetMetaData, rows);
851+
TableName tableName = new TableName(showStmt.getCatalogName(), showStmt.getDb(), showStmt.getTable());
852+
// Lookup is ConcurrentHashMap-backed (Database.nameToTable) for internal catalog and
853+
// throws SemanticException if the table is missing, so it is safe outside the lock.
854+
Table table = MetaUtils.getSessionAwareTable(connectContext, db, tableName);
855+
// TODO(sync-mv): the (table == null) branch below is currently unreachable -
856+
// MetaUtils.getSessionAwareTable throws SemanticException on miss rather than
857+
// returning null (since #43162, "temporary table part-1"), so the sync-MV
858+
// fallback never runs. As a side effect, SHOW CREATE MATERIALIZED VIEW <sync_mv>
859+
// has been broken since that change (sync MVs live as MaterializedIndexMeta
860+
// inside an OLAP table, not as separately-registered Tables, so they are missed
861+
// by the throwing lookup above). The fallback is kept here as a marker until a
862+
// follow-up commit reworks the lookup to make this path reachable. When that
863+
// happens, the iteration must run under DB READ (or per-table READ) - it reads
864+
// HashMap-backed OlapTable index metadata which is mutated by concurrent
865+
// ALTER/rollup. Today the unlocked iteration is harmless because it never
866+
// executes.
867+
if (table == null) {
868+
if (showStmt.getType() != ShowCreateTableStmt.CreateTableType.MATERIALIZED_VIEW) {
869+
ErrorReport.reportSemanticException(ErrorCode.ERR_BAD_TABLE_ERROR, showStmt.getTable());
870+
} else {
871+
// For Sync Materialized View, it is a mv index inside OLAP table,
872+
// so we can not get it from database.
873+
for (Table tbl : GlobalStateMgr.getCurrentState().getLocalMetastore().getTables(db.getId())) {
874+
if (tbl.getType() == Table.TableType.OLAP) {
875+
OlapTable olapTable = (OlapTable) tbl;
876+
List<MaterializedIndexMeta> visibleMaterializedViews =
877+
olapTable.getVisibleIndexMetas();
878+
for (MaterializedIndexMeta mvMeta : visibleMaterializedViews) {
879+
if (olapTable.getIndexNameByMetaId(mvMeta.getIndexMetaId()).equals(showStmt.getTable())) {
880+
if (mvMeta.getOriginStmt() == null) {
881+
String mvName = olapTable.getIndexNameByMetaId(mvMeta.getIndexMetaId());
882+
rows.add(Lists.newArrayList(showStmt.getTable(),
883+
ShowMaterializedViewStatus.buildCreateMVSql(olapTable,
884+
mvName, mvMeta), "utf8", "utf8_general_ci"));
885+
} else {
886+
rows.add(Lists.newArrayList(showStmt.getTable(), mvMeta.getOriginStmt(),
887+
"utf8", "utf8_general_ci"));
886888
}
889+
890+
ShowResultSetMetaData showResultSetMetaData = ShowResultSetMetaData.builder()
891+
.addColumn(new Column("Materialized View",
892+
TypeFactory.createVarcharType(20)))
893+
.addColumn(new Column("Create Materialized View",
894+
TypeFactory.createVarcharType(30)))
895+
.build();
896+
return new ShowResultSet(showResultSetMetaData, rows);
887897
}
888898
}
889899
}
890-
ErrorReport.reportSemanticException(ErrorCode.ERR_BAD_TABLE_ERROR, showStmt.getTable());
891900
}
901+
ErrorReport.reportSemanticException(ErrorCode.ERR_BAD_TABLE_ERROR, showStmt.getTable());
902+
}
903+
}
904+
Locker locker = new Locker();
905+
locker.lockTableWithIntensiveDbLock(db.getId(), table.getId(), LockType.READ);
906+
try {
907+
// Revalidate by name to detect concurrent DROP/RENAME between unlocked lookup
908+
// and lock acquisition. The table id is stable for a given Table object, so an
909+
// id-based check would miss a RENAME that re-binds the name to a different
910+
// (or no) table; revalidate via the same lookup path so both regular and
911+
// temporary tables are handled correctly.
912+
if (MetaUtils.getSessionAwareTable(connectContext, db, tableName) != table) {
913+
ErrorReport.reportSemanticException(ErrorCode.ERR_BAD_TABLE_ERROR, showStmt.getTable());
892914
}
893-
894915
List<String> createTableStmt = Lists.newArrayList();
895916
AstToStringBuilder.getDdlStmt(table, createTableStmt, null, null, false, true /* hide password */);
896917
if (createTableStmt.isEmpty()) {
@@ -943,7 +964,7 @@ private ShowResultSet showCreateInternalCatalogTable(ShowCreateTableStmt showStm
943964
return new ShowResultSet(showResultMetaFactory.getMetadata(showStmt), rows);
944965
}
945966
} finally {
946-
locker.unLockDatabase(db.getId(), LockType.READ);
967+
locker.unLockTableWithIntensiveDbLock(db.getId(), table.getId(), LockType.READ);
947968
}
948969
}
949970

@@ -1882,7 +1903,7 @@ public ShowResultSet visitShowTabletStatement(ShowTabletStmt statement, ConnectC
18821903
dbName = db.getFullName();
18831904

18841905
Locker locker = new Locker();
1885-
locker.lockDatabase(db.getId(), LockType.READ);
1906+
locker.lockTableWithIntensiveDbLock(db.getId(), tableId, LockType.READ);
18861907
try {
18871908
Table table = GlobalStateMgr.getCurrentState().getLocalMetastore().getTable(db.getId(), tableId);
18881909
if (!(table instanceof OlapTable)) {
@@ -1943,7 +1964,7 @@ public ShowResultSet visitShowTabletStatement(ShowTabletStmt statement, ConnectC
19431964
}
19441965

19451966
} finally {
1946-
locker.unLockDatabase(db.getId(), LockType.READ);
1967+
locker.unLockTableWithIntensiveDbLock(db.getId(), tableId, LockType.READ);
19471968
}
19481969
} while (false);
19491970

@@ -1962,19 +1983,25 @@ public ShowResultSet visitShowTabletStatement(ShowTabletStmt statement, ConnectC
19621983
Database db = globalStateMgr.getLocalMetastore().getDb(dbName);
19631984
MetaUtils.checkDbNullAndReport(db, dbName);
19641985

1986+
// Lookup is ConcurrentHashMap-backed for the internal catalog and throws on missing
1987+
// table, so it is safe outside the lock.
1988+
TableName tableName = new TableName(tableRef.getCatalogName(), tableRef.getDbName(),
1989+
tableRef.getTableName());
1990+
Table table = MetaUtils.getSessionAwareTable(context, db, tableName);
1991+
if (!table.isNativeTableOrMaterializedView()) {
1992+
ErrorReport.reportSemanticException(ErrorCode.ERR_NOT_OLAP_TABLE, statement.getTableName());
1993+
}
1994+
19651995
Locker locker = new Locker();
1966-
locker.lockDatabase(db.getId(), LockType.READ);
1996+
locker.lockTableWithIntensiveDbLock(db.getId(), table.getId(), LockType.READ);
19671997
try {
1968-
Table table = MetaUtils.getSessionAwareTable(
1969-
context, db, new TableName(tableRef.getCatalogName(), tableRef.getDbName(),
1970-
tableRef.getTableName()));
1971-
if (table == null) {
1972-
ErrorReport.reportSemanticException(ErrorCode.ERR_BAD_TABLE_ERROR, statement.getTableName());
1973-
}
1974-
if (!table.isNativeTableOrMaterializedView()) {
1975-
ErrorReport.reportSemanticException(ErrorCode.ERR_NOT_OLAP_TABLE, statement.getTableName());
1998+
// Revalidate by name to detect concurrent DROP/RENAME between unlocked lookup
1999+
// and lock acquisition. Id-based check would miss a RENAME that re-binds the
2000+
// name to a different table; re-running the same lookup catches that.
2001+
if (MetaUtils.getSessionAwareTable(context, db, tableName) != table) {
2002+
ErrorReport.reportSemanticException(ErrorCode.ERR_BAD_TABLE_ERROR,
2003+
statement.getTableName());
19762004
}
1977-
19782005
Pair<Boolean, Boolean> privResult = Authorizer.checkPrivForShowTablet(
19792006
context, db.getFullName(), table);
19802007
if (!privResult.first) {
@@ -2068,7 +2095,7 @@ context, db, new TableName(tableRef.getCatalogName(), tableRef.getDbName(),
20682095
rows.add(oneTablet);
20692096
}
20702097
} finally {
2071-
locker.unLockDatabase(db.getId(), LockType.READ);
2098+
locker.unLockTableWithIntensiveDbLock(db.getId(), table.getId(), LockType.READ);
20722099
}
20732100
}
20742101

0 commit comments

Comments
 (0)