diff --git a/common/concurrency/limits/src/main/java/io/helidon/common/concurrency/limits/FixedLimit.java b/common/concurrency/limits/src/main/java/io/helidon/common/concurrency/limits/FixedLimit.java index 0f3984e758d..14f039f67a4 100644 --- a/common/concurrency/limits/src/main/java/io/helidon/common/concurrency/limits/FixedLimit.java +++ b/common/concurrency/limits/src/main/java/io/helidon/common/concurrency/limits/FixedLimit.java @@ -264,25 +264,26 @@ public void init(String socketName) { Gauge.Builder queueLengthBuilder = metricsFactory.gaugeBuilder( namePrefix + "_queue_length", semaphore::getQueueLength).scope(VENDOR); meterRegistry.getOrCreate(queueLengthBuilder); + } - Gauge.Builder concurrentRequestsBuilder = metricsFactory.gaugeBuilder( - namePrefix + "_concurrent_requests", concurrentRequests::get).scope(VENDOR); - meterRegistry.getOrCreate(concurrentRequestsBuilder); + Gauge.Builder concurrentRequestsBuilder = metricsFactory.gaugeBuilder( + namePrefix + "_concurrent_requests", concurrentRequests::get).scope(VENDOR); + meterRegistry.getOrCreate(concurrentRequestsBuilder); - Gauge.Builder rejectedRequestsBuilder = metricsFactory.gaugeBuilder( - namePrefix + "_rejected_requests", rejectedRequests::get).scope(VENDOR); - meterRegistry.getOrCreate(rejectedRequestsBuilder); + Gauge.Builder rejectedRequestsBuilder = metricsFactory.gaugeBuilder( + namePrefix + "_rejected_requests", rejectedRequests::get).scope(VENDOR); + meterRegistry.getOrCreate(rejectedRequestsBuilder); - Timer.Builder rttTimerBuilder = metricsFactory.timerBuilder(namePrefix + "_rtt") - .scope(VENDOR) - .baseUnit(Timer.BaseUnits.MILLISECONDS); - rttTimer = meterRegistry.getOrCreate(rttTimerBuilder); + Timer.Builder rttTimerBuilder = metricsFactory.timerBuilder(namePrefix + "_rtt") + .scope(VENDOR) + .baseUnit(Timer.BaseUnits.MILLISECONDS); + rttTimer = meterRegistry.getOrCreate(rttTimerBuilder); + + Timer.Builder waitTimerBuilder = metricsFactory.timerBuilder(namePrefix + "_queue_wait_time") + .scope(VENDOR) + .baseUnit(Timer.BaseUnits.MILLISECONDS); + queueWaitTimer = meterRegistry.getOrCreate(waitTimerBuilder); - Timer.Builder waitTimerBuilder = metricsFactory.timerBuilder(namePrefix + "_queue_wait_time") - .scope(VENDOR) - .baseUnit(Timer.BaseUnits.MILLISECONDS); - queueWaitTimer = meterRegistry.getOrCreate(waitTimerBuilder); - } } } } diff --git a/webserver/tests/resource-limits/pom.xml b/webserver/tests/resource-limits/pom.xml index 5763952bf8a..efcd37ba329 100644 --- a/webserver/tests/resource-limits/pom.xml +++ b/webserver/tests/resource-limits/pom.xml @@ -57,6 +57,14 @@ io.helidon.webserver helidon-webserver-websocket + + io.helidon.webserver.observe + helidon-webserver-observe + + + io.helidon.webserver.observe + helidon-webserver-observe-metrics + io.helidon.webserver.testing.junit5 helidon-webserver-testing-junit5 diff --git a/webserver/tests/resource-limits/src/test/java/io/helidon/webserver/tests/resourcelimit/AimdLimitMetricsTest.java b/webserver/tests/resource-limits/src/test/java/io/helidon/webserver/tests/resourcelimit/AimdLimitMetricsTest.java new file mode 100644 index 00000000000..95180034aa4 --- /dev/null +++ b/webserver/tests/resource-limits/src/test/java/io/helidon/webserver/tests/resourcelimit/AimdLimitMetricsTest.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2024 Oracle and/or its affiliates. + * + * Licensed 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 io.helidon.webserver.tests.resourcelimit; + +import io.helidon.common.concurrency.limits.AimdLimit; +import io.helidon.webclient.api.HttpClientResponse; +import io.helidon.webclient.api.WebClient; +import io.helidon.webserver.WebServerConfig; +import io.helidon.webserver.http.HttpRules; +import io.helidon.webserver.observe.ObserveFeature; +import io.helidon.webserver.observe.metrics.MetricsObserver; +import io.helidon.webserver.testing.junit5.ServerTest; +import io.helidon.webserver.testing.junit5.SetUpRoute; +import io.helidon.webserver.testing.junit5.SetUpServer; + +import org.junit.jupiter.api.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.MatcherAssert.assertThat; + +@ServerTest +class AimdLimitMetricsTest { + + private static final String[] METRIC_NAMES = { + "default_aimd_queue_length", + "default_aimd_rejected_requests", + "default_aimd_rtt_seconds", + "default_aimd_rtt_seconds_max", + "default_aimd_queue_wait_time_seconds", + "default_aimd_queue_wait_time_seconds_max", + "default_aimd_concurrent_requests", + "default_aimd_limit" + }; + + private final WebClient webClient; + + AimdLimitMetricsTest(WebClient webClient) { + this.webClient = webClient; + } + + @SetUpServer + static void serverSetup(WebServerConfig.Builder builder) { + ObserveFeature observe = ObserveFeature.builder() + .addObserver(MetricsObserver.create()) + .build(); + builder.concurrencyLimit(AimdLimit.builder() + .minLimit(1) + .initialLimit(1) + .maxLimit(1) + .queueLength(1) + .enableMetrics(true) + .build()) + .addFeature(observe); + } + + @SetUpRoute + static void routeSetup(HttpRules rules) { + rules.get("/greet", (req, res) -> { + res.send("hello"); + }); + } + + @Test + void testMetrics() { + try (HttpClientResponse res = webClient.get("/greet").request()) { + assertThat(res.status().code(), is(200)); + } + try (HttpClientResponse res = webClient.get("/observe/metrics").request()) { + String s = res.as(String.class); + for (String metricName : METRIC_NAMES) { + assertThat(s, containsString(metricName)); + } + assertThat(res.status().code(), is(200)); + } + } +} diff --git a/webserver/tests/resource-limits/src/test/java/io/helidon/webserver/tests/resourcelimit/FixedLimitMetricsTest.java b/webserver/tests/resource-limits/src/test/java/io/helidon/webserver/tests/resourcelimit/FixedLimitMetricsTest.java new file mode 100644 index 00000000000..d3f1d996707 --- /dev/null +++ b/webserver/tests/resource-limits/src/test/java/io/helidon/webserver/tests/resourcelimit/FixedLimitMetricsTest.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2024 Oracle and/or its affiliates. + * + * Licensed 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 io.helidon.webserver.tests.resourcelimit; + +import io.helidon.common.concurrency.limits.FixedLimit; +import io.helidon.webclient.api.HttpClientResponse; +import io.helidon.webclient.api.WebClient; + +import io.helidon.webserver.WebServerConfig; +import io.helidon.webserver.http.HttpRules; +import io.helidon.webserver.observe.ObserveFeature; +import io.helidon.webserver.observe.metrics.MetricsObserver; +import io.helidon.webserver.testing.junit5.ServerTest; +import io.helidon.webserver.testing.junit5.SetUpRoute; +import io.helidon.webserver.testing.junit5.SetUpServer; + +import org.junit.jupiter.api.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.MatcherAssert.assertThat; + +@ServerTest +class FixedLimitMetricsTest { + + private static final String[] METRIC_NAMES = { + "default_fixed_queue_length", + "default_fixed_rejected_requests", + "default_fixed_rtt_seconds", + "default_fixed_rtt_seconds_max", + "default_fixed_queue_wait_time_seconds", + "default_fixed_queue_wait_time_seconds_max", + "default_fixed_concurrent_requests" + }; + + private final WebClient webClient; + + FixedLimitMetricsTest(WebClient webClient) { + this.webClient = webClient; + } + + @SetUpServer + static void serverSetup(WebServerConfig.Builder builder) { + ObserveFeature observe = ObserveFeature.builder() + .addObserver(MetricsObserver.create()) + .build(); + builder.concurrencyLimit(FixedLimit.builder() + .permits(1) + .enableMetrics(true) + .build()) + .addFeature(observe); + } + + @SetUpRoute + static void routeSetup(HttpRules rules) { + rules.get("/greet", (req, res) -> { + res.send("hello"); + }); + } + + @Test + void testMetrics() { + try (HttpClientResponse res = webClient.get("/greet").request()) { + assertThat(res.status().code(), is(200)); + } + try (HttpClientResponse res = webClient.get("/observe/metrics").request()) { + String s = res.as(String.class); + for (String metricName : METRIC_NAMES) { + assertThat(s, containsString(metricName)); + } + assertThat(res.status().code(), is(200)); + } + } +}