Skip to content

Commit a70368a

Browse files
committed
[#28877] YSQL: (Auto) ANALYZE will be aborted when a global DDL runs on a different DB
Summary: 9e1c574 and f66094f previous implemented a way for DDLs concurrent with (auto) ANALYZE to be aborted. This approach does not work for the following case 1. Global DDL run against db1 - takes a for share lock on pg_yb_catalog_version row for db1 2. ANALYZE run against db2 - takes a for update lock on pg_yb_catalog_version row for db2 To solve, this auto ANALYZE is being changed to a take a lock on all rows of pg_yb_catalog_version. This solves the case above and is verified with a unit test. Auto analyze currently does not run ANALYZE on different DBs concurrently so there is no effect on it due to this. New approach 1. Global/Regular DDL run against db1 - takes a for share lock on pg_yb_catalog_version row for db1 2. ANALYZE run against db2 - takes a for update lock on all rows in pg_yb_catalog_version. The other possible way to solve this is to have a global DDL lock all rows of pg_yb_catalog_version - however global DDL are not always easy to identify at the start of a DDL, which is when this lock needs to be taken. Jira: DB-18598 Test Plan: Added new test cases. Reviewers: pjain, yguan Reviewed By: yguan Subscribers: yql Differential Revision: https://phorge.dev.yugabyte.com/D47347
1 parent 228b4e5 commit a70368a

File tree

2 files changed

+57
-13
lines changed

2 files changed

+57
-13
lines changed

src/postgres/src/backend/catalog/yb_catalog/yb_catalog_version.c

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -105,18 +105,19 @@ YbMaybeLockMasterCatalogVersion()
105105
* When auto analyze is turned on, we don't want ANALYZE DDL triggered by auto analyze to
106106
* abort user DDLs. To achieve this, regular DDLs take a FOR KEY SHARE lock on the catalog version
107107
* row with a high priority (see pg_session.cc) while ANALYZE triggered by auto analyze takes
108-
* a low priority FOR UPDATE lock on the row.
109-
* (1) Global DDLs (i.e., those that modify shared catalog tables) will increment all catalog
110-
* versions. We still only lock the catalog version of the current database. So, they might
111-
* still face the problem described above that ANALYZE can abort global DDL run on a
112-
* different DB.
108+
* a low priority FOR UPDATE lock on all rows of the catalog version table.
109+
* (1) Global DDLs (i.e., those that modify shared catalog tables) increment all catalog
110+
* versions at the end of the DDL but only lock the catalog version of the current database
111+
* when they start. To enable them to abort ANALYZE on different DBs that can conflict with
112+
* them, ANALYZE locks all rows of catalog version table. It is challenging to identify a
113+
* global DDL at the start of a DDL so the other way around does not work.
113114
* (2) We enable this feature only if the invalidation messages are used and per-database catalog
114115
* version mode is enabled.
115116
*
116117
* TODO(#27037): Re-enable table locks check when concurrent DDL is ready.
117118
*/
118119
if (yb_user_ddls_preempt_auto_analyze &&
119-
/* !*YBCGetGFlags()->enable_object_locking_for_table_locks && */
120+
/* !*YBCGetGFlags()->enable_object_locking_for_table_locks && */
120121
YbIsInvalidationMessageEnabled() && YBIsDBCatalogVersionMode())
121122
{
122123
elog(DEBUG3, "Locking catalog version for db oid %d", MyDatabaseId);
@@ -923,13 +924,15 @@ YbGetMasterCatalogVersionFromTable(Oid db_oid, uint64_t *version,
923924
false /* is_region_local */ ,
924925
&ybc_stmt));
925926

926-
Datum oid_datum = Int32GetDatum(db_oid);
927-
YbcPgExpr pkey_expr = YBCNewConstant(ybc_stmt, oid_attrdesc->atttypid,
928-
oid_attrdesc->attcollation, oid_datum,
929-
false /* is_null */ );
930-
931-
HandleYBStatus(YBCPgDmlBindColumn(ybc_stmt, 1, pkey_expr));
927+
if (!(acquire_lock && yb_use_internal_auto_analyze_service_conn))
928+
{
929+
Datum oid_datum = Int32GetDatum(db_oid);
930+
YbcPgExpr pkey_expr = YBCNewConstant(ybc_stmt, oid_attrdesc->atttypid,
931+
oid_attrdesc->attcollation,
932+
oid_datum, false /* is_null */ );
932933

934+
HandleYBStatus(YBCPgDmlBindColumn(ybc_stmt, 1, pkey_expr));
935+
}
933936
for (AttrNumber attnum = 1; attnum <= natts; ++attnum)
934937
YbDmlAppendTargetRegularAttr(&Desc_pg_yb_catalog_version[attnum - 1],
935938
ybc_stmt);

src/yb/yql/pgwrapper/pg_auto_analyze-test.cc

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1241,8 +1241,10 @@ class PgConcurrentDDLAnalyzeTest : public LibPqTestBase {
12411241
};
12421242

12431243
TEST_F(PgConcurrentDDLAnalyzeTest, ConcurrentDDLAnalyze) {
1244+
const std::string another_db_name = "another_db";
12441245
auto* ts1 = cluster_->tserver_daemons()[0];
12451246
auto* ts2 = cluster_->tserver_daemons()[1];
1247+
auto* ts3 = cluster_->tserver_daemons()[2];
12461248
auto conn1 = ASSERT_RESULT(PGConnBuilder({
12471249
.host = ts1->bind_host(),
12481250
.port = ts1->ysql_port(),
@@ -1257,7 +1259,9 @@ TEST_F(PgConcurrentDDLAnalyzeTest, ConcurrentDDLAnalyze) {
12571259
const std::string wait_string = "sleeping for 20000000 us before next ddl";
12581260
const std::string wait_time_str = "'20s'";
12591261

1260-
ASSERT_OK(conn1.Execute("CREATE TABLE test1(k INT PRIMARY KEY, v INT) SPLIT INTO 1 TABLETS"));
1262+
ASSERT_OK(conn1.Execute("CREATE DATABASE " + another_db_name));
1263+
ASSERT_OK(conn1.Execute(
1264+
"CREATE ROLE role1; CREATE TABLE test1(k INT PRIMARY KEY, v INT) SPLIT INTO 1 TABLETS"));
12611265
ASSERT_OK(conn1.Execute("INSERT INTO test1 (SELECT * FROM generate_series(1,10))"));
12621266

12631267
// All DDLs on conn1 are delayed 20s prior to commit. Top-level retries are disabled.
@@ -1301,6 +1305,43 @@ TEST_F(PgConcurrentDDLAnalyzeTest, ConcurrentDDLAnalyze) {
13011305
ASSERT_OK(LogWaiter(ts1, wait_string).WaitFor(30s));
13021306
ASSERT_OK(conn2.Execute("ALTER TABLE test4 ADD COLUMN v1 INT"));
13031307
thread_holder.JoinAll();
1308+
1309+
auto another_db_conn = ASSERT_RESULT(
1310+
pgwrapper::PGConnBuilder(
1311+
{.host = ts3->bind_host(), .port = ts3->ysql_port(), .dbname = another_db_name})
1312+
.Connect());
1313+
ASSERT_OK(
1314+
another_db_conn.Execute("SET yb_max_query_layer_retries=0; SET log_min_messages=DEBUG1;"));
1315+
ASSERT_OK(another_db_conn.Execute(
1316+
"CREATE TABLE " + another_db_name + "_test1(k INT PRIMARY KEY, v INT) split into 1 tablets"));
1317+
1318+
// Case: Non-global DDL in different databases can run concurrently
1319+
thread_holder.AddThreadFunctor(
1320+
[&conn1]() -> void { ASSERT_OK(conn1.Execute("ALTER TABLE test1 ADD COLUMN v1 INT")); });
1321+
ASSERT_OK(LogWaiter(ts1, wait_string).WaitFor(30s));
1322+
ASSERT_OK(another_db_conn.Execute("RESET yb_test_delay_next_ddl"));
1323+
ASSERT_OK(another_db_conn.Execute("ALTER TABLE " + another_db_name + "_test1 ADD COLUMN v1 INT"));
1324+
thread_holder.JoinAll();
1325+
1326+
// Case: A long (auto) ANALYZE can be interrupted by a DDL in another database
1327+
thread_holder.AddThreadFunctor([&conn1]() -> void {
1328+
ASSERT_OK(conn1.Execute("SET yb_use_internal_auto_analyze_service_conn=true"));
1329+
ASSERT_NOK(conn1.Execute("ANALYZE test1"));
1330+
ASSERT_OK(conn1.Execute("SET yb_use_internal_auto_analyze_service_conn=false"));
1331+
});
1332+
ASSERT_OK(LogWaiter(ts1, wait_string).WaitFor(30s));
1333+
ASSERT_OK(another_db_conn.Execute("ALTER ROLE role1 SET log_min_messages=DEBUG1"));
1334+
thread_holder.JoinAll();
1335+
1336+
// Case: A long running DDL in a different database cannot be interrupted by (auto) ANALYZE
1337+
ASSERT_OK(another_db_conn.Execute("SET yb_test_delay_next_ddl=" + wait_time_str + ";"));
1338+
thread_holder.AddThreadFunctor([&another_db_conn]() -> void {
1339+
ASSERT_OK(another_db_conn.Execute("ALTER ROLE role1 SET log_min_messages=DEBUG2"));
1340+
});
1341+
ASSERT_OK(LogWaiter(ts3, wait_string).WaitFor(30s));
1342+
ASSERT_NOK(conn2.Execute("SET yb_use_internal_auto_analyze_service_conn=true; ANALYZE test1"));
1343+
ASSERT_OK(conn2.Execute("SET yb_use_internal_auto_analyze_service_conn=false;"));
1344+
thread_holder.JoinAll();
13041345
}
13051346

13061347
} // namespace pgwrapper

0 commit comments

Comments
 (0)