From eb2c9e1f9e7ee98e5032720124be51b84ee08a69 Mon Sep 17 00:00:00 2001 From: Wei-Chiu Chuang Date: Tue, 31 Mar 2026 16:03:38 -0700 Subject: [PATCH 01/10] OM Snapshot UI -- backend code. Change-Id: Iea21514269d37a233c816a2cc55be44fe788382a --- .../om/snapshot/SnapshotDiffManager.java | 35 ++++++++++++++++++- .../snapshot/SnapshotDiffManagerMXBean.java | 35 +++++++++++++++++++ .../om/snapshot/TestSnapshotDiffManager.java | 17 +++++++++ 3 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/SnapshotDiffManagerMXBean.java diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/SnapshotDiffManager.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/SnapshotDiffManager.java index 9dbf546f18d1..b66c8f4e0f8c 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/SnapshotDiffManager.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/SnapshotDiffManager.java @@ -107,10 +107,14 @@ import org.apache.hadoop.hdds.utils.db.StringCodec; import org.apache.hadoop.hdds.utils.db.Table; import org.apache.hadoop.hdds.utils.db.TablePrefixInfo; +import javax.management.ObjectName; +import org.apache.hadoop.hdds.HddsUtils; +import org.apache.hadoop.hdds.annotation.InterfaceAudience; import org.apache.hadoop.hdds.utils.db.managed.ManagedColumnFamilyOptions; import org.apache.hadoop.hdds.utils.db.managed.ManagedRocksDB; import org.apache.hadoop.hdfs.protocol.SnapshotDiffReport.DiffReportEntry; import org.apache.hadoop.hdfs.protocol.SnapshotDiffReport.DiffType; +import org.apache.hadoop.metrics2.util.MBeans; import org.apache.hadoop.ozone.OFSPath; import org.apache.hadoop.ozone.OzoneConsts; import org.apache.hadoop.ozone.om.OMMetadataManager; @@ -147,7 +151,8 @@ /** * Class to generate snapshot diff. */ -public class SnapshotDiffManager implements AutoCloseable { +@InterfaceAudience.Private +public class SnapshotDiffManager implements AutoCloseable, SnapshotDiffManagerMXBean { private static final Logger LOG = LoggerFactory.getLogger(SnapshotDiffManager.class); private static final Map DIFF_TYPE_STRING_MAP = @@ -177,6 +182,7 @@ public class SnapshotDiffManager implements AutoCloseable { */ private final PersistentMap snapDiffJobTable; private final ExecutorService snapDiffExecutor; + private ObjectName snapshotDiffManagerBeanName; /** * Directory to keep hardlinks of SST files for a snapDiff job temporarily. @@ -280,6 +286,7 @@ public SnapshotDiffManager(ManagedRocksDB db, // When we build snapDiff HA aware, we will revisit this. // Details: https://github.com/apache/ozone/pull/4438#discussion_r1149788226 this.loadJobsOnStartUp(); + this.registerMXBean(); } @VisibleForTesting @@ -1514,8 +1521,34 @@ void loadJobsOnStartUp() { } } + @Override + public List getSnapshotDiffJobs() { + List jobs = new ArrayList<>(); + try (ClosableIterator> iterator = + snapDiffJobTable.iterator()) { + while (iterator.hasNext()) { + jobs.add(iterator.next().getValue()); + } + } + return jobs; + } + + private void registerMXBean() { + this.snapshotDiffManagerBeanName = HddsUtils.registerWithJmxProperties( + "OzoneManager", "SnapshotDiffManager", + Collections.emptyMap(), this); + } + + private void unregisterMXBean() { + if (this.snapshotDiffManagerBeanName != null) { + MBeans.unregister(this.snapshotDiffManagerBeanName); + this.snapshotDiffManagerBeanName = null; + } + } + @Override public void close() { + unregisterMXBean(); if (snapDiffExecutor != null) { closeExecutorService(snapDiffExecutor, "SnapDiffExecutor"); } diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/SnapshotDiffManagerMXBean.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/SnapshotDiffManagerMXBean.java new file mode 100644 index 000000000000..62626500ef4d --- /dev/null +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/SnapshotDiffManagerMXBean.java @@ -0,0 +1,35 @@ +/* + * 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.snapshot; + +import org.apache.hadoop.hdds.annotation.InterfaceAudience; +import org.apache.hadoop.ozone.om.helpers.SnapshotDiffJob; + +import java.util.List; + +/** + * JMX interface for SnapshotDiffManager. + */ +@InterfaceAudience.Private +public interface SnapshotDiffManagerMXBean { + /** + * Returns all snapshot diff jobs. + * @return list of snapshot diff jobs + */ + List getSnapshotDiffJobs(); +} diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/snapshot/TestSnapshotDiffManager.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/snapshot/TestSnapshotDiffManager.java index 6327b712dee3..2879780421f4 100644 --- a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/snapshot/TestSnapshotDiffManager.java +++ b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/snapshot/TestSnapshotDiffManager.java @@ -1365,4 +1365,21 @@ public void testGetSnapshotDiffReportJob() throws Exception { } } } + + @Test + public void testGetSnapshotDiffJobs() throws Exception { + // Initially no jobs + List jobs = snapshotDiffManager.getSnapshotDiffJobs(); + assertEquals(0, jobs.size()); + + // Submit a job + snapshotDiffManager.getSnapshotDiffReport(VOLUME_NAME, BUCKET_NAME, + snapshotNames.get(0), snapshotNames.get(1), + "", 100, false, false); + + jobs = snapshotDiffManager.getSnapshotDiffJobs(); + assertEquals(1, jobs.size()); + assertEquals(snapshotNames.get(0), jobs.get(0).getFromSnapshot()); + assertEquals(snapshotNames.get(1), jobs.get(0).getToSnapshot()); + } } From 26bf22fa3f1248db137d0b594f5f584eadf92407 Mon Sep 17 00:00:00 2001 From: Wei-Chiu Chuang Date: Tue, 31 Mar 2026 17:38:13 -0700 Subject: [PATCH 02/10] Frontend Change-Id: I93ba5bf171938338f00af6e1f07f27b9cee2bfe2 --- .../main/resources/webapps/static/ozone.js | 2 + .../webapps/static/templates/menu.html | 6 +- .../resources/webapps/ozoneManager/index.html | 4 +- .../webapps/ozoneManager/om-snapshots.html | 63 +++++++++++++++++++ .../webapps/ozoneManager/ozoneManager.js | 40 ++++++++++++ 5 files changed, 112 insertions(+), 3 deletions(-) create mode 100644 hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/om-snapshots.html diff --git a/hadoop-hdds/framework/src/main/resources/webapps/static/ozone.js b/hadoop-hdds/framework/src/main/resources/webapps/static/ozone.js index aac641e625e1..2835c3633e5e 100644 --- a/hadoop-hdds/framework/src/main/resources/webapps/static/ozone.js +++ b/hadoop-hdds/framework/src/main/resources/webapps/static/ozone.js @@ -256,6 +256,8 @@ ioLinkHref: '@', scanner: '<', scannerLinkHref: '@', + snapshot: '@', + snapshotLinkHref: '@' }, templateUrl: 'static/templates/menu.html', controller: function($http) { diff --git a/hadoop-hdds/framework/src/main/resources/webapps/static/templates/menu.html b/hadoop-hdds/framework/src/main/resources/webapps/static/templates/menu.html index 9a14f356d7a4..920d2ac606d6 100644 --- a/hadoop-hdds/framework/src/main/resources/webapps/static/templates/menu.html +++ b/hadoop-hdds/framework/src/main/resources/webapps/static/templates/menu.html @@ -58,5 +58,7 @@
  • IO Status
  • Data Scanner
  • - - +
  • Snapshots
  • + + + diff --git a/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/index.html b/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/index.html index 54accf457f34..153000c2c917 100644 --- a/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/index.html +++ b/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/index.html @@ -49,7 +49,9 @@ Ozone Manager + metrics="{ 'OM metrics' : '#!/metrics/ozoneManager', 'Rpc metrics' : '#!/metrics/rpc'}" + snapshot="true" + snapshot-link-href="#!/snapshots"> diff --git a/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/om-snapshots.html b/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/om-snapshots.html new file mode 100644 index 000000000000..f7adf69c311b --- /dev/null +++ b/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/om-snapshots.html @@ -0,0 +1,63 @@ + +

    Snapshot Metrics

    + + + + + + + + + + + + + + +
    Metric NameValue
    {{metric.key}}{{metric.value}}
    + +

    Snapshot Diff Jobs

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Job IDStatusVolumeBucketFrom SnapshotTo SnapshotProgressTotal Diff EntriesCreation Time
    {{job.JobId}}{{job.Status}}{{job.Volume}}{{job.Bucket}}{{job.FromSnapshot}}{{job.ToSnapshot}}{{job.KeysProcessedPct * 100 | number:2}}%{{job.TotalDiffEntries}}{{job.CreationTime | date:'yyyy-MM-dd HH:mm:ss'}}
    diff --git a/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/ozoneManager.js b/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/ozoneManager.js index 8269b6df0fbb..eea3b0a8aef0 100644 --- a/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/ozoneManager.js +++ b/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/ozoneManager.js @@ -27,8 +27,48 @@ $routeProvider .when("/metrics/ozoneManager", { template: "" + }) + .when("/snapshots", { + template: "" }); }); + angular.module('ozoneManager').component('omSnapshots', { + templateUrl: 'om-snapshots.html', + controller: function ($http) { + var ctrl = this; + ctrl.snapshotMetrics = []; + ctrl.snapshotDiffJobs = []; + + $http.get("jmx?qry=Hadoop:service=OzoneManager,name=OMMetrics") + .then(function (result) { + var metrics = result.data.beans[0]; + for (var key in metrics) { + if (key.match(/NumSnapshot|NumCancelSnapshotDiff|NumListSnapshotDiffJob/)) { + ctrl.snapshotMetrics.push({key: key, value: metrics[key]}); + } + } + }); + + $http.get("jmx?qry=Hadoop:service=OzoneManager,name=OmSnapshotInternalMetrics") + .then(function (result) { + if (result.data.beans && result.data.beans.length > 0) { + var metrics = result.data.beans[0]; + for (var key in metrics) { + if (!isIgnoredJmxKeys(key)) { + ctrl.snapshotMetrics.push({key: key, value: metrics[key]}); + } + } + } + }); + + $http.get("jmx?qry=OzoneManager:name=SnapshotDiffManager") + .then(function (result) { + if (result.data.beans && result.data.beans.length > 0) { + ctrl.snapshotDiffJobs = result.data.beans[0].SnapshotDiffJobs; + } + }); + } + }); angular.module('ozoneManager').component('omMetrics', { templateUrl: 'om-metrics.html', controller: function ($http) { From 84f6d8eba85c6585d8d5539cd0ae1f48397e5ea9 Mon Sep 17 00:00:00 2001 From: Wei-Chiu Chuang Date: Tue, 31 Mar 2026 19:43:15 -0700 Subject: [PATCH 03/10] Test Change-Id: I5b5a2dd86eb12555e74146bf63f461642ceba058 --- .../src/main/resources/webapps/ozoneManager/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/index.html b/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/index.html index 153000c2c917..bd7918bfa5fd 100644 --- a/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/index.html +++ b/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/index.html @@ -49,7 +49,7 @@ Ozone Manager From 5201f65b62b2c1aab15a5a2e92a1c212be27bf60 Mon Sep 17 00:00:00 2001 From: Wei-Chiu Chuang Date: Wed, 1 Apr 2026 15:08:34 -0700 Subject: [PATCH 04/10] unit test for MXBean passed. Change-Id: Ibe1e0c84ec68936f7aa8837d9aea969b2bcc5701 --- .../ozone/om/helpers/SnapshotDiffJob.java | 4 +- .../helpers/TestOmSnapshotDiffJobCodec.java | 3 +- .../hadoop/ozone/om/OmSnapshotManager.java | 2 +- .../om/snapshot/SnapshotDiffManager.java | 1 + .../snapshot/db/SnapshotDiffDBDefinition.java | 2 +- .../webapps/ozoneManager/ozoneManager.js | 3 +- .../TestSnapshotDiffCleanupService.java | 2 +- .../om/snapshot/TestSnapshotDiffManager.java | 2 +- .../TestSnapshotDiffManagerMXBean.java | 150 ++++++++++++++++++ 9 files changed, 161 insertions(+), 8 deletions(-) create mode 100644 hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/snapshot/TestSnapshotDiffManagerMXBean.java diff --git a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/SnapshotDiffJob.java b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/SnapshotDiffJob.java index 1e5da689f4bb..9c6e46164f25 100644 --- a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/SnapshotDiffJob.java +++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/SnapshotDiffJob.java @@ -94,7 +94,9 @@ public SnapshotDiffJob(long creationTime, this.largestEntryKey = largestEntryKey; } - public static Codec getCodec() { + @java.beans.Transient + @com.fasterxml.jackson.annotation.JsonIgnore + public static Codec codec() { return CODEC; } diff --git a/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/helpers/TestOmSnapshotDiffJobCodec.java b/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/helpers/TestOmSnapshotDiffJobCodec.java index b24d225d0cc5..d5dc6d553e71 100644 --- a/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/helpers/TestOmSnapshotDiffJobCodec.java +++ b/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/helpers/TestOmSnapshotDiffJobCodec.java @@ -30,8 +30,7 @@ public class TestOmSnapshotDiffJobCodec { private final OldSnapshotDiffJobCodecForTesting oldCodec = new OldSnapshotDiffJobCodecForTesting(); - private final Codec newCodec = SnapshotDiffJob.getCodec(); - + private final Codec newCodec = SnapshotDiffJob.codec(); @Test public void testOldJsonSerializedDataCanBeReadByNewCodec() throws Exception { // Step 1: Construct a SnapshotDiffJob instance diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmSnapshotManager.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmSnapshotManager.java index 56603a9f207b..1ee53e076fcc 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmSnapshotManager.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmSnapshotManager.java @@ -425,7 +425,7 @@ private static CodecRegistry createCodecRegistryForSnapDiff() { // DiffReportEntry codec for Diff Report. registry.addCodec(SnapshotDiffReportOzone.DiffReportEntry.class, SnapshotDiffReportOzone.getDiffReportEntryCodec()); - registry.addCodec(SnapshotDiffJob.class, SnapshotDiffJob.getCodec()); + registry.addCodec(SnapshotDiffJob.class, SnapshotDiffJob.codec()); return registry.build(); } diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/SnapshotDiffManager.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/SnapshotDiffManager.java index b66c8f4e0f8c..0c685f763da3 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/SnapshotDiffManager.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/SnapshotDiffManager.java @@ -97,6 +97,7 @@ import java.util.stream.Collectors; import org.apache.commons.io.file.PathUtils; import org.apache.commons.lang3.tuple.Pair; +import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.hadoop.hdds.StringUtils; import org.apache.hadoop.hdds.conf.OzoneConfiguration; import org.apache.hadoop.hdds.utils.NativeLibraryNotLoadedException; diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/db/SnapshotDiffDBDefinition.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/db/SnapshotDiffDBDefinition.java index 1add663c7f1c..bb09425a7df6 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/db/SnapshotDiffDBDefinition.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/db/SnapshotDiffDBDefinition.java @@ -51,7 +51,7 @@ public final class SnapshotDiffDBDefinition extends DBDefinition.WithMap { public static final String SNAP_DIFF_JOB_TABLE_NAME = "snap-diff-job-table"; public static final DBColumnFamilyDefinition SNAP_DIFF_JOB_TABLE_DEF - = new DBColumnFamilyDefinition<>(SNAP_DIFF_JOB_TABLE_NAME, StringCodec.get(), SnapshotDiffJob.getCodec()); + = new DBColumnFamilyDefinition<>(SNAP_DIFF_JOB_TABLE_NAME, StringCodec.get(), SnapshotDiffJob.codec()); /** * Global table to keep the diff report. Each key is prefixed by the jobId * to improve look up and clean up. JobId comes from snap-diff-job-table. diff --git a/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/ozoneManager.js b/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/ozoneManager.js index eea3b0a8aef0..e2148d44ffd1 100644 --- a/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/ozoneManager.js +++ b/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/ozoneManager.js @@ -61,12 +61,13 @@ } }); - $http.get("jmx?qry=OzoneManager:name=SnapshotDiffManager") + $http.get("jmx?qry=Hadoop:service=OzoneManager,name=SnapshotDiffManager") .then(function (result) { if (result.data.beans && result.data.beans.length > 0) { ctrl.snapshotDiffJobs = result.data.beans[0].SnapshotDiffJobs; } }); + } }); angular.module('ozoneManager').component('omMetrics', { diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/service/TestSnapshotDiffCleanupService.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/service/TestSnapshotDiffCleanupService.java index 2dc9329601a4..25947fae6454 100644 --- a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/service/TestSnapshotDiffCleanupService.java +++ b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/service/TestSnapshotDiffCleanupService.java @@ -169,7 +169,7 @@ public void init() throws RocksDBException, IOException { // DiffReportEntry codec for Diff Report. b.addCodec(SnapshotDiffReportOzone.DiffReportEntry.class, SnapshotDiffReportOzone.getDiffReportEntryCodec()); - b.addCodec(SnapshotDiffJob.class, SnapshotDiffJob.getCodec()); + b.addCodec(SnapshotDiffJob.class, SnapshotDiffJob.codec()); codecRegistry = b.build(); emptyReportEntry = codecRegistry.asRawData("{}"); diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/snapshot/TestSnapshotDiffManager.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/snapshot/TestSnapshotDiffManager.java index 2879780421f4..4da22a90ed85 100644 --- a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/snapshot/TestSnapshotDiffManager.java +++ b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/snapshot/TestSnapshotDiffManager.java @@ -220,7 +220,7 @@ public class TestSnapshotDiffManager { public static void initCodecRegistry() { codecRegistry = CodecRegistry.newBuilder() .addCodec(DiffReportEntry.class, getDiffReportEntryCodec()) - .addCodec(SnapshotDiffJob.class, SnapshotDiffJob.getCodec()) + .addCodec(SnapshotDiffJob.class, SnapshotDiffJob.codec()) .build(); } diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/snapshot/TestSnapshotDiffManagerMXBean.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/snapshot/TestSnapshotDiffManagerMXBean.java new file mode 100644 index 000000000000..a3e23bfa51d3 --- /dev/null +++ b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/snapshot/TestSnapshotDiffManagerMXBean.java @@ -0,0 +1,150 @@ +/* + * 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.snapshot; + +import org.apache.hadoop.hdds.conf.OzoneConfiguration; +import org.apache.hadoop.hdds.utils.db.CodecRegistry; +import org.apache.hadoop.hdds.utils.db.managed.ManagedColumnFamilyOptions; +import org.apache.hadoop.hdds.utils.db.managed.ManagedDBOptions; +import org.apache.hadoop.hdds.utils.db.managed.ManagedRocksDB; +import org.apache.hadoop.ozone.om.OMMetadataManager; +import org.apache.hadoop.ozone.om.OMMetrics; +import org.apache.hadoop.ozone.om.OmSnapshotManager; +import org.apache.hadoop.ozone.om.OzoneManager; +import org.apache.hadoop.ozone.om.helpers.SnapshotDiffJob; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.rocksdb.ColumnFamilyDescriptor; +import org.rocksdb.ColumnFamilyHandle; +import org.rocksdb.RocksDBException; + +import javax.management.MBeanServer; +import javax.management.ObjectName; +import javax.management.openmbean.CompositeData; +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static org.apache.hadoop.hdds.utils.db.DBStoreBuilder.DEFAULT_COLUMN_FAMILY_NAME; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * Tests for SnapshotDiffManagerMXBean registration. + */ +public class TestSnapshotDiffManagerMXBean { + + private SnapshotDiffManager snapshotDiffManager; + private ManagedRocksDB db; + private PersistentMap snapDiffJobTable; + private PersistentMap snapDiffReportTable; + private MBeanServer mbs; + + @BeforeEach + public void setUp(@TempDir Path tempDir) throws IOException, RocksDBException { + OzoneConfiguration conf = new OzoneConfiguration(); + ManagedDBOptions dbOptions = new ManagedDBOptions(); + dbOptions.setCreateIfMissing(true); + ManagedColumnFamilyOptions columnFamilyOptions = new ManagedColumnFamilyOptions(); + + List columnFamilyDescriptors = + Collections.singletonList(new ColumnFamilyDescriptor( + DEFAULT_COLUMN_FAMILY_NAME.getBytes(), + columnFamilyOptions)); + + List columnFamilyHandles = new ArrayList<>(); + + db = ManagedRocksDB.open(dbOptions, tempDir.toAbsolutePath().toString(), + columnFamilyDescriptors, columnFamilyHandles); + + CodecRegistry codecRegistry = CodecRegistry.newBuilder() + .addCodec(SnapshotDiffJob.class, SnapshotDiffJob.codec()) + .build(); + + ColumnFamilyHandle jobCFH = db.get().createColumnFamily( + new ColumnFamilyDescriptor("jobTable".getBytes(), columnFamilyOptions)); + ColumnFamilyHandle reportCFH = db.get().createColumnFamily( + new ColumnFamilyDescriptor("reportTable".getBytes(), columnFamilyOptions)); + + snapDiffJobTable = new RocksDbPersistentMap<>(db, jobCFH, + codecRegistry, String.class, SnapshotDiffJob.class); + + snapDiffReportTable = new RocksDbPersistentMap<>(db, reportCFH, + codecRegistry, String.class, String.class); + + OzoneManager ozoneManager = mock(OzoneManager.class); + when(ozoneManager.getConfiguration()).thenReturn(conf); + when(ozoneManager.getMetrics()).thenReturn(mock(OMMetrics.class)); + OMMetadataManager omMetadataManager = mock(OMMetadataManager.class); + when(omMetadataManager.getStore()).thenReturn(mock(org.apache.hadoop.hdds.utils.db.RDBStore.class)); + when(ozoneManager.getMetadataManager()).thenReturn(omMetadataManager); + when(ozoneManager.getOmSnapshotManager()).thenReturn(mock(OmSnapshotManager.class)); + + snapshotDiffManager = new SnapshotDiffManager(db, ozoneManager, + jobCFH, reportCFH, columnFamilyOptions, codecRegistry); + + mbs = ManagementFactory.getPlatformMBeanServer(); + } + + @AfterEach + public void tearDown() { + if (snapshotDiffManager != null) { + snapshotDiffManager.close(); + } + if (db != null) { + db.close(); + } + } + + @Test + public void testMXBeanRegistration() throws Exception { + ObjectName name = new ObjectName( + "Hadoop:service=OzoneManager,name=SnapshotDiffManager"); + assertTrue(mbs.isRegistered(name), "SnapshotDiffManager MBean should be registered"); + + // Initially 0 jobs + Object jobsAttr = mbs.getAttribute(name, "SnapshotDiffJobs"); + assertTrue(jobsAttr instanceof CompositeData[]); + assertEquals(0, ((CompositeData[]) jobsAttr).length); + + // Add a job directly to the table + SnapshotDiffJob job = new SnapshotDiffJob(System.currentTimeMillis(), "job-1", + org.apache.hadoop.ozone.snapshot.SnapshotDiffResponse.JobStatus.QUEUED, + "vol", "bucket", "snap1", "snap2", + false, false, 0, + org.apache.hadoop.ozone.snapshot.SnapshotDiffResponse.SubStatus.OBJECT_ID_MAP_GEN_FSO, + 0, null); + snapDiffJobTable.put("job-1", job); + + // Verify MXBean method returns the job via JMX + jobsAttr = mbs.getAttribute(name, "SnapshotDiffJobs"); + assertTrue(jobsAttr instanceof CompositeData[]); + CompositeData[] jobs = (CompositeData[]) jobsAttr; + assertEquals(1, jobs.length); + assertEquals("job-1", jobs[0].get("jobId")); + assertEquals("snap1", jobs[0].get("fromSnapshot")); + assertEquals("snap2", jobs[0].get("toSnapshot")); + } +} From 806c8ccc8737dbc3768e04b91c12cba35818e08f Mon Sep 17 00:00:00 2001 From: Wei-Chiu Chuang Date: Wed, 1 Apr 2026 15:10:40 -0700 Subject: [PATCH 05/10] remove unnecessary annotations Change-Id: I89b3585bbd5e34364a8a4567f81de485b737d69e --- .../org/apache/hadoop/ozone/om/helpers/SnapshotDiffJob.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/SnapshotDiffJob.java b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/SnapshotDiffJob.java index 9c6e46164f25..38836afdf9f8 100644 --- a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/SnapshotDiffJob.java +++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/SnapshotDiffJob.java @@ -94,8 +94,6 @@ public SnapshotDiffJob(long creationTime, this.largestEntryKey = largestEntryKey; } - @java.beans.Transient - @com.fasterxml.jackson.annotation.JsonIgnore public static Codec codec() { return CODEC; } From d90c22401d3b1ec7a03a3a61a215e7388173a4d3 Mon Sep 17 00:00:00 2001 From: Wei-Chiu Chuang Date: Wed, 1 Apr 2026 17:09:31 -0700 Subject: [PATCH 06/10] wip Change-Id: I96660319acfc3b135bd58e592e9d3ff8ebd4a05c --- .../webapps/static/templates/menu.html | 2 +- .../resources/webapps/scm/scm-overview.html | 2 +- .../helpers/TestOmSnapshotDiffJobCodec.java | 1 + .../resources/webapps/ozoneManager/index.html | 4 +- .../webapps/ozoneManager/om-snapshots.html | 111 ++++++++++++++---- .../webapps/ozoneManager/ozoneManager.js | 70 +++++++++-- 6 files changed, 153 insertions(+), 37 deletions(-) diff --git a/hadoop-hdds/framework/src/main/resources/webapps/static/templates/menu.html b/hadoop-hdds/framework/src/main/resources/webapps/static/templates/menu.html index 920d2ac606d6..81f92e0d54df 100644 --- a/hadoop-hdds/framework/src/main/resources/webapps/static/templates/menu.html +++ b/hadoop-hdds/framework/src/main/resources/webapps/static/templates/menu.html @@ -58,7 +58,7 @@
  • IO Status
  • Data Scanner
  • -
  • Snapshots
  • +
  • Ozone Snapshot
  • diff --git a/hadoop-hdds/server-scm/src/main/resources/webapps/scm/scm-overview.html b/hadoop-hdds/server-scm/src/main/resources/webapps/scm/scm-overview.html index 7bfe405850e2..bb2f25a1c325 100644 --- a/hadoop-hdds/server-scm/src/main/resources/webapps/scm/scm-overview.html +++ b/hadoop-hdds/server-scm/src/main/resources/webapps/scm/scm-overview.html @@ -391,4 +391,4 @@

    Safemode rules statuses

    {{typestat.value[1]}} - \ No newline at end of file + diff --git a/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/helpers/TestOmSnapshotDiffJobCodec.java b/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/helpers/TestOmSnapshotDiffJobCodec.java index d5dc6d553e71..d22be5f9090e 100644 --- a/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/helpers/TestOmSnapshotDiffJobCodec.java +++ b/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/helpers/TestOmSnapshotDiffJobCodec.java @@ -31,6 +31,7 @@ public class TestOmSnapshotDiffJobCodec { private final OldSnapshotDiffJobCodecForTesting oldCodec = new OldSnapshotDiffJobCodecForTesting(); private final Codec newCodec = SnapshotDiffJob.codec(); + @Test public void testOldJsonSerializedDataCanBeReadByNewCodec() throws Exception { // Step 1: Construct a SnapshotDiffJob instance diff --git a/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/index.html b/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/index.html index bd7918bfa5fd..54accf457f34 100644 --- a/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/index.html +++ b/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/index.html @@ -49,9 +49,7 @@ Ozone Manager + metrics="{ 'OM metrics' : '#!/metrics/ozoneManager', 'Rpc metrics' : '#!/metrics/rpc'}"> diff --git a/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/om-snapshots.html b/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/om-snapshots.html index f7adf69c311b..659f63be79de 100644 --- a/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/om-snapshots.html +++ b/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/om-snapshots.html @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -

    Snapshot Metrics

    +

    Snapshot Internal Metrics

    @@ -33,31 +33,102 @@

    Snapshot Metrics

    Snapshot Diff Jobs

    +
    +
    + + +
    +
    + +
    +
    +
    - - - - - - - - - + + + + + + + + + - - - - - - - - - - + + + + + + + + + +
    Job IDStatusVolumeBucketFrom SnapshotTo SnapshotProgressTotal Diff EntriesCreation Time + Job ID + + Status + + Volume + + Bucket + + From Snapshot + + To Snapshot + + Progress + + Total Diff Entries + + Creation Time +
    {{job.JobId}}{{job.Status}}{{job.Volume}}{{job.Bucket}}{{job.FromSnapshot}}{{job.ToSnapshot}}{{job.KeysProcessedPct * 100 | number:2}}%{{job.TotalDiffEntries}}{{job.CreationTime | date:'yyyy-MM-dd HH:mm:ss'}}
    {{job.jobId}} + {{job.status}} +
    + Reason: {{job.reason}} +
    +
    {{job.volume}}{{job.bucket}}{{job.fromSnapshot}}{{job.toSnapshot}} + Completed + {{job.keysProcessedPct | number:2}}% + {{job.totalDiffEntries}}{{job.creationTime | date:'yyyy-MM-dd HH:mm:ss'}}
    + +
    +
    + + + of {{lastIndex}}. + + Showing {{getCurrentPageFirstItemIndex()}} to {{getCurrentPageLastItemIndex()}} of the total {{totalItems}} entries. + +
    +
    + +
    +
    diff --git a/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/ozoneManager.js b/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/ozoneManager.js index e2148d44ffd1..20824105a57d 100644 --- a/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/ozoneManager.js +++ b/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/ozoneManager.js @@ -34,20 +34,16 @@ }); angular.module('ozoneManager').component('omSnapshots', { templateUrl: 'om-snapshots.html', - controller: function ($http) { + controller: function ($http, $scope) { var ctrl = this; ctrl.snapshotMetrics = []; ctrl.snapshotDiffJobs = []; - - $http.get("jmx?qry=Hadoop:service=OzoneManager,name=OMMetrics") - .then(function (result) { - var metrics = result.data.beans[0]; - for (var key in metrics) { - if (key.match(/NumSnapshot|NumCancelSnapshotDiff|NumListSnapshotDiffJob/)) { - ctrl.snapshotMetrics.push({key: key, value: metrics[key]}); - } - } - }); + $scope.reverse = false; + $scope.columnName = "jobId"; + let snapDiffJobsCopy = []; + $scope.RecordsToDisplay = "10"; + $scope.currentPage = 1; + $scope.lastIndex = 0; $http.get("jmx?qry=Hadoop:service=OzoneManager,name=OmSnapshotInternalMetrics") .then(function (result) { @@ -64,10 +60,60 @@ $http.get("jmx?qry=Hadoop:service=OzoneManager,name=SnapshotDiffManager") .then(function (result) { if (result.data.beans && result.data.beans.length > 0) { - ctrl.snapshotDiffJobs = result.data.beans[0].SnapshotDiffJobs; + snapDiffJobsCopy = result.data.beans[0].SnapshotDiffJobs; + $scope.totalItems = snapDiffJobsCopy.length; + $scope.lastIndex = Math.ceil(snapDiffJobsCopy.length / $scope.RecordsToDisplay); + ctrl.snapshotDiffJobs = snapDiffJobsCopy.slice(0, $scope.RecordsToDisplay); } }); + /*if option is 'All' display all records else display specified record on page*/ + $scope.UpdateRecordsToShow = () => { + if($scope.RecordsToDisplay == 'All') { + $scope.lastIndex = 1; + ctrl.snapshotDiffJobs = snapDiffJobsCopy; + } else { + $scope.lastIndex = Math.ceil(snapDiffJobsCopy.length / $scope.RecordsToDisplay); + ctrl.snapshotDiffJobs = snapDiffJobsCopy.slice(0, $scope.RecordsToDisplay); + } + $scope.currentPage = 1; + } + /* Page Slicing logic */ + $scope.handlePagination = (pageIndex, isDisabled) => { + if(!isDisabled && $scope.RecordsToDisplay != 'All') { + pageIndex = parseInt(pageIndex); + let startIndex = 0, endIndex = 0; + $scope.currentPage = pageIndex; + startIndex = ($scope.currentPage - 1) * parseInt($scope.RecordsToDisplay); + endIndex = startIndex + parseInt($scope.RecordsToDisplay); + ctrl.snapshotDiffJobs = snapDiffJobsCopy.slice(startIndex, endIndex); + } + } + /*column sort logic*/ + $scope.columnSort = (colName) => { + $scope.columnName = colName; + $scope.reverse = !$scope.reverse; + } + /*show page*/ + $scope.getPagesArray = function () { + return Array.from({ length: $scope.lastIndex }, (_, index) => index + 1); + }; + /*show last item index*/ + $scope.getCurrentPageLastItemIndex = () => { + if ($scope.RecordsToDisplay == 'All') { + return $scope.totalItems; + } + + let endIndex = $scope.currentPage * parseInt($scope.RecordsToDisplay); + return Math.min(endIndex, $scope.totalItems); + } + /*show first item index*/ + $scope.getCurrentPageFirstItemIndex = () => { + if ($scope.RecordsToDisplay == 'All') { + return 1; + } + return ($scope.currentPage - 1) * $scope.RecordsToDisplay + 1; + } } }); angular.module('ozoneManager').component('omMetrics', { From 238e2875de6e3fcedbfe71b87f35d606ccfefa8f Mon Sep 17 00:00:00 2001 From: Wei-Chiu Chuang Date: Wed, 1 Apr 2026 17:17:07 -0700 Subject: [PATCH 07/10] All done. Change-Id: I7e742e806d493ecca8b543f6ad19266b10247cd7 --- .../webapps/ozoneManager/om-snapshots.html | 39 +++++++++++-------- .../TestSnapshotDiffManagerMXBean.java | 1 + 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/om-snapshots.html b/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/om-snapshots.html index 659f63be79de..7cc9b0020bb8 100644 --- a/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/om-snapshots.html +++ b/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/om-snapshots.html @@ -14,23 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. --> -

    Snapshot Internal Metrics

    - - - - - - - - - - - - - - -
    Metric NameValue
    {{metric.key}}{{metric.value}}
    -

    Snapshot Diff Jobs

    @@ -88,6 +71,9 @@

    Snapshot Diff Jobs

    Reason: {{job.reason}}
    +
    + Sub-status: {{job.subStatus}} +
    {{job.volume}} {{job.bucket}} @@ -132,3 +118,22 @@

    Snapshot Diff Jobs

    + +
    + +

    Snapshot Internal Metrics

    + + + + + + + + + + + + + + +
    Metric NameValue
    {{metric.key}}{{metric.value}}
    diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/snapshot/TestSnapshotDiffManagerMXBean.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/snapshot/TestSnapshotDiffManagerMXBean.java index a3e23bfa51d3..d4e587f13829 100644 --- a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/snapshot/TestSnapshotDiffManagerMXBean.java +++ b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/snapshot/TestSnapshotDiffManagerMXBean.java @@ -146,5 +146,6 @@ public void testMXBeanRegistration() throws Exception { assertEquals("job-1", jobs[0].get("jobId")); assertEquals("snap1", jobs[0].get("fromSnapshot")); assertEquals("snap2", jobs[0].get("toSnapshot")); + assertEquals("OBJECT_ID_MAP_GEN_FSO", jobs[0].get("subStatus")); } } From bcea8b44cc22d0704db6ae5b3cd3d1e8e72a2cd2 Mon Sep 17 00:00:00 2001 From: Wei-Chiu Chuang Date: Wed, 1 Apr 2026 17:54:08 -0700 Subject: [PATCH 08/10] Added Usage Statis section Change-Id: I9f5b5040fe8835c7cc7aae98732368b12a053961 --- .../resources/webapps/ozoneManager/index.html | 4 ++- .../webapps/ozoneManager/om-snapshots.html | 26 +++++++++++++++++++ .../webapps/ozoneManager/ozoneManager.js | 15 +++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/index.html b/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/index.html index 54accf457f34..153000c2c917 100644 --- a/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/index.html +++ b/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/index.html @@ -49,7 +49,9 @@ Ozone Manager + metrics="{ 'OM metrics' : '#!/metrics/ozoneManager', 'Rpc metrics' : '#!/metrics/rpc'}" + snapshot="true" + snapshot-link-href="#!/snapshots"> diff --git a/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/om-snapshots.html b/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/om-snapshots.html index 7cc9b0020bb8..6884f5a04c1f 100644 --- a/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/om-snapshots.html +++ b/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/om-snapshots.html @@ -14,6 +14,32 @@ See the License for the specific language governing permissions and limitations under the License. --> +

    Usage Statistics

    + + + + + + + + + + + + + + + + + + + + + +
    MetricValue
    Number of Active Snapshots{{$ctrl.snapshotUsageMetrics.NumSnapshotActive}}
    Number of Deleted Snapshots{{$ctrl.snapshotUsageMetrics.NumSnapshotDeleted}}
    Snapshot Cache Size{{$ctrl.snapshotUsageMetrics.NumSnapshotCacheSize}}
    + +
    +

    Snapshot Diff Jobs

    diff --git a/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/ozoneManager.js b/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/ozoneManager.js index 20824105a57d..e98d3f7ba3a7 100644 --- a/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/ozoneManager.js +++ b/hadoop-ozone/ozone-manager/src/main/resources/webapps/ozoneManager/ozoneManager.js @@ -38,6 +38,11 @@ var ctrl = this; ctrl.snapshotMetrics = []; ctrl.snapshotDiffJobs = []; + ctrl.snapshotUsageMetrics = { + 'NumSnapshotActive': 0, + 'NumSnapshotDeleted': 0, + 'NumSnapshotCacheSize': 0 + }; $scope.reverse = false; $scope.columnName = "jobId"; let snapDiffJobsCopy = []; @@ -45,6 +50,16 @@ $scope.currentPage = 1; $scope.lastIndex = 0; + $http.get("jmx?qry=Hadoop:service=OzoneManager,name=OMMetrics") + .then(function (result) { + if (result.data.beans && result.data.beans.length > 0) { + var metrics = result.data.beans[0]; + ctrl.snapshotUsageMetrics.NumSnapshotActive = metrics.NumSnapshotActive || 0; + ctrl.snapshotUsageMetrics.NumSnapshotDeleted = metrics.NumSnapshotDeleted || 0; + ctrl.snapshotUsageMetrics.NumSnapshotCacheSize = metrics.NumSnapshotCacheSize || 0; + } + }); + $http.get("jmx?qry=Hadoop:service=OzoneManager,name=OmSnapshotInternalMetrics") .then(function (result) { if (result.data.beans && result.data.beans.length > 0) { From 07f39ad0804883674037c995ec83b7e09589f6eb Mon Sep 17 00:00:00 2001 From: Wei-Chiu Chuang Date: Wed, 1 Apr 2026 18:12:23 -0700 Subject: [PATCH 09/10] Fixed checkstyle, findbugs Change-Id: I4a2c082ddacd1868934179b4ec47beb64e046417 --- .../webapps/static/templates/menu.html | 5 +-- .../ozone/om/helpers/SnapshotDiffJob.java | 4 +- .../om/snapshot/SnapshotDiffManager.java | 7 ++- .../snapshot/SnapshotDiffManagerMXBean.java | 3 +- .../TestSnapshotDiffManagerMXBean.java | 44 +++++++++---------- 5 files changed, 29 insertions(+), 34 deletions(-) diff --git a/hadoop-hdds/framework/src/main/resources/webapps/static/templates/menu.html b/hadoop-hdds/framework/src/main/resources/webapps/static/templates/menu.html index 81f92e0d54df..1963a6543835 100644 --- a/hadoop-hdds/framework/src/main/resources/webapps/static/templates/menu.html +++ b/hadoop-hdds/framework/src/main/resources/webapps/static/templates/menu.html @@ -59,6 +59,5 @@
  • IO Status
  • Data Scanner
  • Ozone Snapshot
  • - -
    - + + diff --git a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/SnapshotDiffJob.java b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/SnapshotDiffJob.java index 38836afdf9f8..714a54cbd9b2 100644 --- a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/SnapshotDiffJob.java +++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/SnapshotDiffJob.java @@ -228,10 +228,10 @@ public String toString() { sb.append(", reason: ").append(reason); } if (status.equals(JobStatus.IN_PROGRESS) && subStatus != null) { - sb.append(", subStatus: ").append(status); + sb.append(", subStatus: ").append(subStatus); if (subStatus.equals(SubStatus.OBJECT_ID_MAP_GEN_FSO) || subStatus.equals(SubStatus.OBJECT_ID_MAP_GEN_OBS)) { - sb.append(String.format(", keysProcessedPercent: %.2f", keysProcessedPct)); + sb.append(String.format(", keysProcessedPct: %.2f", keysProcessedPct)); } } return sb.toString(); diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/SnapshotDiffManager.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/SnapshotDiffManager.java index 0c685f763da3..c1c61e74226c 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/SnapshotDiffManager.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/SnapshotDiffManager.java @@ -95,10 +95,12 @@ import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.stream.Collectors; +import javax.management.ObjectName; import org.apache.commons.io.file.PathUtils; import org.apache.commons.lang3.tuple.Pair; -import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.hadoop.hdds.HddsUtils; import org.apache.hadoop.hdds.StringUtils; +import org.apache.hadoop.hdds.annotation.InterfaceAudience; import org.apache.hadoop.hdds.conf.OzoneConfiguration; import org.apache.hadoop.hdds.utils.NativeLibraryNotLoadedException; import org.apache.hadoop.hdds.utils.db.CodecRegistry; @@ -108,9 +110,6 @@ import org.apache.hadoop.hdds.utils.db.StringCodec; import org.apache.hadoop.hdds.utils.db.Table; import org.apache.hadoop.hdds.utils.db.TablePrefixInfo; -import javax.management.ObjectName; -import org.apache.hadoop.hdds.HddsUtils; -import org.apache.hadoop.hdds.annotation.InterfaceAudience; import org.apache.hadoop.hdds.utils.db.managed.ManagedColumnFamilyOptions; import org.apache.hadoop.hdds.utils.db.managed.ManagedRocksDB; import org.apache.hadoop.hdfs.protocol.SnapshotDiffReport.DiffReportEntry; diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/SnapshotDiffManagerMXBean.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/SnapshotDiffManagerMXBean.java index 62626500ef4d..c46689e3cb9c 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/SnapshotDiffManagerMXBean.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/SnapshotDiffManagerMXBean.java @@ -17,11 +17,10 @@ package org.apache.hadoop.ozone.om.snapshot; +import java.util.List; import org.apache.hadoop.hdds.annotation.InterfaceAudience; import org.apache.hadoop.ozone.om.helpers.SnapshotDiffJob; -import java.util.List; - /** * JMX interface for SnapshotDiffManager. */ diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/snapshot/TestSnapshotDiffManagerMXBean.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/snapshot/TestSnapshotDiffManagerMXBean.java index d4e587f13829..a4913acaf014 100644 --- a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/snapshot/TestSnapshotDiffManagerMXBean.java +++ b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/snapshot/TestSnapshotDiffManagerMXBean.java @@ -17,6 +17,22 @@ package org.apache.hadoop.ozone.om.snapshot; +import static org.apache.hadoop.hdds.utils.db.DBStoreBuilder.DEFAULT_COLUMN_FAMILY_NAME; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import javax.management.MBeanServer; +import javax.management.ObjectName; +import javax.management.openmbean.CompositeData; import org.apache.hadoop.hdds.conf.OzoneConfiguration; import org.apache.hadoop.hdds.utils.db.CodecRegistry; import org.apache.hadoop.hdds.utils.db.managed.ManagedColumnFamilyOptions; @@ -35,22 +51,6 @@ import org.rocksdb.ColumnFamilyHandle; import org.rocksdb.RocksDBException; -import javax.management.MBeanServer; -import javax.management.ObjectName; -import javax.management.openmbean.CompositeData; -import java.io.IOException; -import java.lang.management.ManagementFactory; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import static org.apache.hadoop.hdds.utils.db.DBStoreBuilder.DEFAULT_COLUMN_FAMILY_NAME; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - /** * Tests for SnapshotDiffManagerMXBean registration. */ @@ -59,7 +59,6 @@ public class TestSnapshotDiffManagerMXBean { private SnapshotDiffManager snapshotDiffManager; private ManagedRocksDB db; private PersistentMap snapDiffJobTable; - private PersistentMap snapDiffReportTable; private MBeanServer mbs; @BeforeEach @@ -71,7 +70,7 @@ public void setUp(@TempDir Path tempDir) throws IOException, RocksDBException { List columnFamilyDescriptors = Collections.singletonList(new ColumnFamilyDescriptor( - DEFAULT_COLUMN_FAMILY_NAME.getBytes(), + DEFAULT_COLUMN_FAMILY_NAME.getBytes(StandardCharsets.UTF_8), columnFamilyOptions)); List columnFamilyHandles = new ArrayList<>(); @@ -84,16 +83,15 @@ public void setUp(@TempDir Path tempDir) throws IOException, RocksDBException { .build(); ColumnFamilyHandle jobCFH = db.get().createColumnFamily( - new ColumnFamilyDescriptor("jobTable".getBytes(), columnFamilyOptions)); + new ColumnFamilyDescriptor("jobTable".getBytes(StandardCharsets.UTF_8), + columnFamilyOptions)); ColumnFamilyHandle reportCFH = db.get().createColumnFamily( - new ColumnFamilyDescriptor("reportTable".getBytes(), columnFamilyOptions)); + new ColumnFamilyDescriptor("reportTable".getBytes(StandardCharsets.UTF_8), + columnFamilyOptions)); snapDiffJobTable = new RocksDbPersistentMap<>(db, jobCFH, codecRegistry, String.class, SnapshotDiffJob.class); - snapDiffReportTable = new RocksDbPersistentMap<>(db, reportCFH, - codecRegistry, String.class, String.class); - OzoneManager ozoneManager = mock(OzoneManager.class); when(ozoneManager.getConfiguration()).thenReturn(conf); when(ozoneManager.getMetrics()).thenReturn(mock(OMMetrics.class)); From d79cc2f9fbae75b725fc8a8ae4ca502aef097f8b Mon Sep 17 00:00:00 2001 From: Wei-Chiu Chuang Date: Wed, 1 Apr 2026 20:10:07 -0700 Subject: [PATCH 10/10] Fix test Change-Id: If87353d4cdc540b271c183f339dd704e42cb8b46 --- .../ozone/om/snapshot/TestSnapshotDiffManagerMXBean.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/snapshot/TestSnapshotDiffManagerMXBean.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/snapshot/TestSnapshotDiffManagerMXBean.java index a4913acaf014..b9e3419f53c7 100644 --- a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/snapshot/TestSnapshotDiffManagerMXBean.java +++ b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/snapshot/TestSnapshotDiffManagerMXBean.java @@ -96,7 +96,11 @@ public void setUp(@TempDir Path tempDir) throws IOException, RocksDBException { when(ozoneManager.getConfiguration()).thenReturn(conf); when(ozoneManager.getMetrics()).thenReturn(mock(OMMetrics.class)); OMMetadataManager omMetadataManager = mock(OMMetadataManager.class); - when(omMetadataManager.getStore()).thenReturn(mock(org.apache.hadoop.hdds.utils.db.RDBStore.class)); + org.apache.hadoop.hdds.utils.db.RDBStore rdbStore = + mock(org.apache.hadoop.hdds.utils.db.RDBStore.class); + when(rdbStore.getSnapshotMetadataDir()) + .thenReturn(tempDir.toAbsolutePath().toString()); + when(omMetadataManager.getStore()).thenReturn(rdbStore); when(ozoneManager.getMetadataManager()).thenReturn(omMetadataManager); when(ozoneManager.getOmSnapshotManager()).thenReturn(mock(OmSnapshotManager.class));