This is an automated email from the ASF dual-hosted git repository.
Apache9 pushed a commit to branch branch-3
in repository https://gitbox.apache.org/repos/asf/hbase.git
The following commit(s) were added to refs/heads/branch-3 by this push:
new b0a5a63dcec HBASE-30340: CatalogJanitor can run concurrent scans due
to incorrect alreadyRunning lock handling (#8569)
b0a5a63dcec is described below
commit b0a5a63dcec6ee128a77752993decc677934953f
Author: Sercan Tekin <[email protected]>
AuthorDate: Mon Aug 31 05:53:48 2026 -0400
HBASE-30340: CatalogJanitor can run concurrent scans due to incorrect
alreadyRunning lock handling (#8569)
Signed-off by: Peng Lu <[email protected]>
Signed-off by: Duo Zhang <[email protected]>
(cherry picked from commit 1fc1aeb11d981d8e70cfc926167ee53d66499455)
---
.../hbase/master/janitor/CatalogJanitor.java | 14 +++----
.../hbase/master/janitor/TestCatalogJanitor.java | 48 ++++++++++++++++++++++
2 files changed, 55 insertions(+), 7 deletions(-)
diff --git
a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/janitor/CatalogJanitor.java
b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/janitor/CatalogJanitor.java
index 14cf61ef970..a9fb510ca39 100644
---
a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/janitor/CatalogJanitor.java
+++
b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/janitor/CatalogJanitor.java
@@ -164,14 +164,14 @@ public class CatalogJanitor extends ScheduledChore {
*/
public int scan() throws IOException {
int gcs = 0;
- try {
- if (!alreadyRunning.compareAndSet(false, true)) {
- if (LOG.isDebugEnabled()) {
- LOG.debug("CatalogJanitor already running");
- }
- // -1 indicates previous scan is in progress
- return -1;
+ if (!alreadyRunning.compareAndSet(false, true)) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("CatalogJanitor already running");
}
+ // -1 indicates previous scan is in progress
+ return -1;
+ }
+ try {
this.lastReport = scanForReport();
if (!this.lastReport.isEmpty()) {
LOG.warn(this.lastReport.toString());
diff --git
a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/janitor/TestCatalogJanitor.java
b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/janitor/TestCatalogJanitor.java
index 259ff1636bf..647451057a3 100644
---
a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/janitor/TestCatalogJanitor.java
+++
b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/janitor/TestCatalogJanitor.java
@@ -35,6 +35,8 @@ import java.util.Map;
import java.util.Objects;
import java.util.SortedMap;
import java.util.TreeMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileStatus;
@@ -690,6 +692,52 @@ public class TestCatalogJanitor {
assertTrue(gcValues.contains(-1), "One janitor.scan() call should have
returned -1");
}
+ @Test
+ public void testAlreadyRunningStatusDoesNotClearLock() throws Exception {
+ CatalogJanitor spy = spy(this.janitor);
+
+ CountDownLatch scanStarted = new CountDownLatch(1);
+ CountDownLatch allowScanToFinish = new CountDownLatch(1);
+
+ doAnswer(invocation -> {
+ scanStarted.countDown();
+ assertTrue(allowScanToFinish.await(15, TimeUnit.SECONDS),
+ "Timed out waiting for the test to release the first catalog janitor
scan.");
+ return new CatalogJanitorReport();
+ }).when(spy).scanForReport();
+
+ Thread scanThread = new Thread(() -> {
+ try {
+ spy.scan();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ });
+
+ scanThread.start();
+ try {
+ // First scan acquires the lock and remains running.
+ assertTrue(scanStarted.await(5, TimeUnit.SECONDS));
+ LOG.info("First catalog janitor scan started and waiting to finish.");
+
+ // Second scan detects that another scan is running.
+ assertEquals(-1, spy.scan());
+ LOG.info("Second catalog janitor scan attempt returned -1.");
+
+ // The second scan must not clear the lock.
+ // Therefore, the third scan must also report that a scan is running.
+ int result = spy.scan();
+ LOG.info("Third catalog janitor scan attempt returned {}.", result);
+ assertEquals(-1, result);
+ } finally {
+ // Let the first scan finish.
+ LOG.info("Releasing first catalog janitor scan and waiting for it to
complete.");
+ allowScanToFinish.countDown();
+ scanThread.join(5000);
+ LOG.info("First catalog janitor scan thread alive after join: {}",
scanThread.isAlive());
+ }
+ }
+
private FileStatus[] addMockStoreFiles(int count, MasterServices services,
Path storedir)
throws IOException {
// get the existing store files