Skip to content

Commit

Permalink
Change virtual threads metrics default to disabled; remove count-leve…
Browse files Browse the repository at this point in the history
… enable/disable; update tests and doc (#9701)

* Change virtual threads metrics default to disabled; remove count-level enable/disable; update tests and doc

* Restore mp-1 test to expect no base metrics now that the virtual threads metrics are off y default
  • Loading branch information
tjquinno authored Jan 27, 2025
1 parent 7b62e77 commit 7d1d452
Show file tree
Hide file tree
Showing 8 changed files with 45 additions and 133 deletions.
62 changes: 21 additions & 41 deletions docs/src/main/asciidoc/includes/guides/metrics.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ By adding a `metrics` section to your application configuration you can control
// end::controlling-intro-part-1[]
// tag::controlling-intro-part-2[]
* Select whether to collect <<basic-and-extended-kpi,extended key performance indicator {metrics}>>.
* Select which <<observing-vthreads,virtual threads {metrics}>> to report.
* Control reporting of <<controlling-vthreads,virtual threads {metrics}>>.
// end::controlling-intro-part-2[]
// end::controlling-intro[]
Expand Down Expand Up @@ -402,49 +402,52 @@ endif::se-flavor[]
// end::KPI[]
// tag::virtualThreadsMetrics[]
[[observing-vthreads]]
==== Observing Virtual Threads Behavior
[[controlling-vthreads]]
==== Controlling Meters Related to Virtual Threads Behavior
:vthreads-prefix: vthreads
Helidon maintains several {metrics} related to virtual threads as summarized in the next table. Helidon might rely on Java Flight Recorder (JFR) events and JMX MXBeans in computing the {metric} values. Be aware that limitations or changes in the values provided by these sources are outside the control of Helidon.
Helidon optionally maintains several {metrics} related to virtual threads as summarized in the next table. Helidon might rely on Java Flight Recorder (JFR) events and JMX MBeans in computing the {metric} values. Be aware that limitations or changes in the values provided by these sources are outside the control of Helidon.
For performance reasons Helidon does not report virtual thread {metrics} unless you enable them using configuration.
.{metrics_uc} for Virtual Threads
[cols="2,5,3,1"]
[cols="2,5,3"]
|===
| {metric_uc} name | Usage | Source | Reported by default
| {metric_uc} name | Usage | Source
| `{vthreads-prefix}.count` | Current number of active virtual threads. | JFR `jdk.virtualThreadStart` and `jdk.virtualThreadEnd` events| no
| `{vthreads-prefix}.pinned` | Number of times virtual threads have been pinned. | JFR `jdk.virtualThreadPinned` event | yes
| `{vthreads-prefix}.recentPinned` | Distribution of the duration of thread pinning. ^1^ | JFR `jdk.virtualThreadPinned` event | yes
| `{vthreads-prefix}.started` | Number of virtual threads started. | JFR `jdk.virtualThreadStart` event | no
| `{vthreads-prefix}.submitFailed` | Number of times submissions of a virtual thread to a platform carrier thread failed. | JFR `jdk.virtualThreadSubmitFailed` event | yes
| `{vthreads-prefix}.count` | Current number of active virtual threads. | JFR `jdk.virtualThreadStart` and `jdk.virtualThreadEnd` events
| `{vthreads-prefix}.pinned` | Number of times virtual threads have been pinned. | JFR `jdk.virtualThreadPinned` event
| `{vthreads-prefix}.recentPinned` | Distribution of the duration of thread pinning. ^1^ | JFR `jdk.virtualThreadPinned` event
| `{vthreads-prefix}.started` | Number of virtual threads started. | JFR `jdk.virtualThreadStart` event
| `{vthreads-prefix}.submitFailed` | Number of times submissions of a virtual thread to a platform carrier thread failed. | JFR `jdk.virtualThreadSubmitFailed` event
|===
^1^ Distribution summaries can discard stale data, so the `recentPinned` summary might not reflect all thread pinning activity.
^1^ Distribution summaries can discard stale data, so the `recentPinned` summary might not reflect all thread pinning activity.
// tag::virtualThreadsMetricsConfig[]
==== Configuring Virtual Threads {metrics_uc}
===== Disabling Virtual Threads {metrics_uc} Entirely
Gathering data to compute the {metrics} for virtual threads is designed to be as efficient as possible, but doing so still imposes a small load on the server and by default Helidon exposes only the {metrics} related to virtual threads noted in the table above.
===== Enabling Virtual Threads {metrics_uc}
Gathering data to compute the {metrics} for virtual threads is designed to be as efficient as possible, but doing so still imposes a load on the server and by default Helidon does not report {metrics} related to virtual threads.
To disable all {metrics} describing virtual threads, include a config setting as shown in the following example:
To enable the {metrics} describing virtual threads include a config setting as shown in the following example.
.Disabling virtual thread metrics entirely
.Enabling virtual thread {metrics}
ifdef::mp-flavor[]
[source,properties]
----
metrics.virtual-threads.enabled = false
metrics.virtual-threads.enabled = true
----
endif::mp-flavor[]
ifdef::se-flavor[]
[source,yaml]
----
metrics:
virtual-threads:
enabled: false
enabled: true
----
endif::se-flavor[]
===== Controlling Measurements of Pinned Virtual Threads
Helidon measures pinned virtual threads only when the thread is pinned for a length of time at or above a threshold. Control the threshold by configuring the threshold as shown in the example below.
Helidon measures pinned virtual threads only when the thread is pinned for a length of time at or above a threshold. Control the threshold as shown in the example below.
.Setting virtual thread pinning threshold to 100 ms
ifdef::mp-flavor[]
Expand All @@ -464,29 +467,6 @@ metrics:
endif::se-flavor[]
The threshold value is a `Duration` string, such as `PT0.100S` for 100 milliseconds.
===== Controlling Virtual Thread Counts
For performance reasons Helidon does not by default report the {metrics} related to the count of virtual threads.
Enable these {metrics} using configuration.
[CAUTION]
Enabling virtual thread counts can degrade the performance of your server. Do so with care.
.Enabling Virtual Thread Counts
ifdef::mp-flavor[]
[source,properties]
----
metrics.virtual-threads.count.enabled=true
----
endif::[]
ifdef::se-flavor[]
[source,yaml]
----
metrics:
virtual-threads:
count:
enabled: true
----
endif::[]
// end::virtualThreadsMetricsConfig[]
// end::virtualThreadsMetrics[]
Expand Down
5 changes: 3 additions & 2 deletions docs/src/main/asciidoc/includes/metrics/metrics-config.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ Metrics configuration is quite extensive and powerful and, therefore, a bit comp
The rest of this section illustrates some of the most common scenarios:
* <<config-disable,Disable metrics entirely.>>
* <<config-virtual-threads,Choose whether to report virtual threads {metrics}>>.
* <<config-kpi,Choose whether to collect extended key performance indicator metrics.>>
ifdef::mp-flavor[]
* <<config-rest-request,Control `REST.request` metrics collection.>>
Expand Down Expand Up @@ -113,9 +114,9 @@ server:
enabled: false
----
endif::[]
Helidon does not update metrics, and the `{metrics-endpoint}` endpoints respond with `404`..
Helidon does not update metrics, and the `{metrics-endpoint}` endpoints respond with `404`.
==== Enabling {metrics_uc} for Virtual Thread Counts
[#config-virtual-threads]
include::{rootdir}/includes/guides/metrics.adoc[tag=virtualThreadsMetricsConfig]
[#config-kpi]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,20 +205,8 @@ static List<Tag> createTags(String pairs) {
* @return true to include meters related to virtual threads
*/
@Option.Configured("virtual-threads.enabled")
@Option.DefaultBoolean(true)
boolean virtualThreadsEnabled();

/**
* Whether the virtual thread count should be exposed as a meter.
* <p>
* Enabling the virtual thread count meters can degrade performance of the server because the server must monitor Java
* Flight Recorder events for virtual thread starts and stops to maintain the count.
*
* @return true if the metrics system should compute virtual thread count meters
*/
@Option.Configured("virtual-threads.count.enabled")
@Option.DefaultBoolean(false)
boolean virtualThreadCountEnabled();
boolean virtualThreadsEnabled();

/**
* Threshold for sampling pinned virtual threads to include in the pinned threads meter.
Expand Down
8 changes: 4 additions & 4 deletions metrics/system-meters/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -94,21 +94,21 @@
<id>default-test</id>
<configuration>
<excludes>
<exclude>**/TestVirtualThreadsMetersWithCounts.java</exclude>
<exclude>**/TestVirtualThreads*.java</exclude>
</excludes>
</configuration>
</execution>
<execution>
<id>test-with-virtual-thread-counts</id>
<id>test-with-virtual-thread-meters</id>
<goals>
<goal>test</goal>
</goals>
<configuration>
<includes>
<include>**/TestVirtualThreadsMetersWithCounts.java</include>
<include>**/TestVirtualThreads*.java</include>
</includes>
<systemPropertyVariables>
<metrics.virtual-threads.count.enabled>true</metrics.virtual-threads.count.enabled>
<metrics.virtual-threads.enabled>true</metrics.virtual-threads.enabled>
</systemPropertyVariables>
</configuration>
</execution>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,17 +102,15 @@ public VThreadSystemMetersProvider() {
listenFor(recordingStream, Map.of("jdk.VirtualThreadSubmitFailed", this::recordSubmitFail,
"jdk.VirtualThreadPinned", this::recordThreadPin));

if (metricsFactory.metricsConfig().virtualThreadCountEnabled()) {
meterBuilders.add(Gauge.builder(METER_NAME_PREFIX + COUNT, () -> virtualThreads)
.description("Active virtual threads")
.scope(METER_SCOPE));
meterBuilders.add(Gauge.builder(METER_NAME_PREFIX + STARTS, () -> virtualThreadStarts)
.description("Number of virtual thread starts")
.scope(METER_SCOPE));

listenFor(recordingStream, Map.of("jdk.VirtualThreadStart", this::recordThreadStart,
"jdk.VirtualThreadEnd", this::recordThreadEnd));
}
meterBuilders.add(Gauge.builder(METER_NAME_PREFIX + COUNT, () -> virtualThreads)
.description("Active virtual threads")
.scope(METER_SCOPE));
meterBuilders.add(Gauge.builder(METER_NAME_PREFIX + STARTS, () -> virtualThreadStarts)
.description("Number of virtual thread starts")
.scope(METER_SCOPE));

listenFor(recordingStream, Map.of("jdk.VirtualThreadStart", this::recordThreadStart,
"jdk.VirtualThreadEnd", this::recordThreadEnd));

recordingStream.startAsync();
return meterBuilders;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,29 +47,12 @@ void checkDefault() {
MetricsFactory metricsFactory = MetricsFactory.getInstance(config);
VThreadSystemMetersProvider provider = new VThreadSystemMetersProvider();
var meterBuilders = provider.meterBuilders(metricsFactory);
assertThat("Default meter builders",
meterBuilders,
containsInAnyOrder(allOf(withName(equalTo(METER_NAME_PREFIX + PINNED)),
instanceOf(Gauge.Builder.class)),
allOf(withName(equalTo(METER_NAME_PREFIX + SUBMIT_FAILURES)),
instanceOf(Gauge.Builder.class)),
allOf(withName(equalTo(METER_NAME_PREFIX + RECENT_PINNED)),
instanceOf(Timer.Builder.class))));
assertThat("Pinned thread threshold", provider.pinnedVirtualThreadsThresholdMillis(), equalTo(20L));
}

@Test
void checkVirtualThreadMetersDisabled() {
Config config = Config.just(ConfigSources.create(Map.of("virtual-threads.enabled", "false")));
MetricsFactory metricsFactory = MetricsFactory.getInstance(config);
VThreadSystemMetersProvider provider = new VThreadSystemMetersProvider();
var meterBuilders = provider.meterBuilders(metricsFactory);
assertThat("Meter builders with virtual threads meters disabled", meterBuilders, empty());
assertThat("Meter builders with default config", meterBuilders, empty());
}

@Test
void checkVirtualThreadCountMetersEnabled() {
Config config = Config.just(ConfigSources.create(Map.of("virtual-threads.count.enabled", "true")));
Config config = Config.just(ConfigSources.create(Map.of("virtual-threads.enabled", "true")));
MetricsFactory metricsFactory = MetricsFactory.getInstance(config);
VThreadSystemMetersProvider provider = new VThreadSystemMetersProvider();
var meterBuilders = provider.meterBuilders(metricsFactory);
Expand All @@ -91,7 +74,8 @@ void checkVirtualThreadCountMetersEnabled() {

@Test
void checkPinnedThreadThreshold() {
Config config = Config.just(ConfigSources.create(Map.of("virtual-threads.pinned.threshold", "PT0.040S")));
Config config = Config.just(ConfigSources.create(Map.of("virtual-threads.enabled", "true",
"virtual-threads.pinned.threshold", "PT0.040S")));
MetricsFactory metricsFactory = MetricsFactory.getInstance(config);
VThreadSystemMetersProvider provider = new VThreadSystemMetersProvider();
provider.meterBuilders(metricsFactory);
Expand All @@ -102,7 +86,8 @@ void checkPinnedThreadThreshold() {

@Test
void checkRecentPinnedTimerLookup() {
Config config = Config.just(ConfigSources.create(Map.of("virtual-threads.pinned.threshold", "PT0.040S")));
Config config = Config.just(ConfigSources.create(Map.of("virtual-threads.enabled", "true",
"virtual-threads.pinned.threshold", "PT0.040S")));
MetricsFactory metricsFactory = MetricsFactory.getInstance(config);
VThreadSystemMetersProvider provider = new VThreadSystemMetersProvider();
provider.meterBuilders(metricsFactory);
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ private static void testBean(int port, String jwtToken) {
});

invoke(collector, "Application metric registry", "Timers.size(): 1", aBean::appRegistry);
invoke(collector, "Base metric registry", "Timers.size(): 1", aBean::baseRegistry);
invoke(collector, "Base metric registry", "Timers.size(): 0", aBean::baseRegistry);

// JWT-Auth
validateJwtProtectedResource(collector, target, jwtToken);
Expand Down

0 comments on commit 7d1d452

Please sign in to comment.