-
Notifications
You must be signed in to change notification settings - Fork 163
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
Fixes memory leak in prepared statement cache. Fixes #1143 #1157
base: main
Are you sure you want to change the base?
Conversation
Prepared statements were created on the server and cached as part of the "prepare cache." However, despite entries being removed from the in-memory cache, the evicted entries were not removed from Postgres itself. This modifies SemispaceCache to keep track of evicted elements. When a Session is recycled, we close any prepared statements that have been evicted.
modules/tests/shared/src/test/scala/data/SemispaceCacheTest.scala
Outdated
Show resolved
Hide resolved
def insert(k: K, v: V): SemispaceCache[K, V] = { | ||
if (max == 0) this.withEvicted(v :: evicted) // special case, can't insert! | ||
else if (gen0.size < max) SemispaceCache(gen0 + (k -> v), gen1, max, evicted) // room in gen0, done! | ||
else SemispaceCache(Map(k -> v), gen0, max, gen1.values.toList ::: evicted)// no room in gen0, slide it down |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there some way we can eagerly close the evicted gen 1 items here? Instead of waiting until session end?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know. I was trying to do that for a while but kept getting stuck on the best place to do it. I don't like adding a method to Session but it seems the easiest to ensure everything that is evicted is cleaned up.
For example, if someone used Session.prepare... how would I know when I could safely get rid of that? It's somewhat easier for the execute methods.
The prepare methods seem to conflict with this cache model. Before the cache, you had docs saying to prepare the queries ahead of time and then re-use them each session. But now they are getting cached both manually by the user and in the SemispaceCache. So you could imagine a bug where user has prepared cache size of 10, then they call
session.prepare { pq =>
...
}
Then they create 10 more prepared statements using execute. This pq
will get evicted from semispace cache and closed on the Postgres side and when they go to execute it again it will blow up.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't thought much about it yet but what about something like providing SemispaceCache
an onEvicted
callback? Then at least we know that any statements that have been evicted have also been closed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we could say methods that use Session.prepare/prepareR just aren't cached via SemispaceCache and then update the execute/option/stream methods to close any evicted statements immediately?
Prepared statements were created on the server and cached as part of the "prepare cache." However, despite entries being removed from the in-memory cache, the evicted entries were not removed from Postgres itself.
This modifies SemispaceCache to keep track of evicted elements. When a Session is recycled, we close any prepared statements that have been evicted.