-
Notifications
You must be signed in to change notification settings - Fork 464
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Spotless in a long-running multi-threaded JVM #1555
Comments
TL;DR: I won't accept a thread-safety requirement for all of Spotless. I'm happy to merge thread-safety fixes and tests to keep them fixed for any subset of our formatters. In the long run, my advice would be to handle non-threadsafe formatters, but either way could work.
We could put a thread-safe requirement on any given FormatterStep, but unless we ran threading stress tests on every formatter in our unit test suite, they would not actually stay thread-safe. That requirement would also raise the difficulty level for PR authors looking to add new formatters. I am fine with making all/a-subset-of existing formatters thread safe, and I am fine with adding tests which keep them thread safe. I do not want a contributor adding a new step to have thread-safety as a requirement. The build plugins parallelize work in other ways, such that we can get good performance without requiring each step to be thread safe. So the burden for keeping things thread-safe will always fall on the consumers who need that feature. But once you add a thread-safe-stress-test for a given step, the rest of the project can keep it running.
We should absolutely do this, but it is blocked on #1524.
I would recommend some kind of batching system, e.g.
and maybe restart the whole thing every N minutes to deal with the JDT metaspace issue. |
Totally fine with me. The a priori lack of thread-safety was more a mere observation, ground for the rest of the analysis. My initial strategy was to build a set of Formatters per Thread, to execute Spotless in a seemingly mono-thread fashion. However, it led to Metaspace issues. Hence the suggestion to be able to share a JarState through multiple Formatters (but, as described later, this is generally irrelevant given SpotlessCache and ClassLoaders caching).
In my case, a single JVM would process different stylesheets/repositories. Hence, I (supposedly) needs multiple Formatters.
This would be the last resort.
I had a look, but I do not get how it relates with closing Eclipse resources. I'm fine waiting #1524 to be completed before considering enabling proper closing of Eclipse resources. |
Digging into this, I feel more and more confident this is a cache matter. The The equivalent behavior can be observed with plugin-maven in case of a stylesheet not available as a File (e.g. but available as a Resource from a dependency): see |
The behavior is drastically improved in my case with a cache
Would this be relevant to be introduced in |
I am always in favor of content-addressing, PR for that is welcome! |
The main issue is fixed, and is confirmed as due to cache behavior (based on File, instead of content). |
Here, I consider Spotless use through CleanThat
First, we try to process files in parallel. It lead to issues with some FormatterFunc which are not thread-safe. Typical example:
org.eclipse.jdt.core.formatter.CodeFormatter
which is not thread-safe.Then, we considered instantiating Spotless formatters on a per-thread basis. However, it lead to Metaspace issues (
java.lang.OutOfMemoryError: Metaspace
). This is quite normal as we end duplicating each Class (e.g. the whole Eclipse JDT eco-system) for each working thread.While investigating this Metaspace issue, we spot that Eclipse starts a bunch of Thread (for each ClassLoader), hence remaining in RAM, hence maintaining a reference to the whole JarState.
Typical stacks:
1:
2:
3:
We considered calling
FormatterStepImpl.Standard
->FormatterFunc.Closeable
(e.g. to callorg.eclipse.core.internal.jobs.JobManager.shutdown()
). However, it seems not implemented in the EclipseJdt case.We understand it could be fixed through:
com.diffplug.spotless.extra.java.EclipseJdtFormatterStep#getFormatterFunc
to add theFormatterFunc.Closeable
facet.com.diffplug.spotless.Jvm.Support#suggestLaterVersionOnError
to transfer the optionalFormatterFunc.Closeable
facet.Is our approach correct? Do you have recommendations to rely on Spotless on a multithread+long-running environnement?
We may rather work in making each formatter thread-safe (which may not be achievable/quite difficult with some formatters (e.g. Prettier ?)) ?
Should we rather consider being able to share a JarState through multiple instance of the same formatterFunction ?
Thanks
The text was updated successfully, but these errors were encountered: