The existing uses a synchronize block. See https://github.com/spring-projects/spring-boot/pull/8650#issuecomment-287809311

Comment From: wilkinsona

If we don't care too much about the order of the traces then here's a possible implementation of a non-blocking, circular buffer that could be used by a TraceRepository implementation: https://github.com/wilkinsona/spring-boot/tree/gh-8679.

Comment From: gaganis

Hello,

I have made an attempt to implement a lock free implementation for the InMemoryTraceRepository. Unfortunately my implementation did not perform well so no progress came out from that work. :disappointed:

I have created some JMH benchmarks, and I decided to share them, hoping they might prove useful to someone else. I have published them here: https://github.com/gaganis/trace-repo-perftest

some notes... - The circular buffer proposed by @wilkinsona performs impressively well - kudos!. I expect that many users will use the trace repository to debug applications and looking at the latest ones. So even for 100 entries it would be difficult to locate what they are looking for. So IMHO I believe that order is important for this feature. - I have not thoroughly reasoned about nor tested the correctness of my implementation, but since it is not performing there is not point to do that. - This is the first time I have used JMH so the setup the benchmarks and my interpretations of the results might not be correct.


A small description of the ideas behind my implementation:

The idea of my implementation was to replace the non-thread safe LinkedList with a ConcurrentLinkedDeque and remove the synchronized blocks. To keep the internal capacity in check I used an AtomicInteger as a counter for the entries that are currently stored and removed the extra elements whenever the store becomes bigger than capacity.

To ensure that findAll would have access to at least capacity number of elements the add method defers removal, which is then done by findAll. So adds that run while a find is running will add elements and not block.

On the other hand only one thread can run findAll and only when adds are not removing elements.

Thanks for reading! Giorgos


PS here is a sample benchmark summary :smile:

# Run complete. Total time: 00:01:32

Benchmark                                                      Mode  Cnt           Score           Error  Units
BlockingInMemoryBenchmark.g                                   thrpt    5    10584245.101 ±   1009913.120  ops/s
BlockingInMemoryBenchmark.g:testAdd                           thrpt    5    10555367.910 ±   1004002.421  ops/s
BlockingInMemoryBenchmark.g:testFind                          thrpt    5       28877.192 ±     13163.471  ops/s
CircularBufferInMemoryBenchmark.g                             thrpt    5    25514885.289 ±   1843593.821  ops/s
CircularBufferInMemoryBenchmark.g:testAdd                     thrpt    5    25476595.201 ±   1826544.420  ops/s
CircularBufferInMemoryBenchmark.g:testFind                    thrpt    5       38290.087 ±     18251.569  ops/s
LockFreeInMemoryBenchmark.g                                   thrpt    5     8124761.537 ±   1651855.995  ops/s
LockFreeInMemoryBenchmark.g:testAdd                           thrpt    5     8107624.543 ±   1657146.079  ops/s
LockFreeInMemoryBenchmark.g:testFind                          thrpt    5       17136.995 ±      6305.657  ops/s
noncontention.BlockingInMemoryNoContentionBenchmark.testAdd   thrpt    5    89548233.963 ±   8708153.372  ops/s
noncontention.BlockingInMemoryNoContentionBenchmark.testFind  thrpt    5   117178593.048 ±  15751186.156  ops/s
noncontention.CircularBufferNoContentionBenchmark.testAdd     thrpt    5    71260826.010 ±   9124265.148  ops/s
noncontention.CircularBufferNoContentionBenchmark.testFind    thrpt    5  3383888455.416 ± 423124043.662  ops/s
noncontention.LockFreeNoContentionBenchmark.testAdd           thrpt    5    41675362.606 ±   3174082.398  ops/s
noncontention.LockFreeNoContentionBenchmark.testFind          thrpt    5     2294318.197 ±     80518.053  ops/s

Comment From: wilkinsona

Given that we now disable tracing by default and strongly encourage the use of a "proper" tracing solution outside of a development environment, I don't think we need this any more.