This is an automated email from the ASF dual-hosted git repository.

kturner pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/accumulo.git


The following commit(s) were added to refs/heads/main by this push:
     new 1ca2509423 Clears unreferenced tables from TabletLocator (#6204)
1ca2509423 is described below

commit 1ca25094235a1ec22c4b51e23ebb796538257b16
Author: Keith Turner <[email protected]>
AuthorDate: Tue Mar 10 12:23:16 2026 -0400

    Clears unreferenced tables from TabletLocator (#6204)
    
    follow on to #6196
---
 .../accumulo/core/clientImpl/ClientContext.java    | 41 +++++++++++++++++
 .../java/org/apache/accumulo/test/LocatorIT.java   | 51 ++++++++++++++++++++++
 2 files changed, 92 insertions(+)

diff --git 
a/core/src/main/java/org/apache/accumulo/core/clientImpl/ClientContext.java 
b/core/src/main/java/org/apache/accumulo/core/clientImpl/ClientContext.java
index 41674ebca7..085777e54d 100644
--- a/core/src/main/java/org/apache/accumulo/core/clientImpl/ClientContext.java
+++ b/core/src/main/java/org/apache/accumulo/core/clientImpl/ClientContext.java
@@ -32,6 +32,7 @@ import static 
org.apache.accumulo.core.util.threads.ThreadPoolNames.SCANNER_READ
 import java.lang.Thread.UncaughtExceptionHandler;
 import java.net.URL;
 import java.nio.file.Path;
+import java.time.Duration;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.EnumMap;
@@ -112,6 +113,7 @@ import 
org.apache.accumulo.core.spi.common.ServiceEnvironment;
 import org.apache.accumulo.core.spi.scan.ScanServerInfo;
 import org.apache.accumulo.core.spi.scan.ScanServerSelector;
 import org.apache.accumulo.core.util.Pair;
+import org.apache.accumulo.core.util.Timer;
 import org.apache.accumulo.core.util.cache.Caches;
 import org.apache.accumulo.core.util.tables.TableMapping;
 import org.apache.accumulo.core.util.tables.TableNameUtil;
@@ -125,6 +127,8 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.github.benmanes.caffeine.cache.Cache;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
 import com.google.common.base.Suppliers;
 
 import io.micrometer.core.instrument.MeterRegistry;
@@ -1247,8 +1251,45 @@ public class ClientContext implements AccumuloClient {
     return mapping;
   }
 
+  @VisibleForTesting
+  public boolean isTabletLocationCachePresent(TableId tableId) {
+    return tabletLocationCache.get(DataLevel.of(tableId)).containsKey(tableId);
+  }
+
+  private volatile Duration clearFrequency = Duration.ofMinutes(10);
+
+  /**
+   * Sets how often checks for unused tables are done
+   */
+  @VisibleForTesting
+  public void setClearFrequency(Duration frequency) {
+    Preconditions.checkArgument(frequency != null && !frequency.isNegative() 
&& !frequency.isZero(),
+        "frequency:%s", frequency);
+    clearFrequency = frequency;
+  }
+
+  private final Timer lastClearTimer = Timer.startNew();
+
   public ClientTabletCache getTabletLocationCache(TableId tableId) {
     ensureOpen();
+    if (lastClearTimer.hasElapsed(clearFrequency)) {
+      synchronized (lastClearTimer) {
+        if (lastClearTimer.hasElapsed(clearFrequency)) {
+          tabletLocationCache.get(DataLevel.USER).entrySet().removeIf(entry -> 
{
+            TableId tableIdToCheck = entry.getKey();
+            ClientTabletCache cache = entry.getValue();
+            var tableState = getTableState(tableIdToCheck);
+            if (tableState != TableState.ONLINE && tableState != 
TableState.OFFLINE) {
+              cache.invalidateCache();
+              return true;
+            }
+            return false;
+          });
+          lastClearTimer.restart();
+        }
+      }
+    }
+
     return 
tabletLocationCache.get(DataLevel.of(tableId)).computeIfAbsent(tableId,
         (TableId key) -> {
           var lockChecker = getTServerLockChecker();
diff --git a/test/src/main/java/org/apache/accumulo/test/LocatorIT.java 
b/test/src/main/java/org/apache/accumulo/test/LocatorIT.java
index eea4ee5733..75a421316e 100644
--- a/test/src/main/java/org/apache/accumulo/test/LocatorIT.java
+++ b/test/src/main/java/org/apache/accumulo/test/LocatorIT.java
@@ -19,6 +19,7 @@
 package org.apache.accumulo.test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -40,9 +41,11 @@ import org.apache.accumulo.core.client.AccumuloException;
 import org.apache.accumulo.core.client.TableNotFoundException;
 import org.apache.accumulo.core.client.TableOfflineException;
 import org.apache.accumulo.core.client.admin.Locations;
+import org.apache.accumulo.core.client.admin.NewTableConfiguration;
 import org.apache.accumulo.core.client.admin.TableOperations;
 import org.apache.accumulo.core.client.admin.TabletAvailability;
 import org.apache.accumulo.core.client.admin.servers.ServerId;
+import org.apache.accumulo.core.clientImpl.ClientContext;
 import org.apache.accumulo.core.data.Range;
 import org.apache.accumulo.core.data.RowRange;
 import org.apache.accumulo.core.data.TableId;
@@ -175,4 +178,52 @@ public class LocatorIT extends AccumuloClusterHarness {
       assertThrows(TableNotFoundException.class, () -> 
tableOps.locate(tableName, ranges));
     }
   }
+
+  @Test
+  public void testClearingUnused() throws Exception {
+    try (AccumuloClient client = 
Accumulo.newClient().from(getClientProps()).build()) {
+      String[] tables = getUniqueNames(4);
+      String table1 = tables[0];
+      String table2 = tables[1];
+      String table3 = tables[2];
+      String table4 = tables[3];
+
+      TableOperations tableOps = client.tableOperations();
+      tableOps.create(table1);
+      tableOps.create(table2);
+      tableOps.create(table3, new NewTableConfiguration().createOffline());
+      tableOps.create(table4, new NewTableConfiguration().createOffline());
+
+      ClientContext ctx = (ClientContext) client;
+
+      ctx.setClearFrequency(Duration.ofMillis(100));
+
+      TableId tableId1 = ctx.getTableId(table1);
+      TableId tableId2 = ctx.getTableId(table2);
+      TableId tableId3 = ctx.getTableId(table3);
+      TableId tableId4 = ctx.getTableId(table4);
+
+      for (var tableId : List.of(tableId1, tableId2, tableId3, tableId4)) {
+        assertFalse(ctx.isTabletLocationCachePresent(tableId));
+        assertNotNull(ctx.getTabletLocationCache(tableId));
+        assertTrue(ctx.isTabletLocationCachePresent(tableId));
+      }
+
+      tableOps.delete(table1);
+      tableOps.delete(table4);
+
+      Wait.waitFor(() -> {
+        // Accessing table3 in the cache should cause table1 and table4 to 
eventually be cleared
+        // because they no longer exist. This also test that online and 
offline tables a properly
+        // cleared from the cache.
+        assertNotNull(ctx.getTabletLocationCache(tableId3));
+        return !ctx.isTabletLocationCachePresent(tableId1)
+            && !ctx.isTabletLocationCachePresent(tableId4);
+      });
+
+      // table2 and table3 should be left in the cache
+      assertTrue(ctx.isTabletLocationCachePresent(tableId2));
+      assertTrue(ctx.isTabletLocationCachePresent(tableId3));
+    }
+  }
 }

Reply via email to