This is an automated email from the ASF dual-hosted git repository.
ivandika3 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ozone.git
The following commit(s) were added to refs/heads/master by this push:
new 8bdfef0b8fb HDDS-13365. OM support automatic table cache tracking and
cleanup (#10171)
8bdfef0b8fb is described below
commit 8bdfef0b8fb21a5d6db4528fede53abb13b150f1
Author: Ivan Andika <[email protected]>
AuthorDate: Tue Sep 29 15:15:17 2026 +0800
HDDS-13365. OM support automatic table cache tracking and cleanup (#10171)
Co-authored-by: Tsz-Wo Nicholas Sze <[email protected]>
---
.../apache/hadoop/hdds/utils/db/TypedTable.java | 2 +
.../utils/db/cache/TableCacheUpdateTracker.java | 88 +++++++++++++++
.../hdds/utils/db/TestTableCacheUpdateTracker.java | 118 +++++++++++++++++++++
.../ozone/om/ratis/OzoneManagerDoubleBuffer.java | 30 +++---
.../hadoop/ozone/om/request/OMClientRequest.java | 7 +-
.../hadoop/ozone/om/response/OMClientResponse.java | 24 ++++-
.../om/response/key/OMOpenKeysDeleteResponse.java | 8 --
.../protocolPB/OzoneManagerRequestHandler.java | 4 +-
...tOzoneManagerDoubleBufferWithDummyResponse.java | 93 ++++++++++++++--
.../request/key/TestOMOpenKeysDeleteRequest.java | 7 ++
.../ozone/om/response/TestCleanupTableInfo.java | 3 +
.../ozone/om/response/TestOMClientResponse.java | 51 +++++++++
12 files changed, 398 insertions(+), 37 deletions(-)
diff --git
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/utils/db/TypedTable.java
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/utils/db/TypedTable.java
index b9c0c56feba..cc5d661cd03 100644
---
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/utils/db/TypedTable.java
+++
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/utils/db/TypedTable.java
@@ -40,6 +40,7 @@
import org.apache.hadoop.hdds.utils.db.cache.PartialTableCache;
import org.apache.hadoop.hdds.utils.db.cache.TableCache;
import org.apache.hadoop.hdds.utils.db.cache.TableCache.CacheType;
+import org.apache.hadoop.hdds.utils.db.cache.TableCacheUpdateTracker;
import org.apache.hadoop.hdds.utils.db.cache.TableNoCache;
import org.apache.ratis.util.Preconditions;
import org.apache.ratis.util.function.CheckedBiFunction;
@@ -587,6 +588,7 @@ public long getEstimatedKeyCount() throws
RocksDatabaseException {
public void addCacheEntry(CacheKey<KEY> cacheKey,
CacheValue<VALUE> cacheValue) {
// This will override the entry if there is already entry for this key.
+ TableCacheUpdateTracker.recordCacheUpdate(getName());
cache.put(cacheKey, cacheValue);
}
diff --git
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/utils/db/cache/TableCacheUpdateTracker.java
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/utils/db/cache/TableCacheUpdateTracker.java
new file mode 100644
index 00000000000..0bb5413d3e4
--- /dev/null
+++
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/utils/db/cache/TableCacheUpdateTracker.java
@@ -0,0 +1,88 @@
+/*
+ * 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.hadoop.hdds.utils.db.cache;
+
+import java.util.LinkedHashSet;
+import java.util.Objects;
+import java.util.Set;
+import org.apache.ratis.util.Preconditions;
+import org.apache.ratis.util.UncheckedAutoCloseable;
+
+/**
+ * Tracks table caches updated by the current thread.
+ * Only one tracker is allowed to be active per thread.
+ * <p>
+ * Only one thread, which is the thread invoked the constructor, can access
the non-static methods.
+ * The call sequences must be:
+ * {@link #TableCacheUpdateTracker()},
+ * {@link #record(String)} (any number of times including zero),
+ * {@link #removeUpdatedTables()} (exactly one time),
+ * {@link #close()} (exactly one time).
+ */
+public final class TableCacheUpdateTracker implements UncheckedAutoCloseable {
+ private static final ThreadLocal<TableCacheUpdateTracker> CURRENT = new
ThreadLocal<>();
+
+ private final Thread thread = Thread.currentThread();
+ private Set<String> tables = null;
+
+ public static TableCacheUpdateTracker track() {
+ TableCacheUpdateTracker tracker = new TableCacheUpdateTracker();
+ CURRENT.set(tracker);
+ return tracker;
+ }
+
+ public static void recordCacheUpdate(String tableName) {
+ TableCacheUpdateTracker tracker = CURRENT.get();
+ if (tracker != null) {
+ tracker.record(tableName);
+ }
+ }
+
+ private TableCacheUpdateTracker() {
+ }
+
+ private void assertCurrent() {
+ Preconditions.assertSame(this, CURRENT.get(), "tracker");
+ Preconditions.assertSame(thread, Thread.currentThread(), "thread");
+ }
+
+ public Set<String> removeUpdatedTables() {
+ assertCurrent();
+ final Set<String> t = tables;
+ tables = null;
+ return t; // can be null
+ }
+
+ @Override
+ public void close() {
+ assertCurrent(); // not idempotent
+ Preconditions.assertNull(tables, "tables");
+ CURRENT.remove();
+ }
+
+ private void record(String tableName) {
+ assertCurrent();
+ Objects.requireNonNull(tableName, "tableName");
+ Preconditions.assertTrue(!tableName.isEmpty(), "tableName is empty");
+
+ if (tables == null) {
+ tables = new LinkedHashSet<>();
+ }
+ tables.add(tableName);
+ }
+}
diff --git
a/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/utils/db/TestTableCacheUpdateTracker.java
b/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/utils/db/TestTableCacheUpdateTracker.java
new file mode 100644
index 00000000000..adf4416b9ad
--- /dev/null
+++
b/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/utils/db/TestTableCacheUpdateTracker.java
@@ -0,0 +1,118 @@
+/*
+ * 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.hadoop.hdds.utils.db;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
+import static org.assertj.core.api.Assertions.assertThatNullPointerException;
+
+import java.util.concurrent.atomic.AtomicReference;
+import org.apache.hadoop.hdds.utils.db.cache.TableCacheUpdateTracker;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests {@link TableCacheUpdateTracker}.
+ */
+public class TestTableCacheUpdateTracker {
+
+ @Test
+ public void trackReturnsScopedTrackerWithUpdatedTables() {
+ try (TableCacheUpdateTracker tracker = TableCacheUpdateTracker.track()) {
+ TableCacheUpdateTracker.recordCacheUpdate("table1");
+ TableCacheUpdateTracker.recordCacheUpdate("table2");
+
+ assertThat(tracker.removeUpdatedTables())
+ .containsExactly("table1", "table2");
+ }
+ }
+
+ @Test
+ public void closeRequiresUpdatedTablesToBeRemoved() {
+ TableCacheUpdateTracker tracker = TableCacheUpdateTracker.track();
+ TableCacheUpdateTracker.recordCacheUpdate("table1");
+
+ assertThatIllegalStateException().isThrownBy(tracker::close);
+
+ assertThat(tracker.removeUpdatedTables()).containsExactly("table1");
+ tracker.close();
+ }
+
+ @Test
+ public void removeUpdatedTablesReturnsNullWithoutUpdates() {
+ try (TableCacheUpdateTracker tracker = TableCacheUpdateTracker.track()) {
+ assertThat(tracker.removeUpdatedTables()).isNull();
+ }
+ }
+
+ @Test
+ public void recordCacheUpdateDeduplicatesTablesInInsertionOrder() {
+ try (TableCacheUpdateTracker tracker = TableCacheUpdateTracker.track()) {
+ TableCacheUpdateTracker.recordCacheUpdate("table1");
+ TableCacheUpdateTracker.recordCacheUpdate("table2");
+ TableCacheUpdateTracker.recordCacheUpdate("table1");
+
+ assertThat(tracker.removeUpdatedTables())
+ .containsExactly("table1", "table2");
+ }
+ }
+
+ @Test
+ public void recordCacheUpdateRejectsInvalidTableNames() {
+ try (TableCacheUpdateTracker tracker = TableCacheUpdateTracker.track()) {
+ assertThatNullPointerException()
+ .isThrownBy(() -> TableCacheUpdateTracker.recordCacheUpdate(null));
+ assertThatIllegalStateException()
+ .isThrownBy(() -> TableCacheUpdateTracker.recordCacheUpdate(""));
+
+ assertThat(tracker.removeUpdatedTables()).isNull();
+ }
+ }
+
+ @Test
+ public void updatesDoNotLeakIntoSubsequentTracker() {
+ try (TableCacheUpdateTracker tracker = TableCacheUpdateTracker.track()) {
+ TableCacheUpdateTracker.recordCacheUpdate("table1");
+ assertThat(tracker.removeUpdatedTables()).containsExactly("table1");
+ }
+
+ try (TableCacheUpdateTracker tracker = TableCacheUpdateTracker.track()) {
+ TableCacheUpdateTracker.recordCacheUpdate("table2");
+ assertThat(tracker.removeUpdatedTables()).containsExactly("table2");
+ }
+ }
+
+ @Test
+ public void nonOwnerThreadCannotAccessTracker() throws InterruptedException {
+ TableCacheUpdateTracker tracker = TableCacheUpdateTracker.track();
+ AtomicReference<Throwable> failure = new AtomicReference<>();
+ Thread thread = new Thread(() -> {
+ try {
+ tracker.removeUpdatedTables();
+ } catch (Throwable t) {
+ failure.set(t);
+ }
+ });
+
+ thread.start();
+ thread.join();
+
+ assertThat(failure.get()).isInstanceOf(IllegalStateException.class);
+ assertThat(tracker.removeUpdatedTables()).isNull();
+ tracker.close();
+ }
+}
diff --git
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/OzoneManagerDoubleBuffer.java
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/OzoneManagerDoubleBuffer.java
index c822e0b9fe2..5de26d73ad4 100644
---
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/OzoneManagerDoubleBuffer.java
+++
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/OzoneManagerDoubleBuffer.java
@@ -528,26 +528,20 @@ private static boolean
isStandaloneBatchCmdTypes(OMResponse response) {
}
private void addCleanupEntry(Entry entry, Map<String, List<Long>>
cleanupEpochs) {
- Class<? extends OMClientResponse> responseClass =
- entry.getResponse().getClass();
+ OMClientResponse response = entry.getResponse();
CleanupTableInfo cleanupTableInfo =
- responseClass.getAnnotation(CleanupTableInfo.class);
- if (cleanupTableInfo != null) {
- final List<String> cleanupTables;
- if (cleanupTableInfo.cleanupAll()) {
- cleanupTables = OMDBDefinition.get().getColumnFamilyNames();
- } else {
- cleanupTables = Arrays.asList(cleanupTableInfo.cleanupTables());
- }
- for (String table : cleanupTables) {
- cleanupEpochs.computeIfAbsent(table, list -> new ArrayList<>())
- .add(entry.getTermIndex().getIndex());
- }
+ response.getClass().getAnnotation(CleanupTableInfo.class);
+ final Iterable<String> cleanupTables;
+ if (cleanupTableInfo == null) {
+ cleanupTables = response.removeCleanupTables();
+ } else if (cleanupTableInfo.cleanupAll()) {
+ cleanupTables = OMDBDefinition.get().getColumnFamilyNames();
} else {
- // This is to catch early errors, when a new response class missed to
- // add CleanupTableInfo annotation.
- throw new RuntimeException("CleanupTableInfo Annotation is missing " +
- "for" + responseClass);
+ cleanupTables = Arrays.asList(cleanupTableInfo.cleanupTables());
+ }
+ for (String table : cleanupTables) {
+ cleanupEpochs.computeIfAbsent(table, list -> new ArrayList<>())
+ .add(entry.getTermIndex().getIndex());
}
}
diff --git
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/OMClientRequest.java
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/OMClientRequest.java
index b6cca710d78..62013f7c071 100644
---
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/OMClientRequest.java
+++
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/OMClientRequest.java
@@ -29,6 +29,7 @@
import java.util.Objects;
import org.apache.commons.lang3.StringUtils;
import org.apache.hadoop.hdds.utils.TransactionInfo;
+import org.apache.hadoop.hdds.utils.db.cache.TableCacheUpdateTracker;
import org.apache.hadoop.ipc_.ProtobufRpcEngine;
import org.apache.hadoop.ozone.OmUtils;
import org.apache.hadoop.ozone.OzoneConsts;
@@ -165,7 +166,11 @@ public void handleRequestFailure(OzoneManager
ozoneManager) {
public OMClientResponse validateAndUpdateCache(OzoneManager ozoneManager,
long transactionLogIndex) {
ExecutionContext context = ExecutionContext.of(transactionLogIndex,
TransactionInfo.getTermIndex(transactionLogIndex));
- return validateAndUpdateCache(ozoneManager, context);
+ try (TableCacheUpdateTracker tracker = TableCacheUpdateTracker.track()) {
+ OMClientResponse response = validateAndUpdateCache(ozoneManager,
context);
+ response.addCleanupTables(tracker.removeUpdatedTables());
+ return response;
+ }
}
@VisibleForTesting
diff --git
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/OMClientResponse.java
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/OMClientResponse.java
index ffe3f70099c..8ca2189bc9b 100644
---
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/OMClientResponse.java
+++
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/OMClientResponse.java
@@ -19,7 +19,10 @@
import com.google.common.base.Preconditions;
import java.io.IOException;
+import java.util.Collections;
import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
import org.apache.hadoop.hdds.utils.db.BatchOperation;
import org.apache.hadoop.ozone.om.OMMetadataManager;
import org.apache.hadoop.ozone.om.helpers.BucketLayout;
@@ -34,6 +37,7 @@ public abstract class OMClientResponse {
private final OMResponse omResponse;
private OMLockDetails omLockDetails;
+ private final AtomicReference<Set<String>> cleanupTables = new
AtomicReference<>();
public OMClientResponse(OMResponse omResponse) {
Objects.requireNonNull(omResponse, "omResponse == null");
@@ -86,5 +90,23 @@ public void setOmLockDetails(
OMLockDetails omLockDetails) {
this.omLockDetails = omLockDetails;
}
-}
+ public Set<String> removeCleanupTables() {
+ final Set<String> tables = cleanupTables.getAndSet(null);
+ return tables == null ? Collections.emptySet() :
Collections.unmodifiableSet(tables);
+ }
+
+ public void addCleanupTables(Set<String> tables) {
+ if (tables == null || tables.isEmpty()) {
+ return;
+ }
+ cleanupTables.updateAndGet(t -> {
+ if (t == null || t.isEmpty()) {
+ return tables;
+ } else {
+ t.addAll(tables);
+ return t;
+ }
+ });
+ }
+}
diff --git
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/key/OMOpenKeysDeleteResponse.java
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/key/OMOpenKeysDeleteResponse.java
index e46bd7db64f..e507159894e 100644
---
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/key/OMOpenKeysDeleteResponse.java
+++
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/key/OMOpenKeysDeleteResponse.java
@@ -17,11 +17,6 @@
package org.apache.hadoop.ozone.om.response.key;
-import static org.apache.hadoop.ozone.om.codec.OMDBDefinition.BUCKET_TABLE;
-import static org.apache.hadoop.ozone.om.codec.OMDBDefinition.DELETED_TABLE;
-import static org.apache.hadoop.ozone.om.codec.OMDBDefinition.OPEN_FILE_TABLE;
-import static org.apache.hadoop.ozone.om.codec.OMDBDefinition.OPEN_KEY_TABLE;
-
import jakarta.annotation.Nonnull;
import java.io.IOException;
import java.util.Map;
@@ -31,15 +26,12 @@
import org.apache.hadoop.ozone.om.OMMetadataManager;
import org.apache.hadoop.ozone.om.helpers.BucketLayout;
import org.apache.hadoop.ozone.om.helpers.OmKeyInfo;
-import org.apache.hadoop.ozone.om.response.CleanupTableInfo;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMResponse;
/**
* Handles responses to move open keys from the open key table to the delete
* table. Modifies the open key table and delete table databases.
*/
-@CleanupTableInfo(cleanupTables = {OPEN_KEY_TABLE, OPEN_FILE_TABLE,
- DELETED_TABLE, BUCKET_TABLE})
public class OMOpenKeysDeleteResponse extends AbstractOMKeyDeleteResponse {
private Map<String, Pair<Long, OmKeyInfo>> keysToDelete;
diff --git
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java
index 327dd9d53dc..2fc6000abe6 100644
---
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java
+++
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java
@@ -60,6 +60,7 @@
import
org.apache.hadoop.hdds.protocol.proto.HddsProtos.UpgradeFinalizationStatus;
import org.apache.hadoop.hdds.scm.protocolPB.OzonePBHelper;
import org.apache.hadoop.hdds.utils.FaultInjector;
+import org.apache.hadoop.hdds.utils.db.cache.TableCacheUpdateTracker;
import org.apache.hadoop.ozone.OzoneAcl;
import org.apache.hadoop.ozone.om.OzoneAclUtils;
import org.apache.hadoop.ozone.om.OzoneManager;
@@ -459,11 +460,12 @@ public OMClientResponse handleWriteRequestImpl(OMRequest
omRequest, ExecutionCon
injectPause();
OMClientRequest omClientRequest =
OzoneManagerRatisUtils.createClientRequest(omRequest, impl);
- try {
+ try (TableCacheUpdateTracker tracker = TableCacheUpdateTracker.track()) {
OMClientResponse omClientResponse = captureLatencyNs(
impl.getPerfMetrics().getValidateAndUpdateCacheLatencyNs(),
() ->
Objects.requireNonNull(omClientRequest.validateAndUpdateCache(getOzoneManager(),
context),
"omClientResponse returned by validateAndUpdateCache cannot be
null"));
+ omClientResponse.addCleanupTables(tracker.removeUpdatedTables());
OMAuditLogger.log(omClientRequest.getAuditBuilder(),
context.getTermIndex());
return omClientResponse;
} catch (Throwable th) {
diff --git
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/ratis/TestOzoneManagerDoubleBufferWithDummyResponse.java
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/ratis/TestOzoneManagerDoubleBufferWithDummyResponse.java
index 21af533b545..34eb8a7a944 100644
---
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/ratis/TestOzoneManagerDoubleBufferWithDummyResponse.java
+++
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/ratis/TestOzoneManagerDoubleBufferWithDummyResponse.java
@@ -20,18 +20,23 @@
import static org.apache.hadoop.hdds.HddsConfigKeys.OZONE_METADATA_DIRS;
import static org.apache.hadoop.ozone.OzoneConsts.TRANSACTION_INFO_KEY;
import static org.apache.hadoop.ozone.om.codec.OMDBDefinition.BUCKET_TABLE;
+import static org.apache.hadoop.ozone.om.codec.OMDBDefinition.VOLUME_TABLE;
import static org.apache.ozone.test.GenericTestUtils.waitFor;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
+import com.google.common.collect.Iterators;
import java.io.IOException;
import java.nio.file.Path;
+import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.hdds.utils.TransactionInfo;
import org.apache.hadoop.hdds.utils.db.BatchOperation;
+import org.apache.hadoop.hdds.utils.db.cache.CacheKey;
+import org.apache.hadoop.hdds.utils.db.cache.CacheValue;
import org.apache.hadoop.ozone.om.OMMetadataManager;
import org.apache.hadoop.ozone.om.OmMetadataManagerImpl;
import org.apache.hadoop.ozone.om.helpers.OmBucketInfo;
@@ -96,22 +101,32 @@ public void testDoubleBufferWithDummyResponse() throws
Exception {
assertEquals(0, metrics.getTotalNumOfFlushedTransactions());
assertEquals(0, metrics.getMaxNumberOfTransactionsFlushedInOneIteration());
+ OMDummyCreateBucketResponse annotatedResponse =
+ createDummyBucketResponse(UUID.randomUUID().toString());
+ annotatedResponse.addCleanupTables(Collections.singleton(VOLUME_TABLE));
+ assertAnnotatedCleanupTakesPrecedence(annotatedResponse);
+
+ OMTrackedDummyCreateBucketResponse trackedResponse =
+ createTrackedDummyBucketResponse(UUID.randomUUID().toString());
+ assertBucketCacheCleanup(trackedResponse);
+
for (int i = 0; i < bucketCount; i++) {
doubleBuffer.add(createDummyBucketResponse(volumeName),
TermIndex.valueOf(term, trxId.incrementAndGet()));
}
- waitFor(() -> metrics.getTotalNumOfFlushedTransactions() == bucketCount,
+ int totalBucketCount = bucketCount + 2;
+ waitFor(() -> metrics.getTotalNumOfFlushedTransactions() ==
totalBucketCount,
100, 60000);
assertThat(metrics.getTotalNumOfFlushOperations()).isGreaterThan(0);
- assertEquals(bucketCount,
doubleBuffer.getFlushedTransactionCountForTesting());
+ assertEquals(totalBucketCount,
doubleBuffer.getFlushedTransactionCountForTesting());
assertThat(metrics.getMaxNumberOfTransactionsFlushedInOneIteration()).isGreaterThan(0);
- assertEquals(bucketCount, omMetadataManager.countRowsInTable(
+ assertEquals(totalBucketCount, omMetadataManager.countRowsInTable(
omMetadataManager.getBucketTable()));
assertThat(doubleBuffer.getFlushIterationsForTesting()).isGreaterThan(0);
assertThat(metrics.getFlushTime().lastStat().numSamples()).isGreaterThan(0);
assertThat(metrics.getAvgFlushTransactionsInOneIteration()).isGreaterThan(0);
- assertEquals(bucketCount, (long)
metrics.getQueueSize().lastStat().total());
+ assertEquals(totalBucketCount, (long)
metrics.getQueueSize().lastStat().total());
assertThat(metrics.getQueueSize().lastStat().numSamples()).isGreaterThan(0);
// Assert there is only instance of OM Double Metrics.
@@ -123,10 +138,36 @@ public void testDoubleBufferWithDummyResponse() throws
Exception {
omMetadataManager.getTransactionInfoTable().get(TRANSACTION_INFO_KEY);
// Check lastAppliedIndex is updated correctly or not.
assertNotNull(transactionInfo);
- assertEquals(bucketCount, transactionInfo.getTransactionIndex());
+ assertEquals(totalBucketCount, transactionInfo.getTransactionIndex());
assertEquals(term, transactionInfo.getTerm());
}
+ private void assertBucketCacheCleanup(
+ AbstractDummyCreateBucketResponse response) throws Exception {
+ long transactionIndex = trxId.incrementAndGet();
+ OmBucketInfo bucketInfo = response.getOmBucketInfo();
+ String dbBucketKey = omMetadataManager.getBucketKey(
+ bucketInfo.getVolumeName(), bucketInfo.getBucketName());
+ omMetadataManager.getBucketTable().addCacheEntry(
+ new CacheKey<>(dbBucketKey), CacheValue.get(transactionIndex));
+
+ doubleBuffer.add(response, TermIndex.valueOf(term, transactionIndex));
+ waitFor(() ->
Iterators.size(omMetadataManager.getBucketTable().cacheIterator()) == 0, 100,
5000);
+ }
+
+ private void assertAnnotatedCleanupTakesPrecedence(
+ AbstractDummyCreateBucketResponse response) throws Exception {
+ long transactionIndex = trxId.get() + 1;
+ String volumeName = response.getOmBucketInfo().getVolumeName();
+ omMetadataManager.getVolumeTable().addCacheEntry(
+ new CacheKey<>(volumeName), CacheValue.get(transactionIndex));
+
+ assertBucketCacheCleanup(response);
+
+ assertEquals(1,
Iterators.size(omMetadataManager.getVolumeTable().cacheIterator()));
+
omMetadataManager.getVolumeTable().cleanupCache(Collections.singletonList(transactionIndex));
+ }
+
/**
* Create DummyBucketCreate response.
*/
@@ -146,20 +187,38 @@ private OMDummyCreateBucketResponse
createDummyBucketResponse(
.build());
}
+ private OMTrackedDummyCreateBucketResponse createTrackedDummyBucketResponse(
+ String volumeName) {
+ OmBucketInfo omBucketInfo =
+ OmBucketInfo.newBuilder()
+ .setVolumeName(volumeName)
+ .setBucketName(UUID.randomUUID().toString())
+ .setCreationTime(Time.now())
+ .build();
+ return new OMTrackedDummyCreateBucketResponse(omBucketInfo,
+ OMResponse.newBuilder()
+ .setCmdType(OzoneManagerProtocolProtos.Type.CreateBucket)
+ .setStatus(OzoneManagerProtocolProtos.Status.OK)
+ .setCreateBucketResponse(CreateBucketResponse.newBuilder().build())
+ .build());
+ }
/**
* DummyCreatedBucket Response class used in testing.
*/
- @CleanupTableInfo(cleanupTables = {BUCKET_TABLE})
- private static class OMDummyCreateBucketResponse extends OMClientResponse {
+ private abstract static class AbstractDummyCreateBucketResponse extends
OMClientResponse {
private final OmBucketInfo omBucketInfo;
- OMDummyCreateBucketResponse(OmBucketInfo omBucketInfo,
+ AbstractDummyCreateBucketResponse(OmBucketInfo omBucketInfo,
OMResponse omResponse) {
super(omResponse);
this.omBucketInfo = omBucketInfo;
}
+ OmBucketInfo getOmBucketInfo() {
+ return omBucketInfo;
+ }
+
@Override
public void addToDBBatch(OMMetadataManager omMetadataManager,
BatchOperation batchOperation) throws IOException {
@@ -170,4 +229,22 @@ public void addToDBBatch(OMMetadataManager
omMetadataManager,
dbBucketKey, omBucketInfo);
}
}
+
+ @CleanupTableInfo(cleanupTables = {BUCKET_TABLE})
+ private static class OMDummyCreateBucketResponse
+ extends AbstractDummyCreateBucketResponse {
+ OMDummyCreateBucketResponse(OmBucketInfo omBucketInfo,
+ OMResponse omResponse) {
+ super(omBucketInfo, omResponse);
+ }
+ }
+
+ private static class OMTrackedDummyCreateBucketResponse
+ extends AbstractDummyCreateBucketResponse {
+ OMTrackedDummyCreateBucketResponse(OmBucketInfo omBucketInfo,
+ OMResponse omResponse) {
+ super(omBucketInfo, omResponse);
+ addCleanupTables(Collections.singleton(BUCKET_TABLE));
+ }
+ }
}
diff --git
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/key/TestOMOpenKeysDeleteRequest.java
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/key/TestOMOpenKeysDeleteRequest.java
index 03f9b774411..fef6583ed49 100644
---
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/key/TestOMOpenKeysDeleteRequest.java
+++
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/key/TestOMOpenKeysDeleteRequest.java
@@ -17,6 +17,8 @@
package org.apache.hadoop.ozone.om.request.key;
+import static org.apache.hadoop.ozone.om.codec.OMDBDefinition.OPEN_FILE_TABLE;
+import static org.apache.hadoop.ozone.om.codec.OMDBDefinition.OPEN_KEY_TABLE;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
@@ -35,6 +37,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@@ -256,6 +259,10 @@ public void testDeleteKeyWithHigherUpdateID(
openKeyDeleteRequest.validateAndUpdateCache(ozoneManager,
transactionId);
assertEquals(Status.OK, omClientResponse.getOMResponse().getStatus());
+ String expectedCleanupTable =
+ getBucketLayout().isFileSystemOptimized() ? OPEN_FILE_TABLE :
OPEN_KEY_TABLE;
+ assertEquals(Collections.singleton(expectedCleanupTable),
+ omClientResponse.removeCleanupTables());
assertInOpenKeyTable(keysWithHigherUpdateID);
assertNotInOpenKeyTable(keysWithSameUpdateID);
diff --git
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/response/TestCleanupTableInfo.java
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/response/TestCleanupTableInfo.java
index 5de300e2c2b..fb8b2eb6f77 100644
---
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/response/TestCleanupTableInfo.java
+++
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/response/TestCleanupTableInfo.java
@@ -62,6 +62,7 @@
import org.apache.hadoop.ozone.om.request.key.OMKeyCreateRequest;
import org.apache.hadoop.ozone.om.response.file.OMFileCreateResponse;
import org.apache.hadoop.ozone.om.response.key.OMKeyCreateResponse;
+import org.apache.hadoop.ozone.om.response.key.OMOpenKeysDeleteResponse;
import
org.apache.hadoop.ozone.om.response.lifecycle.OMLifecycleSetServiceStatusResponse;
import org.apache.hadoop.ozone.om.response.s3.security.S3AssumeRoleResponse;
import org.apache.hadoop.ozone.om.response.util.OMEchoRPCWriteResponse;
@@ -141,7 +142,9 @@ public void checkAnnotationAndTableName() {
subTypes.remove(OMEchoRPCWriteResponse.class);
subTypes.remove(DummyOMClientResponse.class);
subTypes.remove(OMLifecycleSetServiceStatusResponse.class);
+ subTypes.remove(OMOpenKeysDeleteResponse.class);
subTypes.remove(S3AssumeRoleResponse.class);
+
assertFalse(OMOpenKeysDeleteResponse.class.isAnnotationPresent(CleanupTableInfo.class));
subTypes.forEach(aClass -> {
if (Modifier.isAbstract(aClass.getModifiers())) {
assertFalse(aClass.isAnnotationPresent(CleanupTableInfo.class),
diff --git
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/response/TestOMClientResponse.java
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/response/TestOMClientResponse.java
new file mode 100644
index 00000000000..3c68407682c
--- /dev/null
+++
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/response/TestOMClientResponse.java
@@ -0,0 +1,51 @@
+/*
+ * 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.hadoop.ozone.om.response;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.Arrays;
+import java.util.LinkedHashSet;
+import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMResponse;
+import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Status;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Type;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests generic OM client response behavior.
+ */
+public class TestOMClientResponse {
+
+ @Test
+ public void testCleanupTablesMergeTrackedAndResponseTables() {
+ OMClientResponse response = new DummyOMClientResponse(
+ OMResponse.newBuilder()
+ .setStatus(Status.OK)
+ .setCmdType(Type.CreateSnapshot)
+ .build());
+
+ response.addCleanupTables(new LinkedHashSet<>(Arrays.asList(
+ "deletedTable", "snapshotRenamedTable")));
+ response.addCleanupTables(new LinkedHashSet<>(
+ Arrays.asList("snapshotInfoTable")));
+
+ assertEquals(new LinkedHashSet<>(Arrays.asList("deletedTable",
+ "snapshotRenamedTable", "snapshotInfoTable")),
+ response.removeCleanupTables());
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]