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));
+ }
+ }
}