-
Notifications
You must be signed in to change notification settings - Fork 346
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
NormalizeCaching cache grows without bound #2484
Comments
As an aside, I was able to get the dynamic query to compile statically by removing a type ascription to I will report back if removing the dynamic query resolves the memory leak. I'm optimistic it will. |
For reference "don't ascribe types to quotations" best practice is reference in the docs, although not that loudly. https://github.com/zio/zio-quill#compile-time-quotations |
And in other news, the root cause was indeed the dynamic query. Now that it's static the memory usage is completely stable. I'd argue this issue should still be fixed, however, it's quite unexpected for a library such as quill to have a fixed and growing multi-gb memory footprint. |
I think this is a perfect use-case for ZIO cache since it has a fixed size eviction ordering. Will mention this to @adamgfraser who will hopefully have some helpful advice. |
I've used a lot of dynamic query and haven't see the OOM yet. |
@deusaquilus The query looks like the following. Note that removing the errant type ascription on
|
I'll take a look at it. |
Can you provide a minimal self-contained reproduce example? I try you example but it lack some context and won't compile |
I also saw a similar issue with |
We've been hit by this bug. Complex dynamic query with 3 After rewrite to compiled query the issue was resolved. |
I'm having the same issue. My dynamic query is quite simple: just update the row based on whether the passed in parameters are None or not: override def update(id: ID, updater: SourceUpdater): IO[Boolean] = {
Clock[IO].realTimeInstant.flatMap { nowInstant =>
val now = ZonedDateTime.ofInstant(nowInstant, ZoneId.systemDefault())
val q = dynamicQuery[Source]
.filter(_.id == lift(id))
.update(
setOpt(_.description, updater.description),
setOpt(_.fetchCompletedAt, updater.fetchCompletedAt),
setOpt(_.fetchStatus, updater.fetchStatus),
setOpt(_.fetchDelayMillis, updater.fetchDelayMillis),
setOpt(_.fetchFailedMsg, updater.fetchFailedMsg),
setOpt(_.fetchScheduledAt, updater.fetchScheduledAt),
setOpt(_.htmlUrl, updater.htmlUrl),
setOpt(_.title, updater.title),
setOpt(_.fetchStartedAt, updater.fetchStartedAt),
setOpt(_.articleOrder, updater.articleOrder),
setOpt(_.iconUrl, updater.iconUrl),
setOpt(_.updatedAt, Some(now)),
setOpt(_.fetchErrorCount, updater.fetchErrorCount),
setOpt(_.showTitle, updater.showTitle),
setOpt(_.showMedia, updater.showMedia),
setOpt(_.showFullArticle, updater.showFullArticle),
)
run(q).transact(xa).map(_ > 0)
}
} |
This fixes zio#2484. Added a capacity to normalize cache. Default to 40960 and can be override by `quill.query.cacheDynamicMaxSize`. Added a new dependency Google Guava to use its cache implementation. I know it's better to avoid new dependency but Google Guava is a pretty widely adopted library. It's cache implementation is widely tested. For such a critical use case in quill, I think it's better to have a widely tested implementation than reinventing the wheel.
This fixes zio#2484. Added a capacity to normalize cache. Default to 40960 and can be override by `quill.query.cacheDynamicMaxSize`. Added a new dependency Google Guava to use its cache implementation. I know it's better to avoid new dependency but Google Guava is a pretty widely adopted library. It's cache implementation is widely tested. For such a critical use case in quill, I think it's better to have a widely tested implementation than reinventing the wheel.
This fixes zio#2484. Added a capacity to normalize cache. Default to 1024 and can be override by `quill.query.cacheDynamicMaxSize`. Added a new dependency Google Guava to use its cache implementation. I know it's better to avoid new dependency but Google Guava is a pretty widely adopted library. It's cache implementation is widely tested. For such a critical use case in quill, I think it's better to have a widely tested implementation than reinventing the wheel.
This fixes zio#2484. Added a capacity to normalize cache. Default to 1024 and can be override by `quill.query.cacheDynamicMaxSize`. Added a new dependency Caffeine to use its cache implementation, which is recommended by Guava. For a critical use case in quill, I think it's better to have a widely tested implementation than reinventing the wheel.
This fixes zio#2484. Added a capacity to normalize cache. Default to 1024 and can be override by `quill.query.cacheDynamicMaxSize`. Added a new dependency Caffeine to use its cache implementation, which is recommended by Guava. For a critical use case in quill, I think it's better to have a widely tested implementation than reinventing the wheel.
This fixes zio#2484. Added a capacity to normalize cache. Default to 1024 and can be override by `quill.query.cacheDynamicMaxSize`. Added a new dependency Caffeine to use its cache implementation, which is recommended by Guava. For a critical use case in quill, I think it's better to have a widely tested implementation than reinventing the wheel.
This fixes zio#2484. Added a capacity to normalize cache. Default to 1024 and can be override by `quill.query.cacheDynamicMaxSize`. Added a new dependency Caffeine to use its cache implementation, which is recommended by Guava. For a critical use case in quill, I think it's better to have a widely tested implementation than reinventing the wheel.
This fixes #2484. Added a capacity to normalize cache. Default to 1024 and can be override by `quill.query.cacheDynamicMaxSize`. Added a new dependency Caffeine to use its cache implementation, which is recommended by Guava. For a critical use case in quill, I think it's better to have a widely tested implementation than reinventing the wheel. Co-authored-by: Jules Ivanic <guizmaii@users.noreply.github.com>
This fixes #2484. Added a capacity to normalize cache. Default to 1024 and can be override by `quill.query.cacheDynamicMaxSize`. Added a new dependency Caffeine to use its cache implementation, which is recommended by Guava. For a critical use case in quill, I think it's better to have a widely tested implementation than reinventing the wheel. Co-authored-by: Jules Ivanic <guizmaii@users.noreply.github.com>
Version: 3.16.5
Module: quill-jdbc-zio
Database: postgres
Expected behavior
JVM does not run OOM after running a lot of dynamic queries
Actual behavior
JVM runs OOM
Steps to reproduce the behavior
I've somehow managed to construct a dynamic query. My assumption is that because the query is dynamic, it's being compiled and run through NormalizedCaching and sticking some changing element (probably a date range from the query) into the cache. This is all fine except the cache never evicts anything and the jvm runs out of heap.
Workaround
Don't use dynamic queries?
Notes
I assume the thing to do here is put a bound on the cache size, possibly even evicting the entire thing in case the bound is met. This seems like a sufficient edge case that it's not worth introducing a proper LRU cache. The ConcurrentHashMap at
zio-quill/quill-engine/src/main/scala/io/getquill/norm/NormalizeCaching.scala
Line 7 in 52011e6
An alternative solution would be to somehow prevent high entropy nodes from going into the cache. Not sure what this would look like as of yet, but I'm going to try (again) to make this query compile statically and may learn something in the process.
@getquill/maintainers
The text was updated successfully, but these errors were encountered: