Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AMLII-1148 - Adding Unit test to check GC metrics emitted by each type of Java garbage collector #500

Merged
merged 24 commits into from
Dec 22, 2023
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
ba48c6d
Added ability to change the final Docker image for misbehaving-jmx-se…
carlosroman Dec 14, 2023
e352022
wip, adding GC tests
carlosroman Dec 14, 2023
96a45bc
wip: Second test that checks with JMXFetch for sanity
carlosroman Dec 14, 2023
dc55bbb
wip: Added working Old GC test
carlosroman Dec 14, 2023
f9a8680
Added option to pass JAVA_OPTS and image to use to MisbehavingJMXServer
carlosroman Dec 15, 2023
a7fdff8
Added test for default new JVM metrics
carlosroman Dec 15, 2023
22a91de
Added UseParallelGC test using new_gc_metrics: true#
carlosroman Dec 15, 2023
ed430f9
Added UseG1GC test using new_gc_metrics: true
carlosroman Dec 15, 2023
d006fd0
Added UseZGC test using new_gc_metrics: true
carlosroman Dec 15, 2023
50c6041
Adding missing change to MisbehavingJMXServer.java
carlosroman Dec 18, 2023
a1585b0
Adding a working ZGC test
carlosroman Dec 18, 2023
5ee4f54
Fixed broken test
carlosroman Dec 18, 2023
57b4d53
Fixed it so Misbehaving server runs with correct GC now
carlosroman Dec 19, 2023
bcea1a3
Small refactor
carlosroman Dec 20, 2023
fc3a762
Simple tidy up
carlosroman Dec 20, 2023
4224dc7
Small refactor to use main metrics assert function
carlosroman Dec 20, 2023
156f305
Refactor to tidy up some metrics asserts
carlosroman Dec 20, 2023
90835f9
Got TestReconnectContainer to use helper functions
carlosroman Dec 20, 2023
2484b72
Refactor + adding UseConcMarkSweepGC test
carlosroman Dec 20, 2023
5ca1719
Small refactor to remove some magic strings
carlosroman Dec 21, 2023
e8a8007
Updated docs and comments
carlosroman Dec 21, 2023
229b018
Update tools/misbehaving-jmx-server/README.md
carlosroman Dec 21, 2023
a902ae1
Updated Dockerfile comments
carlosroman Dec 21, 2023
87a8750
Updated comments and added correct TODOs
carlosroman Dec 22, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,13 @@
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<version>1.18.0</version>
<version>1.19.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
</dependencies>
Expand Down
1 change: 0 additions & 1 deletion src/main/java/org/datadog/jmxfetch/Instance.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import java.io.InputStream;
import java.lang.management.ManagementFactory;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
Expand Down
58 changes: 6 additions & 52 deletions src/test/java/org/datadog/jmxfetch/TestCommon.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
package org.datadog.jmxfetch;

import static org.hamcrest.CoreMatchers.hasItem;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
Expand All @@ -19,24 +15,24 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.management.InstanceAlreadyExistsException;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.NotCompliantMBeanException;
import javax.management.ObjectName;

import org.junit.After;
import org.junit.BeforeClass;

import org.datadog.jmxfetch.reporter.ConsoleReporter;
import org.datadog.jmxfetch.reporter.Reporter;
import org.datadog.jmxfetch.util.CustomLogger;
import org.datadog.jmxfetch.util.MetricsAssert;
import org.datadog.jmxfetch.util.LogLevel;
import org.junit.After;
import org.junit.BeforeClass;

final class ConfigUtil {
public static Path writeConfigYamlToTemp(String content, String yamlName) throws IOException {
Expand Down Expand Up @@ -261,49 +257,7 @@ public void assertMetric(
List<String> additionalTags,
int countTags,
String metricType) {
List<String> tags = new ArrayList<String>(commonTags);
tags.addAll(additionalTags);

for (Map<String, Object> m : metrics) {
String mName = (String) (m.get("name"));
Double mValue = (Double) (m.get("value"));
Set<String> mTags = new HashSet<String>(Arrays.asList((String[]) (m.get("tags"))));

if (mName.equals(name)) {

if (!value.equals(-1)) {
assertEquals((Double) value.doubleValue(), mValue);
} else if (!lowerBound.equals(-1) || !upperBound.equals(-1)) {
assertTrue(mValue > (Double) lowerBound.doubleValue());
assertTrue(mValue < (Double) upperBound.doubleValue());
}

if (countTags != -1) {
assertEquals(countTags, mTags.size());
}
for (String t : tags) {
assertThat(mTags, hasItem(t));
}

if (metricType != null) {
assertEquals(metricType, m.get("type"));
}
// Brand the metric
m.put("tested", true);

return;
}
}
fail(
"Metric assertion failed (name: "
+ name
+ ", value: "
+ value
+ ", tags: "
+ tags
+ ", #tags: "
+ countTags
+ ").");
MetricsAssert.assertMetric(name, value, lowerBound, upperBound, commonTags, additionalTags, countTags, metricType, this.metrics);
}

public void assertMetric(
Expand Down
208 changes: 208 additions & 0 deletions src/test/java/org/datadog/jmxfetch/TestGCMetrics.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
package org.datadog.jmxfetch;

import static org.datadog.jmxfetch.util.MetricsAssert.assertDomainPresent;
import static org.datadog.jmxfetch.util.server.app.org.datadog.jmxfetch.util.server.JDKImage.JDK_11;
import static org.datadog.jmxfetch.util.server.app.org.datadog.jmxfetch.util.server.JDKImage.JDK_17;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.hasItems;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.management.MBeanServerConnection;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;

import lombok.extern.slf4j.Slf4j;

import org.junit.Test;

import org.datadog.jmxfetch.reporter.ConsoleReporter;
import org.datadog.jmxfetch.util.MetricsAssert;
import org.datadog.jmxfetch.util.server.MisbehavingJMXServer;
import org.datadog.jmxfetch.util.server.SimpleAppContainer;

@Slf4j
public class TestGCMetrics extends TestCommon {

@Test
public void testJMXDirectBasic() throws Exception {
try (final SimpleAppContainer container = new SimpleAppContainer()) {
container.start();
final String ipAddress = container.getIp();
final String remoteJmxServiceUrl = String.format(
"service:jmx:rmi:///jndi/rmi://%s:%s/jmxrmi", ipAddress, container.getRMIPort());
final JMXServiceURL jmxUrl = new JMXServiceURL(remoteJmxServiceUrl);
final JMXConnector conn = JMXConnectorFactory.connect(jmxUrl);
final MBeanServerConnection mBeanServerConnection = conn.getMBeanServerConnection();
assertDomainPresent("java.lang", mBeanServerConnection);
}
}

@Test
public void testDefaultOldGC() throws IOException {
try (final MisbehavingJMXServer server = new MisbehavingJMXServer.Builder().build()) {
final List<Map<String, Object>> actualMetrics = startAndGetMetrics(server, false);
List<String> gcGenerations = Arrays.asList(
"G1 Old Generation",
"G1 Young Generation");
assertGCMetric(actualMetrics, "jvm.gc.cms.count", gcGenerations);
assertGCMetric(actualMetrics, "jvm.gc.parnew.time", gcGenerations);
}
}

@Test
public void testDefaultNewGCMetricsUseParallelGC() throws IOException {
try (final MisbehavingJMXServer server = new MisbehavingJMXServer.Builder().withJDKImage(
JDK_11).appendJavaOpts("-XX:+UseParallelGC").build()) {
final List<Map<String, Object>> actualMetrics = startAndGetMetrics(server, true);
assertThat(actualMetrics, hasSize(13));
assertGCMetric(actualMetrics,
"jvm.gc.minor_collection_count", "PS Scavenge", "counter");
assertGCMetric(actualMetrics,
"jvm.gc.minor_collection_time", "PS Scavenge", "counter");
assertGCMetric(actualMetrics,
"jvm.gc.major_collection_count", "PS MarkSweep", "counter");
assertGCMetric(actualMetrics,
"jvm.gc.major_collection_time", "PS MarkSweep", "counter");
}
}

@Test
public void testDefaultNewGCMetricsUseConcMarkSweepGC() throws IOException {
try (final MisbehavingJMXServer server = new MisbehavingJMXServer.Builder().withJDKImage(
JDK_11).appendJavaOpts("-XX:+UseConcMarkSweepGC").build()) {
final List<Map<String, Object>> actualMetrics = startAndGetMetrics(server, true);
assertThat(actualMetrics, hasSize(13));
assertGCMetric(actualMetrics,
"jvm.gc.minor_collection_count", "ParNew", "counter");
assertGCMetric(actualMetrics,
"jvm.gc.minor_collection_time", "ParNew", "counter");
assertGCMetric(actualMetrics,
"jvm.gc.major_collection_count", "ConcurrentMarkSweep", "counter");
assertGCMetric(actualMetrics,
"jvm.gc.major_collection_time", "ConcurrentMarkSweep", "counter");
}
}

@Test
public void testDefaultNewGCMetricsUseG1GC() throws IOException {
try (final MisbehavingJMXServer server = new MisbehavingJMXServer.Builder().withJDKImage(
JDK_17).appendJavaOpts("-XX:+UseG1GC").build()) {
final List<Map<String, Object>> actualMetrics = startAndGetMetrics(server, true);
assertThat(actualMetrics, hasSize(13));
assertGCMetric(actualMetrics,
"jvm.gc.minor_collection_count", "G1 Young Generation", "counter");
assertGCMetric(actualMetrics,
"jvm.gc.minor_collection_time", "G1 Young Generation", "counter");
assertGCMetric(actualMetrics,
"jvm.gc.major_collection_count", "G1 Old Generation", "counter");
assertGCMetric(actualMetrics,
"jvm.gc.major_collection_time", "G1 Old Generation", "counter");
}
}

@Test
public void testDefaultNewGCMetricsUseZGC() throws IOException {
try (final MisbehavingJMXServer server = new MisbehavingJMXServer.Builder().withJDKImage(
JDK_17).appendJavaOpts("-XX:+UseZGC").build()) {
final List<Map<String, Object>> actualMetrics = startAndGetMetrics(server, true);
assertThat(actualMetrics, hasSize(13));
assertGCMetric(actualMetrics,
"jvm.gc.zgc_pauses_collection_count", "ZGC Pauses", "counter");
assertGCMetric(actualMetrics,
"jvm.gc.zgc_pauses_collection_time", "ZGC Pauses", "counter");
assertGCMetric(actualMetrics,
"jvm.gc.zgc_cycles_collection_count", "ZGC Cycles", "counter");
assertGCMetric(actualMetrics,
"jvm.gc.zgc_cycles_collection_time", "ZGC Cycles", "counter");
}
}

private List<Map<String, Object>> startAndGetMetrics(final MisbehavingJMXServer server,
final boolean newGCMetrics) throws IOException {
server.start();
this.initApplicationWithYamlLines(
"init_config:",
" is_jmx: true",
" new_gc_metrics: " + newGCMetrics,
"",
"instances:",
" - name: jmxint_container",
" host: " + server.getIp(),
" collect_default_jvm_metrics: true",
" max_returned_metrics: 300000",
" port: " + server.getRMIPort());
// Run one iteration first
carlosroman marked this conversation as resolved.
Show resolved Hide resolved
this.app.doIteration();
// And then pull get the metrics or else reporter does not have correct number of metrics
((ConsoleReporter) appConfig.getReporter()).getMetrics();

// Actual iteration we care about
this.app.doIteration();
return ((ConsoleReporter) appConfig.getReporter()).getMetrics();
}

private static void assertGCMetric(final List<Map<String, Object>> actualMetrics,
final String expectedMetric,
final String gcGeneration,
final String metricType) {
MetricsAssert.assertMetric(
expectedMetric,
-1,
-1,
10.0,
Collections.singletonList(String.format("name:%s", gcGeneration)),
Arrays.asList(
"instance:jmxint_container",
"jmx_domain:java.lang",
"type:GarbageCollector"),
5,
metricType,
actualMetrics);
}

private static void assertGCMetric(final List<Map<String, Object>> actualMetrics,
Copy link
Contributor

Choose a reason for hiding this comment

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

can this be implemented in terms of the above assertGCMetric? It looks like most of the tests use the above version

final String expectedMetric,
final List<String> gcGenerations) {
final List<Map<String, Object>> filteredMetrics = new ArrayList<>();
for (Map<String, Object> actualMetric : actualMetrics) {
final String name = (String) actualMetric.get("name");
if(expectedMetric.equals(name)) {
filteredMetrics.add(actualMetric);
}
}
assertThat(filteredMetrics, hasSize(gcGenerations.size()));
for (final String name : gcGenerations) {
log.debug("Asserting for metric '{}'", name);
scottopell marked this conversation as resolved.
Show resolved Hide resolved
boolean found = false;
for (Map<String, Object> filteredMetric : filteredMetrics) {
final Set<String> mTags = new HashSet<>(
Arrays.asList((String[]) (filteredMetric.get("tags"))));

if(mTags.contains(String.format("name:%s", name))) {
assertThat(mTags, not(empty()));
assertThat(mTags, hasSize(5));
log.debug("mTags '{}' has size: {}\n{}", name, mTags.size(), mTags);
assertThat(mTags, hasItems(
"instance:jmxint_container",
"jmx_domain:java.lang",
"type:GarbageCollector",
String.format("name:%s", name)));
found = true;
}
}
assertThat(String.format("Did not find metric '%s'", name), found, is(true));
}
}
}
28 changes: 8 additions & 20 deletions src/test/java/org/datadog/jmxfetch/TestReconnectContainer.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package org.datadog.jmxfetch;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;

import static org.datadog.jmxfetch.util.MetricsAssert.assertDomainPresent;
import static org.datadog.jmxfetch.util.MetricsAssert.isDomainPresent;

import java.io.IOException;
import java.util.Collections;
Expand All @@ -12,7 +16,6 @@
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
Expand All @@ -39,21 +42,6 @@ public class TestReconnectContainer extends TestCommon {
private JMXServerSupervisorClient supervisorClient;
private static Slf4jLogConsumer logConsumer = new Slf4jLogConsumer(log);

private static boolean isDomainPresent(String domain, MBeanServerConnection mbs) {
boolean found = false;
try {
String[] domains = mbs.getDomains();
for (int i = 0; i < domains.length; i++) {
if (domains[i].equals(domain)) {
found = true;
}
}
} catch (IOException e) {
found = false;
}
return found;
}

private static ImageFromDockerfile img = new ImageFromDockerfile()
.withFileFromPath(".", Paths.get("./tools/misbehaving-jmx-server/"));

Expand Down Expand Up @@ -101,7 +89,7 @@ public void testJMXDirectBasic() throws Exception {
JMXConnector conn = JMXConnectorFactory.connect(jmxUrl);
MBeanServerConnection mBeanServerConnection = conn.getMBeanServerConnection();

assertEquals(true, isDomainPresent("Bohnanza", mBeanServerConnection));
assertDomainPresent("Bohnanza", mBeanServerConnection);
}

@Test
Expand All @@ -117,15 +105,15 @@ public void testJMXDirectReconnect() throws Exception {
JMXConnector conn = JMXConnectorFactory.connect(jmxUrl);
MBeanServerConnection mBeanServerConnection = conn.getMBeanServerConnection();

assertEquals(true, isDomainPresent("Bohnanza", mBeanServerConnection));
assertDomainPresent("Bohnanza", mBeanServerConnection);

this.controlClient.jmxCutNetwork();

assertEquals(false, isDomainPresent("Bohnanza", mBeanServerConnection));
assertFalse(isDomainPresent("Bohnanza", mBeanServerConnection));

this.controlClient.jmxRestoreNetwork();

assertEquals(true, isDomainPresent("Bohnanza", mBeanServerConnection));
assertDomainPresent("Bohnanza", mBeanServerConnection);
}

@Test
Expand Down
Loading
Loading