This is an automated email from the ASF dual-hosted git repository.
adoroszlai 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 b13f01c781 HDDS-9528. Managed objects should not override finalize()
(#5853)
b13f01c781 is described below
commit b13f01c781593b1c300bba3f0c30778ed946e926
Author: Duong Nguyen <[email protected]>
AuthorDate: Wed Jan 3 10:37:47 2024 -0800
HDDS-9528. Managed objects should not override finalize() (#5853)
---
.../org/apache/hadoop/hdds/utils/LeakDetector.java | 101 ++++++++++++++++++
.../org/apache/hadoop/hdds/utils/LeakTracker.java} | 37 ++++---
.../hadoop/hdds/resource/TestLeakDetector.java | 69 +++++++++++++
.../utils/db/TestRDBStoreByteArrayIterator.java | 25 -----
.../hdds/utils/db/managed/ManagedBloomFilter.java | 17 ++-
.../db/managed/ManagedColumnFamilyOptions.java | 19 ++--
.../db/managed/ManagedCompactRangeOptions.java | 17 ++-
.../hdds/utils/db/managed/ManagedDBOptions.java | 17 ++-
.../hdds/utils/db/managed/ManagedEnvOptions.java | 17 ++-
.../hdds/utils/db/managed/ManagedFlushOptions.java | 17 ++-
.../managed/ManagedIngestExternalFileOptions.java | 20 ++--
.../hdds/utils/db/managed/ManagedLRUCache.java | 17 ++-
.../hdds/utils/db/managed/ManagedObject.java | 22 +---
.../hdds/utils/db/managed/ManagedOptions.java | 17 ++-
.../hdds/utils/db/managed/ManagedReadOptions.java | 18 ++--
.../db/managed/ManagedRocksObjectMetrics.java | 11 ++
.../utils/db/managed/ManagedRocksObjectUtils.java | 29 +++---
.../hadoop/hdds/utils/db/managed/ManagedSlice.java | 26 ++---
.../utils/db/managed/ManagedSstFileReader.java | 10 +-
.../db/managed/ManagedSstFileReaderIterator.java | 9 +-
.../utils/db/managed/ManagedSstFileWriter.java | 17 ++-
.../hdds/utils/db/managed/ManagedStatistics.java | 17 ++-
.../hdds/utils/db/managed/ManagedWriteBatch.java | 18 ++--
.../hdds/utils/db/managed/ManagedWriteOptions.java | 17 ++-
.../hadoop/hdds/utils/db/managed/package-info.java | 10 +-
.../db/managed/TestRocksObjectLeakDetector.java | 114 +++++++++++++++++++++
.../apache/hadoop/ozone/MiniOzoneClusterImpl.java | 3 +-
27 files changed, 437 insertions(+), 274 deletions(-)
diff --git
a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/utils/LeakDetector.java
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/utils/LeakDetector.java
new file mode 100644
index 0000000000..67f5c2f2bb
--- /dev/null
+++
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/utils/LeakDetector.java
@@ -0,0 +1,101 @@
+/*
+ * 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;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.ref.ReferenceQueue;
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Simple general resource leak detector using {@link ReferenceQueue} and
{@link java.lang.ref.WeakReference} to
+ * observe resource object life-cycle and assert proper resource closure
before they are GCed.
+ *
+ * <p>
+ * Example usage:
+ *
+ * <pre> {@code
+ * class MyResource implements AutoClosable {
+ * static final LeakDetector LEAK_DETECTOR = new LeakDetector("MyResource");
+ *
+ * private final LeakTracker leakTracker = LEAK_DETECTOR.track(this, () -> {
+ * // report leaks, don't refer to the original object (MyResource) here.
+ * System.out.println("MyResource is not closed before being discarded.");
+ * });
+ *
+ * @Override
+ * public void close() {
+ * // proper resources cleanup...
+ * // inform tracker that this object is closed properly.
+ * leakTracker.close();
+ * }
+ * }
+ *
+ * }</pre>
+ */
+public class LeakDetector {
+ public static final Logger LOG = LoggerFactory.getLogger(LeakDetector.class);
+ private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
+ private final Set<LeakTracker> allLeaks = Collections.newSetFromMap(new
ConcurrentHashMap<>());
+ private final String name;
+
+ public LeakDetector(String name) {
+ this.name = name;
+ start();
+ }
+
+ private void start() {
+ Thread t = new Thread(this::run);
+ t.setName(LeakDetector.class.getSimpleName() + "-" + name);
+ t.setDaemon(true);
+ LOG.info("Starting leak detector thread {}.", name);
+ t.start();
+ }
+
+ private void run() {
+ while (true) {
+ try {
+ LeakTracker tracker = (LeakTracker) queue.remove();
+ // Original resource already been GCed, if tracker is not closed yet,
+ // report a leak.
+ if (allLeaks.remove(tracker)) {
+ tracker.reportLeak();
+ }
+ } catch (InterruptedException e) {
+ LOG.warn("Thread interrupted, exiting.", e);
+ break;
+ }
+ }
+
+ LOG.warn("Exiting leak detector {}.", name);
+ }
+
+ public LeakTracker track(Object leakable, Runnable reportLeak) {
+ // A rate filter can be put here to only track a subset of all objects,
e.g. 5%, 10%,
+ // if we have proofs that leak tracking impacts performance, or a single
LeakDetector
+ // thread can't keep up with the pace of object allocation.
+ // For now, it looks effective enough and let keep it simple.
+ LeakTracker tracker = new LeakTracker(leakable, queue, allLeaks,
reportLeak);
+ allLeaks.add(tracker);
+ return tracker;
+ }
+}
diff --git
a/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedSstFileReader.java
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/utils/LeakTracker.java
similarity index 53%
copy from
hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedSstFileReader.java
copy to
hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/utils/LeakTracker.java
index 7ba1001a43..6103d520ca 100644
---
a/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedSstFileReader.java
+++
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/utils/LeakTracker.java
@@ -16,28 +16,35 @@
* limitations under the License.
*
*/
-package org.apache.hadoop.hdds.utils.db.managed;
+package org.apache.hadoop.hdds.utils;
-import org.rocksdb.SstFileReader;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.util.Set;
/**
- * Managed SstFileReader.
+ * A token to track resource closure.
+ *
+ * @see LeakDetector
*/
-public class ManagedSstFileReader extends ManagedObject<SstFileReader> {
-
- ManagedSstFileReader(SstFileReader original) {
- super(original);
+public class LeakTracker extends WeakReference<Object> {
+ private final Set<LeakTracker> allLeaks;
+ private final Runnable leakReporter;
+ LeakTracker(Object referent, ReferenceQueue<Object> referenceQueue,
+ Set<LeakTracker> allLeaks, Runnable leakReporter) {
+ super(referent, referenceQueue);
+ this.allLeaks = allLeaks;
+ this.leakReporter = leakReporter;
}
- public static ManagedSstFileReader managed(
- SstFileReader reader) {
- return new ManagedSstFileReader(reader);
+ /**
+ * Called by the tracked resource when closing.
+ */
+ public void close() {
+ allLeaks.remove(this);
}
- @Override
- protected void finalize() throws Throwable {
- ManagedRocksObjectUtils.assertClosed(this);
- super.finalize();
+ void reportLeak() {
+ leakReporter.run();
}
-
}
diff --git
a/hadoop-hdds/common/src/test/java/org/apache/hadoop/hdds/resource/TestLeakDetector.java
b/hadoop-hdds/common/src/test/java/org/apache/hadoop/hdds/resource/TestLeakDetector.java
new file mode 100644
index 0000000000..4a60fcc8a4
--- /dev/null
+++
b/hadoop-hdds/common/src/test/java/org/apache/hadoop/hdds/resource/TestLeakDetector.java
@@ -0,0 +1,69 @@
+/*
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.resource;
+
+import org.apache.hadoop.hdds.utils.LeakDetector;
+import org.apache.hadoop.hdds.utils.LeakTracker;
+import org.junit.jupiter.api.Test;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * Test LeakDetector.
+ */
+public class TestLeakDetector {
+ private static final LeakDetector LEAK_DETECTOR = new LeakDetector("test");
+ private AtomicInteger leaks = new AtomicInteger(0);
+
+ @Test
+ public void testLeakDetector() throws Exception {
+ // create and close resource => no leaks.
+ createResource(true);
+ System.gc();
+ Thread.sleep(100);
+ assertEquals(0, leaks.get());
+
+ // create and not close => leaks.
+ createResource(false);
+ System.gc();
+ Thread.sleep(100);
+ assertEquals(1, leaks.get());
+ }
+
+ private void createResource(boolean close) throws Exception {
+ MyResource resource = new MyResource(leaks);
+ if (close) {
+ resource.close();
+ }
+ }
+
+ private static final class MyResource implements AutoCloseable {
+ private final LeakTracker leakTracker;
+
+ private MyResource(final AtomicInteger leaks) {
+ leakTracker = LEAK_DETECTOR.track(this, () -> leaks.incrementAndGet());
+ }
+
+ @Override
+ public void close() throws Exception {
+ leakTracker.close();
+ }
+ }
+}
diff --git
a/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/utils/db/TestRDBStoreByteArrayIterator.java
b/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/utils/db/TestRDBStoreByteArrayIterator.java
index d81771aceb..525ff27c54 100644
---
a/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/utils/db/TestRDBStoreByteArrayIterator.java
+++
b/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/utils/db/TestRDBStoreByteArrayIterator.java
@@ -33,7 +33,6 @@ import java.nio.charset.StandardCharsets;
import java.util.NoSuchElementException;
import java.util.function.Consumer;
-import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
@@ -307,28 +306,4 @@ public class TestRDBStoreByteArrayIterator {
iter.close();
}
-
- @Test
- public void testGetStackTrace() {
- ManagedRocksIterator iterator = mock(ManagedRocksIterator.class);
- RocksIterator mock = mock(RocksIterator.class);
- when(iterator.get()).thenReturn(mock);
- when(mock.isOwningHandle()).thenReturn(true);
- ManagedRocksObjectUtils.assertClosed(iterator);
- verify(iterator, times(1)).getStackTrace();
-
- iterator = new ManagedRocksIterator(rocksDBIteratorMock);
-
- // construct the expected trace.
- StackTraceElement[] traceElements = Thread.currentThread().getStackTrace();
- StringBuilder sb = new StringBuilder();
- // first 2 lines will differ.
- for (int i = 2; i < traceElements.length; i++) {
- sb.append(traceElements[i]);
- sb.append("\n");
- }
- String expectedTrace = sb.toString();
- String fromObjectInit = iterator.getStackTrace();
- assertThat(fromObjectInit).contains(expectedTrace);
- }
}
diff --git
a/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedBloomFilter.java
b/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedBloomFilter.java
index 9e7bad651f..ffee7c1f55 100644
---
a/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedBloomFilter.java
+++
b/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedBloomFilter.java
@@ -18,25 +18,20 @@
*/
package org.apache.hadoop.hdds.utils.db.managed;
+import org.apache.hadoop.hdds.utils.LeakTracker;
import org.rocksdb.BloomFilter;
-import javax.annotation.Nullable;
-
-import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.assertClosed;
-import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.formatStackTrace;
-import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.getStackTrace;
+import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.track;
/**
* Managed BloomFilter.
*/
public class ManagedBloomFilter extends BloomFilter {
-
- @Nullable
- private final StackTraceElement[] elements = getStackTrace();
+ private final LeakTracker leakTracker = track(this);
@Override
- protected void finalize() throws Throwable {
- assertClosed(this, formatStackTrace(elements));
- super.finalize();
+ public void close() {
+ super.close();
+ leakTracker.close();
}
}
diff --git
a/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedColumnFamilyOptions.java
b/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedColumnFamilyOptions.java
index 39ee7b0ce7..7b1da6a169 100644
---
a/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedColumnFamilyOptions.java
+++
b/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedColumnFamilyOptions.java
@@ -18,31 +18,24 @@
*/
package org.apache.hadoop.hdds.utils.db.managed;
+import org.apache.hadoop.hdds.utils.LeakTracker;
import org.rocksdb.ColumnFamilyOptions;
import org.rocksdb.TableFormatConfig;
-import javax.annotation.Nullable;
-
-import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.assertClosed;
-import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.formatStackTrace;
-import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.getStackTrace;
+import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.track;
/**
* Managed ColumnFamilyOptions.
*/
public class ManagedColumnFamilyOptions extends ColumnFamilyOptions {
-
- @Nullable
- private final StackTraceElement[] elements = getStackTrace();
-
/**
* Indicate if this ColumnFamilyOptions is intentionally used across RockDB
* instances.
*/
private boolean reused = false;
+ private final LeakTracker leakTracker = track(this);
public ManagedColumnFamilyOptions() {
- super();
}
public ManagedColumnFamilyOptions(ColumnFamilyOptions columnFamilyOptions) {
@@ -85,9 +78,9 @@ public class ManagedColumnFamilyOptions extends
ColumnFamilyOptions {
}
@Override
- protected void finalize() throws Throwable {
- assertClosed(this, formatStackTrace(elements));
- super.finalize();
+ public void close() {
+ super.close();
+ leakTracker.close();
}
/**
diff --git
a/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedCompactRangeOptions.java
b/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedCompactRangeOptions.java
index 6f7da30cdb..0e397ed0e9 100644
---
a/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedCompactRangeOptions.java
+++
b/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedCompactRangeOptions.java
@@ -18,25 +18,20 @@
*/
package org.apache.hadoop.hdds.utils.db.managed;
+import org.apache.hadoop.hdds.utils.LeakTracker;
import org.rocksdb.CompactRangeOptions;
-import javax.annotation.Nullable;
-
-import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.assertClosed;
-import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.formatStackTrace;
-import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.getStackTrace;
+import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.track;
/**
* Managed CompactRangeOptions.
*/
public class ManagedCompactRangeOptions extends CompactRangeOptions {
-
- @Nullable
- private final StackTraceElement[] elements = getStackTrace();
+ private final LeakTracker leakTracker = track(this);
@Override
- protected void finalize() throws Throwable {
- assertClosed(this, formatStackTrace(elements));
- super.finalize();
+ public void close() {
+ super.close();
+ leakTracker.close();
}
}
diff --git
a/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedDBOptions.java
b/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedDBOptions.java
index a66a04eae9..fa01e2e101 100644
---
a/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedDBOptions.java
+++
b/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedDBOptions.java
@@ -18,25 +18,20 @@
*/
package org.apache.hadoop.hdds.utils.db.managed;
+import org.apache.hadoop.hdds.utils.LeakTracker;
import org.rocksdb.DBOptions;
-import javax.annotation.Nullable;
-
-import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.assertClosed;
-import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.formatStackTrace;
-import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.getStackTrace;
+import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.track;
/**
* Managed DBOptions.
*/
public class ManagedDBOptions extends DBOptions {
-
- @Nullable
- private final StackTraceElement[] elements = getStackTrace();
+ private final LeakTracker leakTracker = track(this);
@Override
- protected void finalize() throws Throwable {
- assertClosed(this, formatStackTrace(elements));
- super.finalize();
+ public void close() {
+ super.close();
+ leakTracker.close();
}
}
diff --git
a/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedEnvOptions.java
b/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedEnvOptions.java
index 38d6f95ce7..baad1ad7f4 100644
---
a/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedEnvOptions.java
+++
b/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedEnvOptions.java
@@ -18,25 +18,20 @@
*/
package org.apache.hadoop.hdds.utils.db.managed;
+import org.apache.hadoop.hdds.utils.LeakTracker;
import org.rocksdb.EnvOptions;
-import javax.annotation.Nullable;
-
-import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.assertClosed;
-import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.formatStackTrace;
-import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.getStackTrace;
+import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.track;
/**
* Managed EnvOptions.
*/
public class ManagedEnvOptions extends EnvOptions {
-
- @Nullable
- private final StackTraceElement[] elements = getStackTrace();
+ private final LeakTracker leakTracker = track(this);
@Override
- protected void finalize() throws Throwable {
- assertClosed(this, formatStackTrace(elements));
- super.finalize();
+ public void close() {
+ super.close();
+ leakTracker.close();
}
}
diff --git
a/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedFlushOptions.java
b/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedFlushOptions.java
index 8801f7d241..126f5336ba 100644
---
a/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedFlushOptions.java
+++
b/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedFlushOptions.java
@@ -18,25 +18,20 @@
*/
package org.apache.hadoop.hdds.utils.db.managed;
+import org.apache.hadoop.hdds.utils.LeakTracker;
import org.rocksdb.FlushOptions;
-import javax.annotation.Nullable;
-
-import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.assertClosed;
-import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.formatStackTrace;
-import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.getStackTrace;
+import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.track;
/**
* Managed FlushOptions.
*/
public class ManagedFlushOptions extends FlushOptions {
-
- @Nullable
- private final StackTraceElement[] elements = getStackTrace();
+ private final LeakTracker leakTracker = track(this);
@Override
- protected void finalize() throws Throwable {
- assertClosed(this, formatStackTrace(elements));
- super.finalize();
+ public void close() {
+ super.close();
+ leakTracker.close();
}
}
diff --git
a/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedIngestExternalFileOptions.java
b/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedIngestExternalFileOptions.java
index 94b34e20d2..1783a34587 100644
---
a/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedIngestExternalFileOptions.java
+++
b/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedIngestExternalFileOptions.java
@@ -18,26 +18,20 @@
*/
package org.apache.hadoop.hdds.utils.db.managed;
+import org.apache.hadoop.hdds.utils.LeakTracker;
import org.rocksdb.IngestExternalFileOptions;
-import javax.annotation.Nullable;
-
-import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.assertClosed;
-import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.formatStackTrace;
-import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.getStackTrace;
+import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.track;
/**
* Managed IngestExternalFileOptions.
*/
-public class ManagedIngestExternalFileOptions extends
- IngestExternalFileOptions {
-
- @Nullable
- private final StackTraceElement[] elements = getStackTrace();
+public class ManagedIngestExternalFileOptions extends
IngestExternalFileOptions {
+ private final LeakTracker leakTracker = track(this);
@Override
- protected void finalize() throws Throwable {
- assertClosed(this, formatStackTrace(elements));
- super.finalize();
+ public void close() {
+ super.close();
+ leakTracker.close();
}
}
diff --git
a/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedLRUCache.java
b/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedLRUCache.java
index 8bf9147c15..5244863a5a 100644
---
a/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedLRUCache.java
+++
b/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedLRUCache.java
@@ -18,29 +18,24 @@
*/
package org.apache.hadoop.hdds.utils.db.managed;
+import org.apache.hadoop.hdds.utils.LeakTracker;
import org.rocksdb.LRUCache;
-import javax.annotation.Nullable;
-
-import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.assertClosed;
-import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.formatStackTrace;
-import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.getStackTrace;
+import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.track;
/**
* Managed LRUCache.
*/
public class ManagedLRUCache extends LRUCache {
-
- @Nullable
- private final StackTraceElement[] elements = getStackTrace();
+ private final LeakTracker leakTracker = track(this);
public ManagedLRUCache(long capacity) {
super(capacity);
}
@Override
- protected void finalize() throws Throwable {
- assertClosed(this, formatStackTrace(elements));
- super.finalize();
+ public void close() {
+ super.close();
+ leakTracker.close();
}
}
diff --git
a/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedObject.java
b/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedObject.java
index 0ab87f156e..1e4068a7a8 100644
---
a/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedObject.java
+++
b/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedObject.java
@@ -18,11 +18,10 @@
*/
package org.apache.hadoop.hdds.utils.db.managed;
+import org.apache.hadoop.hdds.utils.LeakTracker;
import org.rocksdb.RocksObject;
-import javax.annotation.Nullable;
-
-import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.formatStackTrace;
+import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.track;
/**
* General template for a managed RocksObject.
@@ -30,13 +29,10 @@ import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.fo
*/
class ManagedObject<T extends RocksObject> implements AutoCloseable {
private final T original;
-
- @Nullable
- private final StackTraceElement[] elements;
+ private final LeakTracker leakTracker = track(this);
ManagedObject(T original) {
this.original = original;
- this.elements = ManagedRocksObjectUtils.getStackTrace();
}
public T get() {
@@ -46,16 +42,6 @@ class ManagedObject<T extends RocksObject> implements
AutoCloseable {
@Override
public void close() {
original.close();
+ leakTracker.close();
}
-
- @Override
- protected void finalize() throws Throwable {
- ManagedRocksObjectUtils.assertClosed(this);
- super.finalize();
- }
-
- public String getStackTrace() {
- return formatStackTrace(elements);
- }
-
}
diff --git
a/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedOptions.java
b/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedOptions.java
index 4ae96e1b76..e438068e3a 100644
---
a/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedOptions.java
+++
b/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedOptions.java
@@ -18,25 +18,20 @@
*/
package org.apache.hadoop.hdds.utils.db.managed;
+import org.apache.hadoop.hdds.utils.LeakTracker;
import org.rocksdb.Options;
-import javax.annotation.Nullable;
-
-import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.assertClosed;
-import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.formatStackTrace;
-import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.getStackTrace;
+import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.track;
/**
* Managed Options.
*/
public class ManagedOptions extends Options {
-
- @Nullable
- private final StackTraceElement[] elements = getStackTrace();
+ private final LeakTracker leakTracker = track(this);
@Override
- protected void finalize() throws Throwable {
- assertClosed(this, formatStackTrace(elements));
- super.finalize();
+ public void close() {
+ super.close();
+ leakTracker.close();
}
}
diff --git
a/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedReadOptions.java
b/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedReadOptions.java
index a281722d24..48c2238ec4 100644
---
a/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedReadOptions.java
+++
b/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedReadOptions.java
@@ -18,26 +18,20 @@
*/
package org.apache.hadoop.hdds.utils.db.managed;
+import org.apache.hadoop.hdds.utils.LeakTracker;
import org.rocksdb.ReadOptions;
-import javax.annotation.Nullable;
-
-import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.assertClosed;
-import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.formatStackTrace;
-import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.getStackTrace;
+import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.track;
/**
* Managed {@link ReadOptions}.
*/
public class ManagedReadOptions extends ReadOptions {
-
- @Nullable
- private final StackTraceElement[] elements = getStackTrace();
+ private final LeakTracker leakTracker = track(this);
@Override
- protected void finalize() throws Throwable {
- assertClosed(this, formatStackTrace(elements));
- super.finalize();
+ public void close() {
+ super.close();
+ leakTracker.close();
}
-
}
diff --git
a/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedRocksObjectMetrics.java
b/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedRocksObjectMetrics.java
index 8d9d7fe78d..706aa9bb34 100644
---
a/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedRocksObjectMetrics.java
+++
b/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedRocksObjectMetrics.java
@@ -18,6 +18,7 @@
*/
package org.apache.hadoop.hdds.utils.db.managed;
+import com.google.common.annotations.VisibleForTesting;
import org.apache.hadoop.hdds.annotation.InterfaceAudience;
import org.apache.hadoop.metrics2.annotation.Metric;
import org.apache.hadoop.metrics2.annotation.Metrics;
@@ -64,4 +65,14 @@ public class ManagedRocksObjectMetrics {
void increaseManagedObject() {
totalManagedObjects.incr();
}
+
+ @VisibleForTesting
+ long totalLeakObjects() {
+ return totalLeakObjects.value();
+ }
+
+ @VisibleForTesting
+ long totalManagedObjects() {
+ return totalManagedObjects.value();
+ }
}
diff --git
a/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedRocksObjectUtils.java
b/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedRocksObjectUtils.java
index b32f1111cb..7ae7001ccd 100644
---
a/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedRocksObjectUtils.java
+++
b/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedRocksObjectUtils.java
@@ -19,10 +19,11 @@
package org.apache.hadoop.hdds.utils.db.managed;
import org.apache.hadoop.hdds.HddsUtils;
+import org.apache.hadoop.hdds.utils.LeakDetector;
+import org.apache.hadoop.hdds.utils.LeakTracker;
import org.awaitility.Awaitility;
import org.awaitility.core.ConditionTimeoutException;
import org.rocksdb.RocksDB;
-import org.rocksdb.RocksObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -41,38 +42,36 @@ public final class ManagedRocksObjectUtils {
public static final Logger LOG =
LoggerFactory.getLogger(ManagedRocksObjectUtils.class);
+
private static final Duration POLL_DELAY_DURATION = Duration.ZERO;
private static final Duration POLL_INTERVAL_DURATION =
Duration.ofMillis(100);
- public static void assertClosed(ManagedObject<?> object) {
- assertClosed(object.get(), object.getStackTrace());
- }
- static void assertClosed(RocksObject rocksObject, String stackTrace) {
+ private static final LeakDetector LEAK_DETECTOR = new
LeakDetector("ManagedRocksObject");
+
+ static LeakTracker track(AutoCloseable object) {
ManagedRocksObjectMetrics.INSTANCE.increaseManagedObject();
- if (rocksObject.isOwningHandle()) {
- reportLeak(rocksObject, stackTrace);
- }
+ final Class<?> clazz = object.getClass();
+ final StackTraceElement[] stackTrace = getStackTrace();
+ return LEAK_DETECTOR.track(object, () -> reportLeak(clazz,
formatStackTrace(stackTrace)));
}
- static void reportLeak(Object object, String stackTrace) {
+ static void reportLeak(Class<?> clazz, String stackTrace) {
ManagedRocksObjectMetrics.INSTANCE.increaseLeakObject();
- String warning = String.format("%s is not closed properly",
- object.getClass().getSimpleName());
+ String warning = String.format("%s is not closed properly",
clazz.getSimpleName());
if (stackTrace != null && LOG.isDebugEnabled()) {
- String debugMessage = String
- .format("%nStackTrace for unclosed instance: %s", stackTrace);
+ String debugMessage = String.format("%nStackTrace for unclosed instance:
%s", stackTrace);
warning = warning.concat(debugMessage);
}
LOG.warn(warning);
}
- static @Nullable StackTraceElement[] getStackTrace() {
+ private static @Nullable StackTraceElement[] getStackTrace() {
return HddsUtils.getStackTrace(LOG);
}
static String formatStackTrace(@Nullable StackTraceElement[] elements) {
- return HddsUtils.formatStackTrace(elements, 3);
+ return HddsUtils.formatStackTrace(elements, 4);
}
/**
diff --git
a/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedSlice.java
b/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedSlice.java
index 2de2031fb9..8c366bdaa4 100644
---
a/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedSlice.java
+++
b/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedSlice.java
@@ -18,23 +18,19 @@
*/
package org.apache.hadoop.hdds.utils.db.managed;
+import org.apache.hadoop.hdds.utils.LeakTracker;
import org.rocksdb.Slice;
-import javax.annotation.Nullable;
-
-import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.formatStackTrace;
+import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.track;
/**
* Managed Slice.
*/
public class ManagedSlice extends Slice {
+ private final LeakTracker leakTracker = track(this);
- @Nullable
- private final StackTraceElement[] elements;
-
- public ManagedSlice(byte[] var1) {
- super(var1);
- this.elements = ManagedRocksObjectUtils.getStackTrace();
+ public ManagedSlice(byte[] data) {
+ super(data);
}
@Override
@@ -43,12 +39,10 @@ public class ManagedSlice extends Slice {
}
@Override
- protected void finalize() throws Throwable {
- ManagedRocksObjectMetrics.INSTANCE.increaseManagedObject();
- if (isOwningHandle()) {
- ManagedRocksObjectUtils.reportLeak(this, formatStackTrace(elements));
- }
- super.finalize();
+ protected void disposeInternal() {
+ super.disposeInternal();
+ // RocksMutableObject.close is final thus can't be decorated.
+ // So, we decorate disposeInternal instead to track closure.
+ leakTracker.close();
}
-
}
diff --git
a/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedSstFileReader.java
b/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedSstFileReader.java
index 7ba1001a43..b49c6e7a9e 100644
---
a/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedSstFileReader.java
+++
b/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedSstFileReader.java
@@ -29,15 +29,7 @@ public class ManagedSstFileReader extends
ManagedObject<SstFileReader> {
super(original);
}
- public static ManagedSstFileReader managed(
- SstFileReader reader) {
+ public static ManagedSstFileReader managed(SstFileReader reader) {
return new ManagedSstFileReader(reader);
}
-
- @Override
- protected void finalize() throws Throwable {
- ManagedRocksObjectUtils.assertClosed(this);
- super.finalize();
- }
-
}
diff --git
a/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedSstFileReaderIterator.java
b/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedSstFileReaderIterator.java
index 0916e89571..660a7c347a 100644
---
a/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedSstFileReaderIterator.java
+++
b/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedSstFileReaderIterator.java
@@ -23,19 +23,12 @@ import org.rocksdb.SstFileReaderIterator;
/**
* Managed SstFileReaderIterator.
*/
-public class ManagedSstFileReaderIterator
- extends ManagedObject<SstFileReaderIterator> {
+public class ManagedSstFileReaderIterator extends
ManagedObject<SstFileReaderIterator> {
ManagedSstFileReaderIterator(SstFileReaderIterator original) {
super(original);
}
- @Override
- protected void finalize() throws Throwable {
- ManagedRocksObjectUtils.assertClosed(this);
- super.finalize();
- }
-
public static ManagedSstFileReaderIterator managed(
SstFileReaderIterator iterator) {
return new ManagedSstFileReaderIterator(iterator);
diff --git
a/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedSstFileWriter.java
b/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedSstFileWriter.java
index e6fdcbc2ed..de7e9d5266 100644
---
a/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedSstFileWriter.java
+++
b/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedSstFileWriter.java
@@ -18,23 +18,18 @@
*/
package org.apache.hadoop.hdds.utils.db.managed;
+import org.apache.hadoop.hdds.utils.LeakTracker;
import org.rocksdb.EnvOptions;
import org.rocksdb.Options;
import org.rocksdb.SstFileWriter;
-import javax.annotation.Nullable;
-
-import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.assertClosed;
-import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.formatStackTrace;
-import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.getStackTrace;
+import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.track;
/**
* Managed SstFileWriter.
*/
public class ManagedSstFileWriter extends SstFileWriter {
-
- @Nullable
- private final StackTraceElement[] elements = getStackTrace();
+ private final LeakTracker leakTracker = track(this);
public ManagedSstFileWriter(EnvOptions envOptions,
Options options) {
@@ -42,8 +37,8 @@ public class ManagedSstFileWriter extends SstFileWriter {
}
@Override
- protected void finalize() throws Throwable {
- assertClosed(this, formatStackTrace(elements));
- super.finalize();
+ public void close() {
+ super.close();
+ leakTracker.close();
}
}
diff --git
a/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedStatistics.java
b/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedStatistics.java
index 4cbd6f9828..75af8b8813 100644
---
a/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedStatistics.java
+++
b/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedStatistics.java
@@ -18,25 +18,20 @@
*/
package org.apache.hadoop.hdds.utils.db.managed;
+import org.apache.hadoop.hdds.utils.LeakTracker;
import org.rocksdb.Statistics;
-import javax.annotation.Nullable;
-
-import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.assertClosed;
-import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.formatStackTrace;
-import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.getStackTrace;
+import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.track;
/**
* Managed Statistics.
*/
public class ManagedStatistics extends Statistics {
-
- @Nullable
- private final StackTraceElement[] elements = getStackTrace();
+ private final LeakTracker leakTracker = track(this);
@Override
- protected void finalize() throws Throwable {
- assertClosed(this, formatStackTrace(elements));
- super.finalize();
+ public void close() {
+ super.close();
+ leakTracker.close();
}
}
diff --git
a/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedWriteBatch.java
b/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedWriteBatch.java
index 51c9bcc0df..b1411b09a4 100644
---
a/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedWriteBatch.java
+++
b/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedWriteBatch.java
@@ -18,24 +18,18 @@
*/
package org.apache.hadoop.hdds.utils.db.managed;
+import org.apache.hadoop.hdds.utils.LeakTracker;
import org.rocksdb.WriteBatch;
-import javax.annotation.Nullable;
-
-import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.assertClosed;
-import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.formatStackTrace;
-import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.getStackTrace;
+import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.track;
/**
* Managed WriteBatch.
*/
public class ManagedWriteBatch extends WriteBatch {
-
- @Nullable
- private final StackTraceElement[] elements = getStackTrace();
+ private final LeakTracker leakTracker = track(this);
public ManagedWriteBatch() {
- super();
}
public ManagedWriteBatch(byte[] data) {
@@ -43,8 +37,8 @@ public class ManagedWriteBatch extends WriteBatch {
}
@Override
- protected void finalize() throws Throwable {
- assertClosed(this, formatStackTrace(elements));
- super.finalize();
+ public void close() {
+ super.close();
+ leakTracker.close();
}
}
diff --git
a/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedWriteOptions.java
b/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedWriteOptions.java
index 7ba05a0ee6..5d32a290b5 100644
---
a/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedWriteOptions.java
+++
b/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/ManagedWriteOptions.java
@@ -18,25 +18,20 @@
*/
package org.apache.hadoop.hdds.utils.db.managed;
+import org.apache.hadoop.hdds.utils.LeakTracker;
import org.rocksdb.WriteOptions;
-import javax.annotation.Nullable;
-
-import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.assertClosed;
-import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.formatStackTrace;
-import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.getStackTrace;
+import static
org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils.track;
/**
* Managed {@link WriteOptions}.
*/
public class ManagedWriteOptions extends WriteOptions {
-
- @Nullable
- private final StackTraceElement[] elements = getStackTrace();
+ private final LeakTracker leakTracker = track(this);
@Override
- protected void finalize() throws Throwable {
- assertClosed(this, formatStackTrace(elements));
- super.finalize();
+ public void close() {
+ super.close();
+ leakTracker.close();
}
}
diff --git
a/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/package-info.java
b/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/package-info.java
index c60dd4c82e..ee214f8150 100644
---
a/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/package-info.java
+++
b/hadoop-hdds/managed-rocksdb/src/main/java/org/apache/hadoop/hdds/utils/db/managed/package-info.java
@@ -18,18 +18,16 @@
*/
/**
- * RocksDB is deprecating the RocksObject's finalizer that cleans up native
- * resources. In fact, the finalizer is removed in the new version of RocksDB
- * as per https://github.com/facebook/rocksdb/commit/99d86252b. That poses a
+ * RocksDB has deprecated the RocksObject's finalizer that cleans up native
+ * resources, see https://github.com/facebook/rocksdb/commit/99d86252b. That
poses a
* requirement for RocksDb's applications to explicitly close RocksObject
* instances themselves to avoid leaking native resources. The general approach
* is to close RocksObjects with try-with-resource statement.
* Yet, this is not always an easy option in Ozone we need a mechanism to
* manage and detect leaks.
*
- * This package contains wrappers and utilities to catch RocksObject
- * instantiates in Ozone, intercept their finalizers and assert if the created
- * instances are closed properly before being GCed.
+ * This package contains RocksObject decorators and utilities to catch track
RocksObject's
+ * lifecycle to ensure they're properly closed before being GCed.
*/
package org.apache.hadoop.hdds.utils.db.managed;
diff --git
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/hdds/utils/db/managed/TestRocksObjectLeakDetector.java
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/hdds/utils/db/managed/TestRocksObjectLeakDetector.java
new file mode 100644
index 0000000000..87fbe23ac7
--- /dev/null
+++
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/hdds/utils/db/managed/TestRocksObjectLeakDetector.java
@@ -0,0 +1,114 @@
+/*
+ * 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.managed;
+
+import org.apache.commons.lang3.RandomUtils;
+import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.ozone.MiniOzoneCluster;
+import org.apache.ozone.test.tag.Unhealthy;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.util.UUID;
+import java.util.concurrent.TimeoutException;
+import java.util.function.Supplier;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+/**
+ * Test managed rocks object leak detection.
+ * This test creates garbage that will fail other tests and is intended for
manual run only.
+ * It is also flaky because of depending on background processes and
environment (other background tasks
+ * can create extra managed rocks objects and thus fails the counter
assertions).
+ */
+@Unhealthy
+public class TestRocksObjectLeakDetector {
+
+ private static MiniOzoneCluster cluster;
+
+ @BeforeAll
+ static void setUp() throws IOException, InterruptedException,
+ TimeoutException {
+ OzoneConfiguration conf = new OzoneConfiguration();
+ String clusterId = UUID.randomUUID().toString();
+ String scmId = UUID.randomUUID().toString();
+ String omServiceId = "omServiceId1";
+ cluster = MiniOzoneCluster.newBuilder(conf)
+ .setClusterId(clusterId)
+ .setScmId(scmId)
+ .setOMServiceId(omServiceId)
+ .setNumOfOzoneManagers(1)
+ .build();
+ cluster.waitForClusterToBeReady();
+ }
+
+ @AfterAll
+ static void cleanUp() {
+ if (cluster != null) {
+ assertThrows(AssertionError.class, () -> cluster.shutdown());
+ }
+ }
+
+ @Test
+ public void testLeakDetector() throws Exception {
+ testLeakDetector(ManagedBloomFilter::new);
+ testLeakDetector(ManagedColumnFamilyOptions::new);
+ testLeakDetector(ManagedEnvOptions::new);
+ testLeakDetector(ManagedFlushOptions::new);
+ testLeakDetector(ManagedIngestExternalFileOptions::new);
+ testLeakDetector(() -> new ManagedLRUCache(1L));
+ testLeakDetector(ManagedOptions::new);
+ testLeakDetector(ManagedReadOptions::new);
+ testLeakDetector(() -> new ManagedSlice(RandomUtils.nextBytes(10)));
+ testLeakDetector(ManagedStatistics::new);
+ testLeakDetector(ManagedWriteBatch::new);
+ testLeakDetector(ManagedWriteOptions::new);
+ }
+
+ private <T extends AutoCloseable> void testLeakDetector(Supplier<T>
supplier) throws Exception {
+ // base metrics
+ long managedObjects =
ManagedRocksObjectMetrics.INSTANCE.totalManagedObjects();
+ long leakObjects = ManagedRocksObjectMetrics.INSTANCE.totalLeakObjects();
+
+ // Allocate and close.
+ allocate(supplier, true);
+ System.gc();
+ // it could take a while for leak detection to run in the background.
+ Thread.sleep(500);
+ assertEquals(managedObjects + 1,
ManagedRocksObjectMetrics.INSTANCE.totalManagedObjects());
+ assertEquals(leakObjects,
ManagedRocksObjectMetrics.INSTANCE.totalLeakObjects());
+
+ // Allocate and not close.
+ allocate(supplier, false);
+ System.gc();
+ Thread.sleep(500);
+ assertEquals(managedObjects + 2,
ManagedRocksObjectMetrics.INSTANCE.totalManagedObjects());
+ assertEquals(leakObjects + 1,
ManagedRocksObjectMetrics.INSTANCE.totalLeakObjects());
+ }
+
+ private <T extends AutoCloseable> void allocate(Supplier<T> supplier,
boolean close) throws Exception {
+ T object = supplier.get();
+ if (close) {
+ object.close();
+ }
+ }
+}
diff --git
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/MiniOzoneClusterImpl.java
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/MiniOzoneClusterImpl.java
index e2e7373569..50c89dd3e6 100644
---
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/MiniOzoneClusterImpl.java
+++
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/MiniOzoneClusterImpl.java
@@ -448,15 +448,14 @@ public class MiniOzoneClusterImpl implements
MiniOzoneCluster {
public void shutdown() {
try {
LOG.info("Shutting down the Mini Ozone Cluster");
+ CodecTestUtil.gc();
IOUtils.closeQuietly(clients);
final File baseDir = new File(getBaseDir());
stop();
FileUtils.deleteDirectory(baseDir);
ContainerCache.getInstance(conf).shutdownCache();
DefaultMetricsSystem.shutdown();
-
ManagedRocksObjectMetrics.INSTANCE.assertNoLeaks();
- CodecTestUtil.gc();
} catch (Exception e) {
LOG.error("Exception while shutting down the cluster.", e);
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]