-
Notifications
You must be signed in to change notification settings - Fork 67
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
0.7.1 unnamed prepared statements aren't closed #200
Comments
Hi @eraserhd, Thanks for the report. There are a couple things that come to mind when reading your message:
So given all this, it seems likely your app is generating a lot of different named prepared statements. We can try to help you reduce that but we'd need to know more about the types of queries you are using. This tends to happen when placeholders are not being used. For example you are doing |
It might also be the case that using |
On an additional note, if you don't want to use |
The word
Ahh, good to know. The primary bit of code that builds the queries from the GraphQL filters is this: def filter_with(query, module, filters) do
with {:ok, query, field_map} <- add_all_fields(query, module, filters) do
expr =
filters
|> normalize()
|> cata(&remove_object_clauses/1)
|> cata(fn
%{op: :and, clauses: clauses} ->
build_logical_grouping(clauses, true, &dynamic(^&2 and ^&1))
%{op: :or, clauses: clauses} ->
build_logical_grouping(clauses, false, &dynamic(^&2 or ^&1))
%{op: op, field: field} = filter ->
{binding, column} = Map.get(field_map, field)
value = module.query_value(value(filter), field)
case op do
:eq ->
dynamic([{^binding, b}], field(b, ^column) == ^value)
:neq ->
dynamic([{^binding, b}], field(b, ^column) != ^value)
:gt ->
dynamic([{^binding, b}], field(b, ^column) > ^value)
:lt ->
dynamic([{^binding, b}], field(b, ^column) < ^value)
:gte ->
dynamic([{^binding, b}], field(b, ^column) >= ^value)
:lte ->
dynamic([{^binding, b}], field(b, ^column) <= ^value)
:match ->
dynamic([{^binding, b}], fragment("? LIKE ?", field(b, ^column), ^value))
:in ->
dynamic([{^binding, b}], fragment("? IN (?)", field(b, ^column), splice(^value)))
:nin ->
dynamic(
[{^binding, b}],
fragment("? NOT IN (?)", field(b, ^column), splice(^value))
)
end
end)
from(query, where: ^expr)
else
err -> err
end
end It seems like we can't just add Is there any way to not sacrifice the performance of the dozens of other, non-dynamic queries? |
Setting it to prepare: :unnamed is the way to go. Perhaps open up an issue in Absinthe to make this customizable somehow? |
@eraserhd We have added the ability to add This means you could do |
Thanks! I like this. Will test it out today. |
This is working for us. The queries we want cached are cached, the ones we don't aren't. I'm OK with closing this issue if you are. |
Thanks for testing, we're glad it is working :). I'll close this issue but please feel free to reach out again if anything else pops up. |
We've just had to reduce our connection pool sizes because we hit the prepared statement limit.
Observations:
Ecto.all
, and we have a GraphQL interface that allows arbitrary filters and heavily usesdynamic
, so we have a potentially unbounded number of distinct queries over time.Conclusion:
We will eventually need to restart our system or the database, as we will eventually have too many prepared statements.
This is a snapshot about an hour after reducing the connections and restarting. There are 6 pods, each with a connection pool limit of 100 connections each for writing and for reading (we don't have a read replica any more, but the code is still there)... so the 1200 and 600 counts are statements prepared for every connection.
I'm not sure why the 1762 query is not capped at 1200. Maybe because it uses
dynamic
?All prepared statements are unnamed, at least according to MySQL:
Perhaps there needs to be an LRU for prepared statements? It seems like we don't want to close them after every query.
Thoughts?
The text was updated successfully, but these errors were encountered: