-
Notifications
You must be signed in to change notification settings - Fork 115
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
Intermittent runtime StackOverflowError with multi-level sealed hierarchy #216
Comments
Thanks for the detailed writeup, @einholen! There is a certain amount global mutable state involved in running Magnolia, which is needed to persist some information between different invocations of the macro within the same compilation unit, but which unfortunately seems to persist between different invocations of the compiler. If there's more than one concurrent instance of the compiler handling compilations, then it can be even harder to diagnose. But it does seem like we're still carrying over some state between compilations which we shouldn't be. I'm a little surprised that the outcome isn't a compiler crash. That would be better than a runtime error, frankly... |
Ah I didn't know Magnolia (or macros in general) can carry over state between compilations. I think it makes sense. What I didn't mention because I thought it's irrelevant - we use Hydra in the project to speed up compilation by parallelizing it, so it probably contributes to the issue. I thought a clean compilation always fixes it but with this extra context from you I now think that it sometimes fixes it. We also don't use Hydra on CI and that could be why this problem never occurred in prod and I've only ever seen it when running unit tests locally. |
Oh yeah, expanding Magnolia macros in parallel is not well defined. |
Another option would be to try and use |
@propensive perhaps we should contact Triplequote to get a free licence for Magnolia and add it to our CI and try to reproduce the problem. WDYT? |
@einholen can you try the latest release? |
Posting a link to an IntelliJ specific issue with magnolia for posterity. The Scala compile server is backed by a thread pool which reuses long-running threads (whose thread local state never gets cleared), and having magnolia in the project seems to retain a large heap and could lead to OOM errors. I'm open to have a broader discussion about magnolia and the Scala compile server. cc @joroKr21 |
It might be as simple as clearing the local state at the end of macro expansion. |
I'm curious to try that out, from a practical standpoint, but I'm obviously not suggesting that any changes need to be done to the project, especially since I don't know whether that can be done safely and not have an adverse effect on the compilation, both in correctness and/or performance. On the other hand, I also don't particularly feel like making some changes, or adding exceptions on the IntelliJ side, at least not before we have a bit more knowledge on the matter. In any case, thanks for taking a look. |
@joroKr21 it seems the thread locals are cleaned ... magnolia/core/src/main/scala/magnolia1/magnolia.scala Lines 899 to 909 in 2a5d7ec
so it's probably sth more subtle than that :) |
Right I forgot about it. Maybe we also need to null out the thread locals themselves? But I doubt it. |
…ld magnolia. Intern pureconfig-magnolia into distage-config to update it. * Details softwaremill/magnolia#216
…void memory leak in old magnolia version Details: softwaremill/magnolia#216 (comment) Projects using pureconfig-magnolia cause IntelliJ compile server to run out of memory, but the leak seems to happen on old magnolia versions only.
…ld magnolia. Intern pureconfig-magnolia into distage-config to update it. * Details softwaremill/magnolia#216
…ld magnolia. Intern pureconfig-magnolia into distage-config to update it. (#1858) * Details softwaremill/magnolia#216
…void memory leak in old magnolia version (#1445) Details: softwaremill/magnolia#216 (comment) Projects using pureconfig-magnolia cause IntelliJ compile server to run out of memory, but the leak seems to happen on old magnolia versions only.
@joroKr21 removing thread locals instead of just cleaning May we release a new version with these changes? object Stack {
// Cheating to satisfy Singleton bound (which improves type inference).
private val dummyContext: whitebox.Context = null
private val threadLocalStack = ThreadLocal.withInitial[Stack[dummyContext.type]](() => new Stack[dummyContext.type])
private val threadLocalWorkSet = ThreadLocal.withInitial[mutable.Set[whitebox.Context#Symbol]](() => mutable.Set.empty)
def withContext(c: whitebox.Context)(fn: (Stack[c.type], Int) => c.Tree): c.Tree = {
val stack = threadLocalStack.get()
val workSet = threadLocalWorkSet.get()
workSet += c.macroApplication.symbol
val depth = c.enclosingMacros.count(m => workSet(m.macroApplication.symbol))
try fn(stack.asInstanceOf[Stack[c.type]], depth)
finally if (depth <= 1) {
threadLocalStack.remove()
threadLocalWorkSet.remove()
}
}
} |
@Caparow can you maybe create a PR with this change? will be easiest to review |
…void memory leak in old magnolia version (#1445) Details: softwaremill/magnolia#216 (comment) Projects using pureconfig-magnolia cause IntelliJ compile server to run out of memory, but the leak seems to happen on old magnolia versions only.
* Enable coverage for Scala 3 * Update Scala 3 to v3.2.2-RC1 for testing * Update enumeratum to 1.7.2 (#1426) * Update sbt-pgp to 2.2.1 (#1427) * Update joda-time to 2.12.2 (#1428) * Update cats-effect to 3.4.2 (#1430) * Update sbt-microsites to 1.4.0 (#1431) * Update slf4j-simple to 2.0.6 (#1433) * Update jekyll and html-proofer CI checks (#1444) * Update cats-effect to 3.4.4 (#1437) Co-authored-by: Rui Gonçalves <ruippeixotog@gmail.com> * Update model:core to 1.5.4 (#1438) * Update model:core to 1.5.4 * Update jekyll and html-proofer * Revert jekyll to original version * Revert jekyll to original version * Update ruby and jekyll * Fix htmlproofer arguments Co-authored-by: Rui Gonçalves <ruippeixotog@gmail.com> * Update sbt-microsites to 1.4.1 (#1439) Co-authored-by: Rui Gonçalves <ruippeixotog@gmail.com> * Update sbt to 1.8.2 (#1442) Co-authored-by: Rui Gonçalves <ruippeixotog@gmail.com> * Update scalatest to 3.2.15 (#1443) Co-authored-by: Rui Gonçalves <ruippeixotog@gmail.com> * Update http4s-core to 0.23.17 (#1441) * pureconfig-magnolia: Update magnolia to softwaremill.magnolia1_2 to avoid memory leak in old magnolia version (#1445) Details: softwaremill/magnolia#216 (comment) Projects using pureconfig-magnolia cause IntelliJ compile server to run out of memory, but the leak seems to happen on old magnolia versions only. * Add http4s022 compatibility module (#1421) * add http4s022 module * update docs * fix README.md * remove ip4s dependency from pureconfig-http4s module Co-authored-by: Rui Gonçalves <ruippeixotog@gmail.com> * Update http4s-core to 0.22.15 (#1448) * Update magnolia to 1.1.3 (#1447) * Update cats-effect to 3.4.5 (#1452) * Update joda-convert to 2.2.3 (#1451) * Update http4s-core to 0.23.18 (#1450) * Update fs2-core, fs2-io to 3.5.0 (#1449) * Update scalafmt-core to 3.7.0 (#1453) * Update scalafmt-core to 3.7.1 (#1454) * Update sbt-sonatype to 3.9.17 (#1455) * Update mdoc, sbt-mdoc to 2.3.7 (#1456) * Update model:core to 1.5.5 (#1457) --------- Co-authored-by: Scala Steward <43047562+scala-steward@users.noreply.github.com> Co-authored-by: Kai <450507+neko-kai@users.noreply.github.com> Co-authored-by: Sergey Torgashov <satorg@users.noreply.github.com>
Hi! I'm intermittently getting a
StackOverflowError
at runtime because the Magnolia macro resolves itself implicitly instead of the actual concrete type class.I saw #66 and #190 but they seem to be different, so creating a new issue. Feel free to close if it's a dupe and unresolvable in Scala 2 🙂
So there's a sealed trait with a bunch of sealed subtraits each of which has a bunch of concrete case classes underneath. We use magnolia to dispatch specific type classes based on an instance of the sealed trait at runtime. We don't use
combine
, onlydispatch
. All branches of the hierarchy terminate, and each concrete class has an implicit defined that Magnolia is supposed to find.With a hierarchy like this
and implicits for concrete case classes defined in a separate compilation unit from the Magnolia macro
Calling
implicitly[Show[Base]].show(Concrete2("hello"))
sometimes results in a runtimeStackOverflowError
.After a clean compilation the error is gone, but changes to adjacent code seem to sometimes resurrect it.
What I'm seeing in debug inside
sealedTrait.dispatch
is,subtypesArray
containsConcrete1
andSub2
. A value ofConcrete2
thus matchesSub2
, and thensubtype.typeclass
resolves toShow.gen
again and again instead of recursing inside the sealed trait and finding the actual concrete implicit.After a clean compilation,
subtypesArray
does not have any traits and properly has all case classes.I could partially solve the problem by making the
Show.gen
non-implicit and calling it directly where needed - apparently without itself available as a "catch-all" implicit it understands that it needs to go deeper into the sealed hierarchy, and lands intodispatch
twice: forSub2
and then forConcrete2
.Scastie workbook with the full code: https://scastie.scala-lang.org/Hou8fzlAR0anAPeUHvWpFw
I couldn't reproduce the problem in Scastie - It works as intended, most likely because everything is in one file. We can emulate a runtime stackOverflow in this workbook, however, if we remove one of the concrete implicits (it won't be exactly the case I'm describing above but it would follow the same implicit resolution patterns as in my intermittent problem).
The text was updated successfully, but these errors were encountered: