Skip to content

Protect against concurrent reads/writes to Context keyvalues - #5739

Merged
shakuzen merged 2 commits into
micrometer-metrics:1.14.xfrom
shakuzen:concurrent-keyvalues-modification
Dec 10, 2024
Merged

shakuzen merged 2 commits into
micrometer-metrics:1.14.xfrom
shakuzen:concurrent-keyvalues-modification

Conversation

@shakuzen

Copy link
Copy Markdown
Member

@shakuzen
shakuzen force-pushed the concurrent-keyvalues-modification branch from 3cf9063 to fd01cfb Compare December 10, 2024 08:53
@shakuzen
shakuzen changed the base branch from main to 1.14.x December 10, 2024 08:53
@shakuzen shakuzen added this to the 1.14.2 milestone Dec 10, 2024
dependencies {
implementation project(":micrometer-core")
// implementation("io.micrometer:micrometer-core:1.12.4")
implementation project(":micrometer-test")

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This dependency was causing a weird ClassNotFoundException when running jcstress from IntelliJ, but it is only here for the @Issue annotation. We do not do any processing of the annotation, so I'm not sure there's any more value of it being an actual annotation vs a comment - I've commented it out where it was used in this module.

@Benchmark
@Group("contended")
@GroupThreads(1)
public KeyValues contendedRead() {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The contended group will share the threads configured for the benchmark in proportion to the specified @GroupThreads. So in this current setup there will be two execution groups for contended, each with 2 threads, one thread reading and one thread writing concurrently.

@shakuzen
shakuzen force-pushed the concurrent-keyvalues-modification branch from fd01cfb to a6b2eb1 Compare December 10, 2024 08:59
This avoids the issues with concurrent reads and writes of the keyvalues on the Context. Reverts the previous change to KeyValues as it is not needed with CHM.
@shakuzen
shakuzen force-pushed the concurrent-keyvalues-modification branch from a6b2eb1 to d057a34 Compare December 10, 2024 09:07
@shakuzen
shakuzen merged commit 4f534a7 into micrometer-metrics:1.14.x Dec 10, 2024
@shakuzen
shakuzen deleted the concurrent-keyvalues-modification branch December 10, 2024 09:18
shakuzen added a commit that referenced this pull request Dec 12, 2024
This scenario produced a NPE before the change from using a LinkedHashMap to ConcurrentHashMap.

See gh-5739
@shakuzen

Copy link
Copy Markdown
Member Author

I added a jcstress test for concurrent get/remove on a keyvalue (503f2e9), as this scenario was producing the NPE reported in the linked issue.

@shakuzen

Copy link
Copy Markdown
Member Author

And sharing benchmark results from my machine (MBP M1). There was not much of a noticeable difference and at the level of nanoseconds, the run-to-run results can vary as much as the differences here.

With these changes using ConcurrentHashMap:

Benchmark                                                         Mode      Cnt       Score   Error  Units
ObservationKeyValuesBenchmark.contended                         sample  6816019     140.191 Β± 0.406  ns/op
ObservationKeyValuesBenchmark.contended:contendedRead           sample  2979345     133.465 Β± 0.694  ns/op
ObservationKeyValuesBenchmark.contended:contendedRead:p0.00     sample                  β‰ˆ 0          ns/op
ObservationKeyValuesBenchmark.contended:contendedRead:p0.50     sample              125.000          ns/op
ObservationKeyValuesBenchmark.contended:contendedRead:p0.90     sample              250.000          ns/op
ObservationKeyValuesBenchmark.contended:contendedRead:p0.95     sample              250.000          ns/op
ObservationKeyValuesBenchmark.contended:contendedRead:p0.99     sample              375.000          ns/op
ObservationKeyValuesBenchmark.contended:contendedRead:p0.999    sample              459.000          ns/op
ObservationKeyValuesBenchmark.contended:contendedRead:p0.9999   sample            14784.000          ns/op
ObservationKeyValuesBenchmark.contended:contendedRead:p1.00     sample           153856.000          ns/op
ObservationKeyValuesBenchmark.contended:contendedWrite          sample  3836674     145.414 Β± 0.480  ns/op
ObservationKeyValuesBenchmark.contended:contendedWrite:p0.00    sample                  β‰ˆ 0          ns/op
ObservationKeyValuesBenchmark.contended:contendedWrite:p0.50    sample              125.000          ns/op
ObservationKeyValuesBenchmark.contended:contendedWrite:p0.90    sample              250.000          ns/op
ObservationKeyValuesBenchmark.contended:contendedWrite:p0.95    sample              250.000          ns/op
ObservationKeyValuesBenchmark.contended:contendedWrite:p0.99    sample              375.000          ns/op
ObservationKeyValuesBenchmark.contended:contendedWrite:p0.999   sample              458.000          ns/op
ObservationKeyValuesBenchmark.contended:contendedWrite:p0.9999  sample            12255.960          ns/op
ObservationKeyValuesBenchmark.contended:contendedWrite:p1.00    sample           116480.000          ns/op
ObservationKeyValuesBenchmark.contended:p0.00                   sample                  β‰ˆ 0          ns/op
ObservationKeyValuesBenchmark.contended:p0.50                   sample              125.000          ns/op
ObservationKeyValuesBenchmark.contended:p0.90                   sample              250.000          ns/op
ObservationKeyValuesBenchmark.contended:p0.95                   sample              250.000          ns/op
ObservationKeyValuesBenchmark.contended:p0.99                   sample              375.000          ns/op
ObservationKeyValuesBenchmark.contended:p0.999                  sample              458.000          ns/op
ObservationKeyValuesBenchmark.contended:p0.9999                 sample            13696.000          ns/op
ObservationKeyValuesBenchmark.contended:p1.00                   sample           153856.000          ns/op
ObservationKeyValuesBenchmark.uncontendedRead                   sample  1634837      21.613 Β± 0.825  ns/op
ObservationKeyValuesBenchmark.uncontendedRead:p0.00             sample                  β‰ˆ 0          ns/op
ObservationKeyValuesBenchmark.uncontendedRead:p0.50             sample                  β‰ˆ 0          ns/op
ObservationKeyValuesBenchmark.uncontendedRead:p0.90             sample               42.000          ns/op
ObservationKeyValuesBenchmark.uncontendedRead:p0.95             sample               42.000          ns/op
ObservationKeyValuesBenchmark.uncontendedRead:p0.99             sample               42.000          ns/op
ObservationKeyValuesBenchmark.uncontendedRead:p0.999            sample              166.000          ns/op
ObservationKeyValuesBenchmark.uncontendedRead:p0.9999           sample             1708.000          ns/op
ObservationKeyValuesBenchmark.uncontendedRead:p1.00             sample           380416.000          ns/op
ObservationKeyValuesBenchmark.uncontendedWrite                  sample  1401661      23.337 Β± 0.350  ns/op
ObservationKeyValuesBenchmark.uncontendedWrite:p0.00            sample                  β‰ˆ 0          ns/op
ObservationKeyValuesBenchmark.uncontendedWrite:p0.50            sample               41.000          ns/op
ObservationKeyValuesBenchmark.uncontendedWrite:p0.90            sample               42.000          ns/op
ObservationKeyValuesBenchmark.uncontendedWrite:p0.95            sample               42.000          ns/op
ObservationKeyValuesBenchmark.uncontendedWrite:p0.99            sample               42.000          ns/op
ObservationKeyValuesBenchmark.uncontendedWrite:p0.999           sample              166.000          ns/op
ObservationKeyValuesBenchmark.uncontendedWrite:p0.9999          sample             2083.335          ns/op
ObservationKeyValuesBenchmark.uncontendedWrite:p1.00            sample            73344.000          ns/op

Before these changes (1.14.1) using LinkedHashMap:

Benchmark                                                         Mode      Cnt        Score   Error  Units
ObservationKeyValuesBenchmark.contended                         sample  6777815      143.705 Β± 1.565  ns/op
ObservationKeyValuesBenchmark.contended:contendedRead           sample  2938907      138.407 Β± 1.226  ns/op
ObservationKeyValuesBenchmark.contended:contendedRead:p0.00     sample                   β‰ˆ 0          ns/op
ObservationKeyValuesBenchmark.contended:contendedRead:p0.50     sample               125.000          ns/op
ObservationKeyValuesBenchmark.contended:contendedRead:p0.90     sample               250.000          ns/op
ObservationKeyValuesBenchmark.contended:contendedRead:p0.95     sample               250.000          ns/op
ObservationKeyValuesBenchmark.contended:contendedRead:p0.99     sample               333.000          ns/op
ObservationKeyValuesBenchmark.contended:contendedRead:p0.999    sample               417.000          ns/op
ObservationKeyValuesBenchmark.contended:contendedRead:p0.9999   sample             19206.989          ns/op
ObservationKeyValuesBenchmark.contended:contendedRead:p1.00     sample            432128.000          ns/op
ObservationKeyValuesBenchmark.contended:contendedWrite          sample  3838908      147.762 Β± 2.598  ns/op
ObservationKeyValuesBenchmark.contended:contendedWrite:p0.00    sample                   β‰ˆ 0          ns/op
ObservationKeyValuesBenchmark.contended:contendedWrite:p0.50    sample               125.000          ns/op
ObservationKeyValuesBenchmark.contended:contendedWrite:p0.90    sample               250.000          ns/op
ObservationKeyValuesBenchmark.contended:contendedWrite:p0.95    sample               250.000          ns/op
ObservationKeyValuesBenchmark.contended:contendedWrite:p0.99    sample               333.000          ns/op
ObservationKeyValuesBenchmark.contended:contendedWrite:p0.999   sample               417.000          ns/op
ObservationKeyValuesBenchmark.contended:contendedWrite:p0.9999  sample             15248.000          ns/op
ObservationKeyValuesBenchmark.contended:contendedWrite:p1.00    sample           2068480.000          ns/op
ObservationKeyValuesBenchmark.contended:p0.00                   sample                   β‰ˆ 0          ns/op
ObservationKeyValuesBenchmark.contended:p0.50                   sample               125.000          ns/op
ObservationKeyValuesBenchmark.contended:p0.90                   sample               250.000          ns/op
ObservationKeyValuesBenchmark.contended:p0.95                   sample               250.000          ns/op
ObservationKeyValuesBenchmark.contended:p0.99                   sample               333.000          ns/op
ObservationKeyValuesBenchmark.contended:p0.999                  sample               417.000          ns/op
ObservationKeyValuesBenchmark.contended:p0.9999                 sample             16512.000          ns/op
ObservationKeyValuesBenchmark.contended:p1.00                   sample           2068480.000          ns/op
ObservationKeyValuesBenchmark.uncontendedRead                   sample  1663755       18.553 Β± 0.231  ns/op
ObservationKeyValuesBenchmark.uncontendedRead:p0.00             sample                   β‰ˆ 0          ns/op
ObservationKeyValuesBenchmark.uncontendedRead:p0.50             sample                   β‰ˆ 0          ns/op
ObservationKeyValuesBenchmark.uncontendedRead:p0.90             sample                42.000          ns/op
ObservationKeyValuesBenchmark.uncontendedRead:p0.95             sample                42.000          ns/op
ObservationKeyValuesBenchmark.uncontendedRead:p0.99             sample                42.000          ns/op
ObservationKeyValuesBenchmark.uncontendedRead:p0.999            sample               125.000          ns/op
ObservationKeyValuesBenchmark.uncontendedRead:p0.9999           sample              1400.225          ns/op
ObservationKeyValuesBenchmark.uncontendedRead:p1.00             sample             69760.000          ns/op
ObservationKeyValuesBenchmark.uncontendedWrite                  sample  1401784       23.264 Β± 0.479  ns/op
ObservationKeyValuesBenchmark.uncontendedWrite:p0.00            sample                   β‰ˆ 0          ns/op
ObservationKeyValuesBenchmark.uncontendedWrite:p0.50            sample                41.000          ns/op
ObservationKeyValuesBenchmark.uncontendedWrite:p0.90            sample                42.000          ns/op
ObservationKeyValuesBenchmark.uncontendedWrite:p0.95            sample                42.000          ns/op
ObservationKeyValuesBenchmark.uncontendedWrite:p0.99            sample                42.000          ns/op
ObservationKeyValuesBenchmark.uncontendedWrite:p0.999           sample               166.000          ns/op
ObservationKeyValuesBenchmark.uncontendedWrite:p0.9999          sample              1958.000          ns/op
ObservationKeyValuesBenchmark.uncontendedWrite:p1.00            sample            132608.000          ns/op

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants