hudi-agent commented on code in PR #19923:
URL: https://github.com/apache/hudi/pull/19923#discussion_r3995905296


##########
hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/common/HoodieSparkSqlTestBase.scala:
##########
@@ -108,33 +140,107 @@ class HoodieSparkSqlTestBase extends FunSuite with 
BeforeAndAfterAll {
   }
 
   override protected def test(testName: String, testTags: Tag*)(testFun: => 
Any /* Assertion */)(implicit pos: source.Position): Unit = {
-    super.test(testName, testTags: _*)(
+    super.test(testName, testTags: _*)({
+      // Held for the whole test, cleanup included, so an ExclusiveSuite never 
overlaps it.
+      val readLock = HoodieSparkSqlTestBase.suiteLock.readLock()
+      readLock.lock()
       try {
-        testFun
-      } finally {
-        // The INMEMORY index keeps a JVM-static record-location map; reset it 
after every test so
-        // stale keys from an earlier test cannot misroute writes in a later 
one. withRecordType
-        // clears it between record-type iterations, but only on success and 
only for tests that use
-        // it, so a throwing or non-withRecordType INMEMORY test would 
otherwise leak state here.
-        // Runs before the catalog cleanup so it holds even if a drop throws.
-        HoodieInMemoryHashIndex.clear()
-        val catalog = spark.sessionState.catalog
-        catalog.listDatabases().foreach { db =>
-          catalog.listTables(db).foreach { table =>
-            catalog.dropTable(table, true, true)
+        try {
+          if (sharedSessionEnabled) {
+            bindSuiteSession()
           }
+          testFun
+        } finally {
+          // The INMEMORY index keeps a JVM-static record-location map; reset 
it after every test so
+          // stale keys from an earlier test cannot misroute writes in a later 
one. withRecordType
+          // clears it between record-type iterations, but only on success and 
only for tests that use
+          // it, so a throwing or non-withRecordType INMEMORY test would 
otherwise leak state here.
+          // Runs before the catalog cleanup so it holds even if a drop 
throws. In shared mode this is
+          // a no-op, see clearInMemoryIndex.
+          clearInMemoryIndex()
+          dropSuiteTables()
         }
+      } finally {
+        readLock.unlock()
       }
-    )
+    })
+  }
+
+  /**
+   * Per-suite mode: reset the JVM-static INMEMORY index between tests (see 
the note in test()).
+   * Shared mode: the index is keyed per table and dropSuiteTables clears each 
dropped table's
+   * entry, so a global clear would only wipe other suites' tables.
+   */
+  protected def clearInMemoryIndex(): Unit = {

Review Comment:
   🤖 The description leans on the `tagLocation` stale-location guard for shared 
mode, but `checkIfValidCommit` is `containsOrBeforeTimelineStarts`, so an entry 
whose instant predates a re-created table's first commit is accepted, not 
rejected. Since a test's own `drop table ... purge` doesn't clear the per-table 
entry (only `dropSuiteTables`/`withTable` do), a test that recreates a table at 
the same path and writes with INMEMORY would get misrouted upserts. I checked 
every INMEMORY suite and each leg gets a fresh temp dir + name today, so this 
is latent — but could the comment here (or the index javadoc) say the guard 
only covers instants after timeline start, so nobody relies on it for path 
reuse?
   
   <sub><i>⚠️ AI-generated; verify before applying. React 👍/👎 to flag 
quality.</i></sub>



##########
hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/common/ExclusiveSuite.scala:
##########
@@ -0,0 +1,40 @@
+/*
+ * 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.spark.sql.hudi.common
+
+import org.scalatest.{Args, Status}
+
+/**
+ * Marks a suite that must not run at the same time as any other suite in the 
JVM because it
+ * mutates JVM-wide state (the shared Hadoop conf, persisted RDDs of the 
shared context, a
+ * static metrics registry). Takes the write side of 
[[HoodieSparkSqlTestBase.suiteLock]] for
+ * its whole run; every other suite holds the read side while a test runs. No 
effect until
+ * suites run concurrently.
+ */
+trait ExclusiveSuite extends HoodieSparkSqlTestBase {
+
+  abstract override def run(testName: Option[String], args: Args): Status = {

Review Comment:
   🤖 The read side is taken per test, so a non-exclusive suite's 
`beforeAll`/`afterAll` (lazy `spark` init via 
`sharedBaseSession().newSession()`, `applySuiteConfToSharedSession`, the 
Hadoop-conf restore) run with no lock and can overlap an exclusive suite's run. 
Today only the exclusive `TestClusteringBinaryCopyStrategy` sets 
`spark.hadoop.*`, so I don't see a concrete break — is leaving 
`beforeAll`/`afterAll` uncovered intentional, or would it be worth wrapping the 
whole non-exclusive `run` in the read lock instead?
   
   <sub><i>⚠️ AI-generated; verify before applying. React 👍/👎 to flag 
quality.</i></sub>



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

Reply via email to