-
Notifications
You must be signed in to change notification settings - Fork 1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds tests to check concurrent behavior and measure performance of registering meters. Updates the implementation of dealing with stale IDs to make it less prone to stale reads.
- Loading branch information
Showing
11 changed files
with
267 additions
and
22 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
plugins { | ||
alias(libs.plugins.jcstress) | ||
} | ||
|
||
dependencies { | ||
implementation project(":micrometer-core") | ||
// implementation("io.micrometer:micrometer-core:1.12.4") | ||
runtimeOnly(libs.logbackLatest) | ||
} | ||
|
||
jcstress { | ||
jcstressDependency 'org.openjdk.jcstress:jcstress-core:0.16' | ||
|
||
verbose = true | ||
} |
148 changes: 148 additions & 0 deletions
148
...-tests/src/jcstress/java/io/micrometer/concurrencytests/MeterRegistryConcurrencyTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
/* | ||
* Copyright 2024 VMware, Inc. | ||
* | ||
* 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 | ||
* | ||
* https://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.micrometer.concurrencytests; | ||
|
||
import io.micrometer.core.instrument.Counter; | ||
import io.micrometer.core.instrument.MeterRegistry; | ||
import io.micrometer.core.instrument.simple.SimpleMeterRegistry; | ||
import org.openjdk.jcstress.annotations.*; | ||
import org.openjdk.jcstress.infra.results.LL_Result; | ||
import org.openjdk.jcstress.infra.results.Z_Result; | ||
|
||
public class MeterRegistryConcurrencyTest { | ||
|
||
/* | ||
* Registering the same new Meter from multiple threads should be safe and consistent. | ||
*/ | ||
@JCStressTest | ||
@Outcome(id = { "true" }, expect = Expect.ACCEPTABLE, desc = "same meter returned for concurrent registers") | ||
@Outcome(expect = Expect.FORBIDDEN) | ||
@State | ||
public static class ConcurrentRegisterNew { | ||
|
||
MeterRegistry registry = new SimpleMeterRegistry(); | ||
|
||
Counter c1; | ||
|
||
Counter c2; | ||
|
||
@Actor | ||
public void actor1() { | ||
c1 = registry.counter("counter"); | ||
} | ||
|
||
@Actor | ||
public void actor2() { | ||
c2 = registry.counter("counter"); | ||
} | ||
|
||
@Arbiter | ||
public void arbiter(Z_Result r) { | ||
r.r1 = c1 == c2; | ||
} | ||
|
||
} | ||
|
||
/* | ||
* Likewise, registering an existing Meter from multiple threads is safe. | ||
*/ | ||
@JCStressTest | ||
@Outcome(id = { "true" }, expect = Expect.ACCEPTABLE, desc = "same meter returned for concurrent registers") | ||
@Outcome(expect = Expect.FORBIDDEN) | ||
@State | ||
public static class ConcurrentRegisterExisting { | ||
|
||
MeterRegistry registry = new SimpleMeterRegistry(); | ||
|
||
Counter c1; | ||
|
||
Counter c2; | ||
|
||
public ConcurrentRegisterExisting() { | ||
registry.counter("counter"); | ||
} | ||
|
||
@Actor | ||
public void actor1() { | ||
c1 = registry.counter("counter"); | ||
} | ||
|
||
@Actor | ||
public void actor2() { | ||
c2 = registry.counter("counter"); | ||
} | ||
|
||
@Arbiter | ||
public void arbiter(Z_Result r) { | ||
r.r1 = c1 == c2; | ||
} | ||
|
||
} | ||
|
||
// @formatter:off | ||
/* | ||
When configuring a MeterFilter after a Meter has already been registered, existing meters will be marked stale. | ||
Subsequent calls to {@code getOrCreateMeter} for those Meters create a new Meter with all MeterFilters applied. | ||
If multiple concurrent calls to {@code getOrCreateMeter} interleave, it's possible not all see the new Meter. | ||
We ideally want both to get the new meter, but we don't want to pay the cost associated with that level of safety | ||
given the expected rarity of this situation happening, so we aim to get as close as possible for cheap. | ||
RESULT SAMPLES FREQ EXPECT DESCRIPTION | ||
null, null 0 0.00% Forbidden both get stale meter | ||
null, tag 39,491 0.13% Interesting one stale meter returned | ||
tag, null 40,389 0.13% Interesting one stale meter returned | ||
tag, tag 30,941,328 99.74% Acceptable both get new meter | ||
*/ | ||
// @formatter:on | ||
@JCStressTest | ||
@Outcome(id = { "tag, tag" }, expect = Expect.ACCEPTABLE, desc = "both get new meter") | ||
@Outcome(id = { "null, tag", "tag, null" }, expect = Expect.ACCEPTABLE_INTERESTING, | ||
desc = "one stale meter returned") | ||
@Outcome(id = { "null, null" }, expect = Expect.FORBIDDEN, desc = "both get stale meter") | ||
@State | ||
public static class ConcurrentRegisterWithStaleId { | ||
|
||
MeterRegistry registry = new SimpleMeterRegistry(); | ||
|
||
Counter c1; | ||
|
||
Counter c2; | ||
|
||
public ConcurrentRegisterWithStaleId() { | ||
registry.counter("counter"); | ||
registry.counter("another"); | ||
registry.config().commonTags("common", "tag"); | ||
} | ||
|
||
@Actor | ||
public void actor1() { | ||
c1 = registry.counter("counter"); | ||
} | ||
|
||
@Actor | ||
public void actor2() { | ||
c2 = registry.counter("counter"); | ||
} | ||
|
||
@Arbiter | ||
public void arbiter(LL_Result r) { | ||
r.r1 = c1.getId().getTag("common"); | ||
r.r2 = c2.getId().getTag("common"); | ||
} | ||
|
||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
<!-- | ||
Copyright 2024 VMware, Inc. | ||
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 | ||
https://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. | ||
--> | ||
<configuration> | ||
|
||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> | ||
<!-- encoders are by default assigned the type | ||
ch.qos.logback.classic.encoder.PatternLayoutEncoder --> | ||
<encoder> | ||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> | ||
</encoder> | ||
</appender> | ||
|
||
<root level="warn"> | ||
<appender-ref ref="STDOUT" /> | ||
</root> | ||
|
||
<!-- Late MeterFilter config logs are verbose in ConcurrentRegisterWithStaleId --> | ||
<logger name="io.micrometer.core.instrument.simple.SimpleMeterRegistry" level="error"/> | ||
|
||
</configuration> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.