Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,14 @@
import java.io.IOException;
import java.math.BigInteger;
import java.security.cert.X509Certificate;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

import com.google.gson.Gson;
import com.google.protobuf.ByteString;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.hdds.protocol.proto.StorageContainerDatanodeProtocolProtos.DeletedBlocksTransaction;
Expand All @@ -36,6 +43,7 @@
import org.apache.hadoop.hdds.security.x509.certificate.authority.CertificateStore;
import org.apache.hadoop.hdds.security.x509.crl.CRLInfo;
import org.apache.hadoop.hdds.utils.db.BatchOperationHandler;
import org.apache.hadoop.hdds.utils.db.DBColumnFamilyDefinition;
import org.apache.hadoop.hdds.utils.db.DBStore;
import org.apache.hadoop.hdds.utils.db.DBStoreBuilder;
import org.apache.hadoop.hdds.utils.db.Table;
Expand Down Expand Up @@ -67,6 +75,23 @@
*/
public class SCMMetadataStoreImpl implements SCMMetadataStore {

public static final Set<DBColumnFamilyDefinition<?, ?>> COLUMN_FAMILIES =
new HashSet<>(Arrays.asList(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this extra map containing all the tables? They are already stored in the tableMap, so we can iterate it to list out all the tables.

My concern is that if someone later adds a new table, they can easily miss adding it to this map, and then the metric will not automatically appear.

If we drive it off the tableMap, then there is nothing special to do around metrics when adding a new table.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it might be necessary, when I use the tableMap from the StoreImpl, the metrics class will invoke NullPointerException.

DELETED_BLOCKS,
VALID_CERTS,
VALID_SCM_CERTS,
REVOKED_CERTS,
REVOKED_CERTS_V2,
CONTAINERS,
PIPELINES,
TRANSACTIONINFO,
CRLS,
SEQUENCE_ID,
MOVE,
META,
STATEFUL_SERVICE_CONFIG
));

private Table<Long, DeletedBlocksTransaction> deletedBlocksTable;

private Table<BigInteger, X509Certificate> validCertsTable;
Expand Down Expand Up @@ -100,6 +125,9 @@ public class SCMMetadataStoreImpl implements SCMMetadataStore {
private DBStore store;
private final OzoneConfiguration configuration;

private SCMMetadataStoreMetrics metrics;
private Map<String, Table<?, ?>> tableMap = new ConcurrentHashMap<>();

/**
* Constructs the metadata store and starts the DB Services.
*
Expand Down Expand Up @@ -139,57 +167,62 @@ public void start(OzoneConfiguration config)
deletedBlocksTable =
DELETED_BLOCKS.getTable(this.store);

checkTableStatus(deletedBlocksTable,
DELETED_BLOCKS.getName());
checkAndPopulateTable(deletedBlocksTable, DELETED_BLOCKS.getName());

validCertsTable = VALID_CERTS.getTable(store);

checkTableStatus(validCertsTable, VALID_CERTS.getName());
checkAndPopulateTable(validCertsTable, VALID_CERTS.getName());

validSCMCertsTable = VALID_SCM_CERTS.getTable(store);

checkTableStatus(validSCMCertsTable, VALID_SCM_CERTS.getName());
checkAndPopulateTable(validSCMCertsTable, VALID_SCM_CERTS.getName());

revokedCertsTable = REVOKED_CERTS.getTable(store);

checkTableStatus(revokedCertsTable, REVOKED_CERTS.getName());
checkAndPopulateTable(revokedCertsTable, REVOKED_CERTS.getName());

revokedCertsV2Table = REVOKED_CERTS_V2.getTable(store);

checkTableStatus(revokedCertsV2Table, REVOKED_CERTS_V2.getName());
checkAndPopulateTable(revokedCertsV2Table, REVOKED_CERTS_V2.getName());

pipelineTable = PIPELINES.getTable(store);

checkTableStatus(pipelineTable, PIPELINES.getName());
checkAndPopulateTable(pipelineTable, PIPELINES.getName());

containerTable = CONTAINERS.getTable(store);

checkTableStatus(containerTable, CONTAINERS.getName());
checkAndPopulateTable(containerTable, CONTAINERS.getName());

transactionInfoTable = TRANSACTIONINFO.getTable(store);

checkTableStatus(transactionInfoTable, TRANSACTIONINFO.getName());
checkAndPopulateTable(transactionInfoTable, TRANSACTIONINFO.getName());

crlInfoTable = CRLS.getTable(store);

checkAndPopulateTable(crlInfoTable, CRLS.getName());

crlSequenceIdTable = CRL_SEQUENCE_ID.getTable(store);

checkAndPopulateTable(crlInfoTable, CRL_SEQUENCE_ID.getName());

sequenceIdTable = SEQUENCE_ID.getTable(store);

checkTableStatus(sequenceIdTable, SEQUENCE_ID.getName());
checkAndPopulateTable(sequenceIdTable, SEQUENCE_ID.getName());

moveTable = MOVE.getTable(store);

checkTableStatus(moveTable, MOVE.getName());
checkAndPopulateTable(moveTable, MOVE.getName());

metaTable = META.getTable(store);

checkTableStatus(moveTable, META.getName());
checkAndPopulateTable(moveTable, META.getName());

statefulServiceConfigTable = STATEFUL_SERVICE_CONFIG.getTable(store);

checkTableStatus(statefulServiceConfigTable,
checkAndPopulateTable(statefulServiceConfigTable,
STATEFUL_SERVICE_CONFIG.getName());

metrics = SCMMetadataStoreMetrics.create(this);
}
}

Expand All @@ -199,6 +232,10 @@ public void stop() throws Exception {
store.close();
store = null;
}
if (metrics != null) {
metrics.unRegister();
metrics = null;
}
}

@Override
Expand Down Expand Up @@ -304,7 +341,8 @@ public Table<String, ByteString> getStatefulServiceConfigTable() {
return statefulServiceConfigTable;
}

private void checkTableStatus(Table table, String name) throws IOException {
private void checkAndPopulateTable(Table table, String name)
throws IOException {
String logMessage = "Unable to get a reference to %s table. Cannot " +
"continue.";
String errMsg = "Inconsistent DB state, Table - %s. Please check the" +
Expand All @@ -313,6 +351,26 @@ private void checkTableStatus(Table table, String name) throws IOException {
LOG.error(String.format(logMessage, name));
throw new IOException(String.format(errMsg, name));
}
tableMap.put(name, table);
}

public String getEstimatedKeyCountStr() {
Gson gson = new Gson();
return gson.toJson(tableMap.entrySet().stream().map(e -> {
try {
return e.getKey() + " : " + e.getValue().getEstimatedKeyCount();
} catch (IOException ex) {
ex.printStackTrace();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we send this to the logger, rather than stderr?

}
return "N/A";
}).collect(Collectors.toList()));
}

Map<String, Table<?, ?>> getTableMap() {
return tableMap;
}

SCMMetadataStoreMetrics getMetrics() {
return metrics;
}
}
Original file line number Diff line number Diff line change
@@ -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
* <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.scm.metadata;

import org.apache.commons.text.WordUtils;
import org.apache.hadoop.hdds.utils.db.DBColumnFamilyDefinition;
import org.apache.hadoop.metrics2.MetricsCollector;
import org.apache.hadoop.metrics2.MetricsInfo;
import org.apache.hadoop.metrics2.MetricsRecordBuilder;
import org.apache.hadoop.metrics2.MetricsSource;
import org.apache.hadoop.metrics2.annotation.Metrics;
import org.apache.hadoop.metrics2.lib.DefaultMetricsSystem;
import org.apache.hadoop.metrics2.lib.Interns;
import org.apache.hadoop.metrics2.lib.MetricsRegistry;
import org.apache.hadoop.ozone.OzoneConsts;

import java.io.IOException;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;

/**
* Class contains metrics related to SCM Metadata Store.
*/
@Metrics(about = "SCM Metadata Store Metrics", context = OzoneConsts.OZONE)
public final class SCMMetadataStoreMetrics implements MetricsSource {

public static final String METRICS_SOURCE_NAME =
SCMMetadataStoreMetrics.class.getSimpleName();

private static final MetricsInfo ESTIMATED_KEY_COUNT = Interns.info(
"EstimatedKeyCount",
"Tracked estimated key count of all column families");

private static final Map<String, MetricsInfo> ESTIMATED_KEY_COUNT_METRICS
= Collections.unmodifiableMap(new LinkedHashMap<String, MetricsInfo>() {{
for (DBColumnFamilyDefinition<?, ?> table:
SCMMetadataStoreImpl.COLUMN_FAMILIES) {
String name = WordUtils.capitalize(table.getName());
String metric = name + "EstimatedKeyCount";
String description = "Estimated key count in table of " + name;
put(table.getName(), Interns.info(metric, description));
}
}});

private MetricsRegistry registry;

private SCMMetadataStoreImpl scmMetadataStore;

public SCMMetadataStoreMetrics(SCMMetadataStoreImpl scmMetadataStoreImpl) {
this.registry = new MetricsRegistry(METRICS_SOURCE_NAME);
this.scmMetadataStore = scmMetadataStoreImpl;
}

public static SCMMetadataStoreMetrics create(SCMMetadataStoreImpl
scmMetadataStore) {
return DefaultMetricsSystem.instance().register(METRICS_SOURCE_NAME,
"SCM Metadata store related metrics",
new SCMMetadataStoreMetrics(scmMetadataStore));
}

@Override
public void getMetrics(MetricsCollector collector, boolean all) {
MetricsRecordBuilder builder = collector.addRecord(METRICS_SOURCE_NAME)
.tag(ESTIMATED_KEY_COUNT, scmMetadataStore.getEstimatedKeyCountStr());

for (Map.Entry<String, MetricsInfo> e :
ESTIMATED_KEY_COUNT_METRICS.entrySet()) {
long count = 0L;
try {
count = scmMetadataStore.getTableMap().get(e.getKey())
.getEstimatedKeyCount();
} catch (IOException exception) {
// Ignore exception here.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably log exceptions here rather than totally ignore them.

}
builder.addGauge(e.getValue(), count);
}


}

public void unRegister() {
DefaultMetricsSystem.instance().unregisterSource(METRICS_SOURCE_NAME);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/**
* 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.scm.metadata;

import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.ozone.container.common.SCMTestUtils;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

import java.io.IOException;
import java.nio.file.Path;

import static org.apache.hadoop.test.MetricsAsserts.getLongGauge;
import static org.apache.hadoop.test.MetricsAsserts.getMetrics;


/**
* Testing of SCMMetadataStoreImpl.
*/
public class TestSCMMetadataStoreImpl {
private OzoneConfiguration conf;
private SCMMetadataStore scmMetadataStore;

@BeforeEach
public void setUp(@TempDir Path tempDir) throws Exception {
conf = SCMTestUtils.getConf();

scmMetadataStore = new SCMMetadataStoreImpl(conf);
scmMetadataStore.start(conf);
}

@Test
public void testEstimatedKeyCount() {
Assertions.assertTrue(((SCMMetadataStoreImpl) scmMetadataStore)
.getEstimatedKeyCountStr().contains("sequenceId : 0"));
Assertions.assertEquals(0, getGauge("SequenceIdEstimatedKeyCount"));

try {
scmMetadataStore.getSequenceIdTable().put("TestKey", 1L);
} catch (IOException e) {
// Ignore
}

Assertions.assertFalse(((SCMMetadataStoreImpl) scmMetadataStore)
.getEstimatedKeyCountStr().contains("sequenceId : 0"));
Assertions.assertEquals(1, getGauge("SequenceIdEstimatedKeyCount"));
Comment thread
sodonnel marked this conversation as resolved.
}

private long getGauge(String metricName) {
return getLongGauge(metricName,
getMetrics(SCMMetadataStoreMetrics.METRICS_SOURCE_NAME));
}
}