From 0fb3d6b7f4b2e5fa8b950793af581b0f8b7f0264 Mon Sep 17 00:00:00 2001 From: Otavio Rodolfo Piske Date: Mon, 22 May 2023 14:11:29 +0200 Subject: [PATCH 1/3] (chores) performance: add new performance test to compute the time required to create an endpoint This test computes the accumulated time of all the operations required to create a given number of routes (500, in this test). --- .../camel/itest/jmh/EndpointCreationTest.java | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 tests/camel-jmh/src/test/java/org/apache/camel/itest/jmh/EndpointCreationTest.java diff --git a/tests/camel-jmh/src/test/java/org/apache/camel/itest/jmh/EndpointCreationTest.java b/tests/camel-jmh/src/test/java/org/apache/camel/itest/jmh/EndpointCreationTest.java new file mode 100644 index 0000000..e8da654 --- /dev/null +++ b/tests/camel-jmh/src/test/java/org/apache/camel/itest/jmh/EndpointCreationTest.java @@ -0,0 +1,77 @@ +package org.apache.camel.itest.jmh; + +import java.util.concurrent.TimeUnit; + +import org.apache.camel.CamelContext; +import org.apache.camel.ProducerTemplate; +import org.apache.camel.ServiceStatus; +import org.apache.camel.impl.DefaultCamelContext; +import org.junit.jupiter.api.Test; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.infra.Blackhole; +import org.openjdk.jmh.results.format.ResultFormatType; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +/** + * This test computes the accumulated time of all the operations required to create a given number of routes (500, in this test). + */ +public class EndpointCreationTest { + + @Test + public void launchBenchmark() throws Exception { + Options opt = new OptionsBuilder() + // Specify which benchmarks to run. + // You can be more specific if you'd like to run only one benchmark per test. + .include(this.getClass().getName() + ".*") + // Set the following options as needed + .measurementIterations(10) + .warmupIterations(5) + .forks(1) + .resultFormat(ResultFormatType.JSON) + .result(this.getClass().getSimpleName() + ".jmh.json") + .build(); + + new Runner(opt).run(); + } + + @State(Scope.Thread) + public static class BenchmarkState { + CamelContext context; + ProducerTemplate producerTemplate; + String[] routes = new String[500]; + + @Setup(Level.Iteration) + public void initialize() throws Exception { + context = new DefaultCamelContext(); + + context.start(); + producerTemplate = context.createProducerTemplate(); + + for (int i = 0; i < routes.length; i++) { + routes[i] = "controlbus:route?routeId=route" + i + "&action=status&loggingLevel=off"; + } + } + } + + + @OutputTimeUnit(TimeUnit.MILLISECONDS) + @BenchmarkMode(Mode.AverageTime) + @Benchmark + public void testCreation(BenchmarkState state, Blackhole bh) { + for (String route : state.routes) { + Object reply = state.producerTemplate.requestBody(route, null, + ServiceStatus.class); + + bh.consume(reply); + } + } +} From 3f26170613aa8ab484d7c99c949122a025743ec7 Mon Sep 17 00:00:00 2001 From: Otavio Rodolfo Piske Date: Mon, 22 May 2023 14:11:49 +0200 Subject: [PATCH 2/3] (chores) performance: add new performance test to compute the time required to resolve an endpoint his test computes the accumulated time of all the operations required to resolve the same route over and over. --- .../camel/itest/jmh/EndpointResolveTest.java | 127 ++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 tests/camel-jmh/src/test/java/org/apache/camel/itest/jmh/EndpointResolveTest.java diff --git a/tests/camel-jmh/src/test/java/org/apache/camel/itest/jmh/EndpointResolveTest.java b/tests/camel-jmh/src/test/java/org/apache/camel/itest/jmh/EndpointResolveTest.java new file mode 100644 index 0000000..90051bc --- /dev/null +++ b/tests/camel-jmh/src/test/java/org/apache/camel/itest/jmh/EndpointResolveTest.java @@ -0,0 +1,127 @@ +package org.apache.camel.itest.jmh; + +import java.util.concurrent.TimeUnit; + +import org.apache.camel.CamelContext; +import org.apache.camel.ProducerTemplate; +import org.apache.camel.ServiceStatus; +import org.apache.camel.impl.DefaultCamelContext; +import org.junit.jupiter.api.Test; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Threads; +import org.openjdk.jmh.infra.Blackhole; +import org.openjdk.jmh.results.format.ResultFormatType; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +/** + * This test computes the accumulated time of all the operations required to resolve the same route over and over. + */ +public class EndpointResolveTest { + + @Test + public void launchBenchmark() throws Exception { + Options opt = new OptionsBuilder() + // Specify which benchmarks to run. + // You can be more specific if you'd like to run only one benchmark per test. + .include(this.getClass().getName() + ".*") + // Set the following options as needed + .measurementIterations(10) + .warmupIterations(5) + .forks(1) + .resultFormat(ResultFormatType.JSON) + .result(this.getClass().getSimpleName() + ".jmh.json") + .build(); + + new Runner(opt).run(); + } + + @State(Scope.Benchmark) + public static class BenchmarkState { + CamelContext context; + ProducerTemplate producerTemplate; + + @Setup(Level.Trial) + public void initialize() throws Exception { + context = new DefaultCamelContext(); + + context.start(); + producerTemplate = context.createProducerTemplate(); + } + } + + + @OutputTimeUnit(TimeUnit.MICROSECONDS) + @BenchmarkMode(Mode.AverageTime) + @Benchmark + public void testActionStatus_1(BenchmarkState state, Blackhole bh) { + Object reply = state.producerTemplate.requestBody("controlbus:route?routeId=route1&action=status&loggingLevel=off", null, + ServiceStatus.class); + + bh.consume(reply); + } + + + @OutputTimeUnit(TimeUnit.MICROSECONDS) + @BenchmarkMode(Mode.AverageTime) + @Benchmark + @Threads(2) + public void testActionStatus_2(BenchmarkState state, Blackhole bh) { + Object reply = state.producerTemplate.requestBody("controlbus:route?routeId=route1&action=status&loggingLevel=off", null, + ServiceStatus.class); + + bh.consume(reply); + } + + @OutputTimeUnit(TimeUnit.MICROSECONDS) + @BenchmarkMode(Mode.AverageTime) + @Benchmark + @Threads(4) + public void testActionStatus_4(BenchmarkState state, Blackhole bh) { + Object reply = state.producerTemplate.requestBody("controlbus:route?routeId=route1&action=status&loggingLevel=off", null, + ServiceStatus.class); + + bh.consume(reply); + } + + @OutputTimeUnit(TimeUnit.MICROSECONDS) + @BenchmarkMode(Mode.AverageTime) + @Benchmark + @Threads(8) + public void testActionStatus_8(BenchmarkState state, Blackhole bh) { + Object reply = state.producerTemplate.requestBody("controlbus:route?routeId=route1&action=status&loggingLevel=off", null, + ServiceStatus.class); + + bh.consume(reply); + } + + @OutputTimeUnit(TimeUnit.MICROSECONDS) + @BenchmarkMode(Mode.AverageTime) + @Benchmark + @Threads(16) + public void testActionStatus_16(BenchmarkState state, Blackhole bh) { + Object reply = state.producerTemplate.requestBody("controlbus:route?routeId=route1&action=status&loggingLevel=off", null, + ServiceStatus.class); + + bh.consume(reply); + } + + @OutputTimeUnit(TimeUnit.MICROSECONDS) + @BenchmarkMode(Mode.AverageTime) + @Benchmark + @Threads(32) + public void testActionStatus_32(BenchmarkState state, Blackhole bh) { + Object reply = state.producerTemplate.requestBody("controlbus:route?routeId=route1&action=status&loggingLevel=off", null, + ServiceStatus.class); + + bh.consume(reply); + } +} From ffab236bc4c4f8cbedbad7f49862153e62e684d1 Mon Sep 17 00:00:00 2001 From: Otavio Rodolfo Piske Date: Mon, 22 May 2023 14:43:44 +0200 Subject: [PATCH 3/3] (chores) performance: add new performance test to verify the EndpointRegistry - EndpointRegistry: this tests specific operations of the Endpoint registry in single-thread scenarios - EndpointRegistryScalabilityTest: this tests specific operations of the Endpoint registry in multi-thread scenarios.= --- .../jmh/EndpointRegistryScalabilityTest.java | 245 ++++++++++++++++++ .../camel/itest/jmh/EndpointRegistryTest.java | 138 ++++++++++ 2 files changed, 383 insertions(+) create mode 100644 tests/camel-jmh/src/test/java/org/apache/camel/itest/jmh/EndpointRegistryScalabilityTest.java create mode 100644 tests/camel-jmh/src/test/java/org/apache/camel/itest/jmh/EndpointRegistryTest.java diff --git a/tests/camel-jmh/src/test/java/org/apache/camel/itest/jmh/EndpointRegistryScalabilityTest.java b/tests/camel-jmh/src/test/java/org/apache/camel/itest/jmh/EndpointRegistryScalabilityTest.java new file mode 100644 index 0000000..f01945c --- /dev/null +++ b/tests/camel-jmh/src/test/java/org/apache/camel/itest/jmh/EndpointRegistryScalabilityTest.java @@ -0,0 +1,245 @@ +package org.apache.camel.itest.jmh; + +import java.util.concurrent.TimeUnit; + +import org.apache.camel.Endpoint; +import org.apache.camel.ProducerTemplate; +import org.apache.camel.ServiceStatus; +import org.apache.camel.impl.DefaultCamelContext; +import org.apache.camel.spi.EndpointRegistry; +import org.apache.camel.support.NormalizedUri; +import org.junit.jupiter.api.Test; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Threads; +import org.openjdk.jmh.infra.Blackhole; +import org.openjdk.jmh.results.format.ResultFormatType; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +/** + * These tests specific operations of the Endpoint registry in multi-thread scenarios. + */ +public class EndpointRegistryScalabilityTest { + + @Test + public void launchBenchmark() throws Exception { + Options opt = new OptionsBuilder() + // Specify which benchmarks to run. + // You can be more specific if you'd like to run only one benchmark per test. + .include(this.getClass().getName() + ".*") + // Set the following options as needed + .measurementIterations(10) + .warmupIterations(5) + .forks(1) + .resultFormat(ResultFormatType.JSON) + .result(this.getClass().getSimpleName() + ".jmh.json") + .build(); + + new Runner(opt).run(); + } + + @State(Scope.Benchmark) + public static class BenchmarkState { + DefaultCamelContext context; + ProducerTemplate producerTemplate; + EndpointRegistry endpointRegistry; + + NormalizedUri[] routes = new NormalizedUri[500]; + NormalizedUri[] nonExistentRoutes = new NormalizedUri[500]; + Endpoint[] endpoints = new Endpoint[500]; + + @Setup(Level.Trial) + public void initialize() throws Exception { + context = new DefaultCamelContext(); + + context.start(); + producerTemplate = context.createProducerTemplate(); + endpointRegistry = context.getEndpointRegistry(); + + for (int i = 0; i < routes.length; i++) { + String route = "controlbus:route?routeId=route" + i + "&action=status&loggingLevel=off"; + routes[i] = NormalizedUri.newNormalizedUri(route, false); + + endpoints[i] = context.getEndpoint(route); + producerTemplate.requestBody(endpoints[i], null, ServiceStatus.class); + + nonExistentRoutes[i] = NormalizedUri.newNormalizedUri("controlbus:route?routeId=nonExistentRoutes" + i + "&action=status&loggingLevel=off", + false); + + } + } + } + + + private static void doContainsCheck(BenchmarkState state, Blackhole bh) { + for (NormalizedUri route : state.routes) { + bh.consume(state.endpointRegistry.containsKey(route)); + } + } + + + @OutputTimeUnit(TimeUnit.MICROSECONDS) + @BenchmarkMode(Mode.AverageTime) + @Threads(2) + @Benchmark + public void testContainsNonExistentKey_2(BenchmarkState state, Blackhole bh) { + doContainsCheck(state, bh); + } + + @OutputTimeUnit(TimeUnit.MICROSECONDS) + @BenchmarkMode(Mode.AverageTime) + @Threads(4) + @Benchmark + public void testContainsNonExistentKey_4(BenchmarkState state, Blackhole bh) { + doContainsCheck(state, bh); + } + + @OutputTimeUnit(TimeUnit.MICROSECONDS) + @BenchmarkMode(Mode.AverageTime) + @Threads(8) + @Benchmark + public void testContainsNonExistentKey_8(BenchmarkState state, Blackhole bh) { + doContainsCheck(state, bh); + } + + @OutputTimeUnit(TimeUnit.MICROSECONDS) + @BenchmarkMode(Mode.AverageTime) + @Threads(2) + @Benchmark + public void testContainsNonExistentValue_2(BenchmarkState state, Blackhole bh) { + doContainsValueCheck(state, bh); + } + + private static void doContainsValueCheck(BenchmarkState state, Blackhole bh) { + for (Endpoint ep : state.endpoints) { + bh.consume(state.endpointRegistry.containsValue(ep)); + } + } + + @OutputTimeUnit(TimeUnit.MICROSECONDS) + @BenchmarkMode(Mode.AverageTime) + @Threads(4) + @Benchmark + public void testContainsNonExistentValue_4(BenchmarkState state, Blackhole bh) { + doContainsValueCheck(state, bh); + } + + + @OutputTimeUnit(TimeUnit.MICROSECONDS) + @BenchmarkMode(Mode.AverageTime) + @Threads(8) + @Benchmark + public void testContainsNonExistentValue_8(BenchmarkState state, Blackhole bh) { + doContainsValueCheck(state, bh); + } + + private static void doIsDynamicCheck(BenchmarkState state, Blackhole bh) { + for (NormalizedUri route : state.routes) { + bh.consume(state.endpointRegistry.isDynamic(route.get())); + } + } + + @OutputTimeUnit(TimeUnit.MICROSECONDS) + @BenchmarkMode(Mode.AverageTime) + @Threads(2) + @Benchmark + public void testIsDynamic_2(BenchmarkState state, Blackhole bh) { + doIsDynamicCheck(state, bh); + } + + @OutputTimeUnit(TimeUnit.MICROSECONDS) + @BenchmarkMode(Mode.AverageTime) + @Threads(4) + @Benchmark + public void testIsDynamic_4(BenchmarkState state, Blackhole bh) { + doIsDynamicCheck(state, bh); + } + + @OutputTimeUnit(TimeUnit.MICROSECONDS) + @BenchmarkMode(Mode.AverageTime) + @Threads(8) + @Benchmark + public void testIsDynamic_8(BenchmarkState state, Blackhole bh) { + doIsDynamicCheck(state, bh); + } + + @OutputTimeUnit(TimeUnit.MICROSECONDS) + @BenchmarkMode(Mode.AverageTime) + @Threads(2) + @Benchmark + public void testDynamicSize_2(BenchmarkState state, Blackhole bh) { + bh.consume(state.endpointRegistry.dynamicSize()); + } + + @OutputTimeUnit(TimeUnit.MICROSECONDS) + @BenchmarkMode(Mode.AverageTime) + @Threads(4) + @Benchmark + public void testDynamicSize_4(BenchmarkState state, Blackhole bh) { + bh.consume(state.endpointRegistry.dynamicSize()); + } + + @OutputTimeUnit(TimeUnit.MICROSECONDS) + @BenchmarkMode(Mode.AverageTime) + @Threads(8) + @Benchmark + public void testDynamicSize_8(BenchmarkState state, Blackhole bh) { + bh.consume(state.endpointRegistry.dynamicSize()); + } + + @OutputTimeUnit(TimeUnit.MICROSECONDS) + @BenchmarkMode(Mode.AverageTime) + @Threads(2) + @Benchmark + public void testReadOnlyMap_2(BenchmarkState state, Blackhole bh) { + bh.consume(state.endpointRegistry.getReadOnlyMap()); + } + + @OutputTimeUnit(TimeUnit.MICROSECONDS) + @BenchmarkMode(Mode.AverageTime) + @Threads(4) + @Benchmark + public void testReadOnlyMap_4(BenchmarkState state, Blackhole bh) { + bh.consume(state.endpointRegistry.getReadOnlyMap()); + } + + @OutputTimeUnit(TimeUnit.MICROSECONDS) + @BenchmarkMode(Mode.AverageTime) + @Threads(8) + @Benchmark + public void testReadOnlyMap_8(BenchmarkState state, Blackhole bh) { + bh.consume(state.endpointRegistry.getReadOnlyMap()); + } + + @OutputTimeUnit(TimeUnit.MICROSECONDS) + @BenchmarkMode(Mode.AverageTime) + @Threads(2) + @Benchmark + public void testReadOnlyValues_2(BenchmarkState state, Blackhole bh) { + bh.consume(state.endpointRegistry.getReadOnlyValues()); + } + + @OutputTimeUnit(TimeUnit.MICROSECONDS) + @BenchmarkMode(Mode.AverageTime) + @Threads(4) + @Benchmark + public void testReadOnlyValues_4(BenchmarkState state, Blackhole bh) { + bh.consume(state.endpointRegistry.getReadOnlyValues()); + } + + @OutputTimeUnit(TimeUnit.MICROSECONDS) + @BenchmarkMode(Mode.AverageTime) + @Threads(8) + @Benchmark + public void testReadOnlyValues_8(BenchmarkState state, Blackhole bh) { + bh.consume(state.endpointRegistry.getReadOnlyValues()); + } +} diff --git a/tests/camel-jmh/src/test/java/org/apache/camel/itest/jmh/EndpointRegistryTest.java b/tests/camel-jmh/src/test/java/org/apache/camel/itest/jmh/EndpointRegistryTest.java new file mode 100644 index 0000000..31fcbda --- /dev/null +++ b/tests/camel-jmh/src/test/java/org/apache/camel/itest/jmh/EndpointRegistryTest.java @@ -0,0 +1,138 @@ +package org.apache.camel.itest.jmh; + +import java.util.concurrent.TimeUnit; + +import org.apache.camel.Endpoint; +import org.apache.camel.ProducerTemplate; +import org.apache.camel.ServiceStatus; +import org.apache.camel.impl.DefaultCamelContext; +import org.apache.camel.spi.EndpointRegistry; +import org.apache.camel.support.NormalizedUri; +import org.junit.jupiter.api.Test; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.infra.Blackhole; +import org.openjdk.jmh.results.format.ResultFormatType; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +/** + * These tests specific operations of the Endpoint registry in single-thread scenarios. + */ +public class EndpointRegistryTest { + + @Test + public void launchBenchmark() throws Exception { + Options opt = new OptionsBuilder() + // Specify which benchmarks to run. + // You can be more specific if you'd like to run only one benchmark per test. + .include(this.getClass().getName() + ".*") + // Set the following options as needed + .measurementIterations(10) + .warmupIterations(5) + .forks(1) + .resultFormat(ResultFormatType.JSON) + .result(this.getClass().getSimpleName() + ".jmh.json") + .build(); + + new Runner(opt).run(); + } + + @State(Scope.Benchmark) + public static class BenchmarkState { + DefaultCamelContext context; + ProducerTemplate producerTemplate; + EndpointRegistry endpointRegistry; + + NormalizedUri[] routes = new NormalizedUri[500]; + NormalizedUri[] nonExistentRoutes = new NormalizedUri[500]; + Endpoint[] endpoints = new Endpoint[500]; + + @Setup(Level.Trial) + public void initialize() throws Exception { + context = new DefaultCamelContext(); + + context.start(); + producerTemplate = context.createProducerTemplate(); + endpointRegistry = context.getEndpointRegistry(); + + for (int i = 0; i < routes.length; i++) { + String route = "controlbus:route?routeId=route" + i + "&action=status&loggingLevel=off"; + routes[i] = NormalizedUri.newNormalizedUri(route, false); + + endpoints[i] = context.getEndpoint(route); + producerTemplate.requestBody(endpoints[i], null, ServiceStatus.class); + + nonExistentRoutes[i] = NormalizedUri.newNormalizedUri("controlbus:route?routeId=nonExistentRoutes" + i + "&action=status&loggingLevel=off", + false); + + } + } + } + + @OutputTimeUnit(TimeUnit.MICROSECONDS) + @BenchmarkMode(Mode.AverageTime) + @Benchmark + public void testContainsExistentKey(BenchmarkState state, Blackhole bh) { + for (NormalizedUri route : state.routes) { + bh.consume(state.endpointRegistry.containsKey(route)); + } + } + + + @OutputTimeUnit(TimeUnit.MICROSECONDS) + @BenchmarkMode(Mode.AverageTime) + @Benchmark + public void testContainsNonExistentKey(BenchmarkState state, Blackhole bh) { + for (NormalizedUri route : state.nonExistentRoutes) { + bh.consume(state.endpointRegistry.containsKey(route)); + } + } + + @OutputTimeUnit(TimeUnit.MICROSECONDS) + @BenchmarkMode(Mode.AverageTime) + @Benchmark + public void testContainsNonExistentValue(BenchmarkState state, Blackhole bh) { + for (Endpoint ep : state.endpoints) { + bh.consume(state.endpointRegistry.containsValue(ep)); + } + } + + @OutputTimeUnit(TimeUnit.MICROSECONDS) + @BenchmarkMode(Mode.AverageTime) + @Benchmark + public void testIsDynamic(BenchmarkState state, Blackhole bh) { + for (NormalizedUri route : state.routes) { + bh.consume(state.endpointRegistry.isDynamic(route.get())); + } + } + + @OutputTimeUnit(TimeUnit.MICROSECONDS) + @BenchmarkMode(Mode.AverageTime) + @Benchmark + public void testDynamicSize(BenchmarkState state, Blackhole bh) { + bh.consume(state.endpointRegistry.dynamicSize()); + } + + // The following methods require camel 4.0.0-M2 or newer. Comment these if running w/ 4.0.0-M1 or 3.x + @OutputTimeUnit(TimeUnit.MICROSECONDS) + @BenchmarkMode(Mode.AverageTime) + @Benchmark + public void testReadOnlyMap(BenchmarkState state, Blackhole bh) { + bh.consume(state.endpointRegistry.getReadOnlyMap()); + } + + @OutputTimeUnit(TimeUnit.MICROSECONDS) + @BenchmarkMode(Mode.AverageTime) + @Benchmark + public void testReadOnlyValues(BenchmarkState state, Blackhole bh) { + bh.consume(state.endpointRegistry.getReadOnlyValues()); + } +}