-
Notifications
You must be signed in to change notification settings - Fork 934
Double query translation #1547
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
Double query translation #1547
Conversation
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.
Typos.
Any tests required?
@@ -68,6 +68,14 @@ public NhLinqExpression(Expression expression, ISessionFactoryImplementor sessio | |||
|
|||
public IASTNode Translate(ISessionFactoryImplementor sessionFactory, bool filter) | |||
{ | |||
if (ExpressionToHqlTranslationResults != null) | |||
{ | |||
// Query has already been translated. Arguments do not really matters, because queries are anyway tied |
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.
matter
(no "s")
if (ExpressionToHqlTranslationResults != null) | ||
{ | ||
// Query has already been translated. Arguments do not really matters, because queries are anyway tied | ||
// to a single session factory and cannot switch from being a filter query (query on mapped collection) |
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.
"on a mapped collection"
Typos fixed, thanks.
Besides running existing ones and seeing they still succeed, no additional ones. |
49ee027
to
e56b626
Compare
Cases which are re-using the translation were failing because the query plan compilation alters the supplied query translation ( Since the The more robust solution is then to yield a copy of the The other option would be to avoid mutating the input when building the query plan. But this looks like requiring many more code changes, with some cases which will anyway have to duplicate the whole |
Although I think the "raw data access bench" is not very suitable for checking impact of this change, I have still run it: no significant differences in timings. |
Still, better or worse? |
Below standard deviation, so cannot say. I will put the figures later. But It think a different bench is needed for really checking. Like one that runs queries having lightweight results in parallel and compute the average throughput. |
So that seems like this PR would be better for tests involving LINQ (first and third). |
9eae6fc
to
eb10b68
Compare
I have added a test fixture (set as explicit) adequate for benchmarking the change. Raw results:
So to compare things (run 2):
So cases optimized by the change (the two first) are actually optimized, while the case for which it can only be a penalty is not significantly impacted. (The apparent gain it even has with the PR is below sample standard deviation, so it is not significant.) |
eb10b68
to
6cb824a
Compare
6cb824a
to
53d81e3
Compare
Rebased. |
} | ||
|
||
[Test] | ||
public Task LinqWithListParameterPerfAsync() |
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.
Try-catch optimization in tests looks ugly. Maybe we need to consider option to disable it for tests.
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.
Why not, but I would see that as just a nice to have.
@hazzik, I think this PR should also be included in 5.1. |
// This is not much an issue anymore: ExpressionQueryImpl are currently created only with NhLinqExpression | ||
// which do cache their translation. By the way, the false transmitted below as filter parameter to | ||
// Translate is wrong, since this ExpandParameters may also be called from ExpressionFilterImpl. | ||
// But that does not matter for NhLinqExpression. | ||
var newTree = ParameterExpander.Expand(QueryExpression.Translate(Session.Factory, false), map); |
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.
ParameterExpander will duplicate the node again.
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.
It needs to duplicate each parameter node potentially many times, due to the possibility of having many aliases for the same parameter. So it would be awkward to change it for not duplicating by example for the last current parameter's alias while duplicating for previous ones.
Then the other option would be to avoid the duplication in Translate
specifically for this case. I am not sure it is worth it. (After all the benchmark still show a gain despite this unneeded duplication for this specific case.) But I may still add a commit for looking how it looks to implement this optimization.
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.
Well it will not look very good.
We need to either add a new interface method useful only for one of the three current implementations and meaningless for the others (and incurring the bloat of using at first an extension method for avoiding a breaking change), or to cast to NhLinqExpression
and use a specific method on it.
Additional commit done. All to be squashed together, or with second and/or third commit dropped if considered better not to have them.
a891fba
to
3d30fb3
Compare
Drop last commit. It's ugly. I still think that this can be done without need of additional clones. But I don't want to waste time now exploring these options. |
3d30fb3
to
cb77f4a
Compare
Dropped. (And it was not even exploiting the additional parameter...) |
NhLinqExpression instances may be translated multiple times in two cases: * When they have list parameters * When their plan is not cacheable, since it is looked-up two times in cache per execution, and (re)generated in case of cache miss.
It was not having any impact because concrete used implementations are currently not using its value.
cb77f4a
to
21dbe58
Compare
This is a kind of follow-up to #1544, for minimizing its potential impact on performances. But it also improves things for other cases.
(By the way, I have checked with the "raw data benchmark", #1544 was not having a measurable impact on it, even by tweaking it for using a Linq query with a non-parameterized constant. But the way it works, pulling a whole table without paging, the incurred IO are quite big and so it is not really surprising we cannot see the impact of a potential performance loss occurring only once per query.)
First commit rationals:
(Avoid re-translating NhLinqExpression)
Whatever the querying API, executing a query currently involves two steps:
First and last steps need the query plan, and these steps ask it from the plan cache, which will generate it if it is missing.
The first step needs it notably to initialize its parameter list. The last step of course needs it for executing the query.
So if a plan is not cacheable (which since #1544 may happen with LINQ queries), it will forcibly be regenerated at the execution step, instead of being retrieved from cache.
At the first step, the generated plan cannot be stored in the query for later re-use, because the query or its execution conditions may be altered and may require another plan for the query execution. This occurs:
But the query translation, done when generating a plan, can be re-used if the query expression instance is not changed for another one. So this PR alter
NhLinqExpression
, which was already storing its translation results, for yielding them again instead of recomputing them in case of laterTranslate
call.This change is not done for non-LINQ queries because they are
string
based, without a common object persisting from first plan look-up to second look-up. Anyway, they would less benefit of the change since non-LINQ query plans are currently always cacheable.By the way this change lighten an old "re-translation" concern occurring with queries having list parameters, see code comments changes.
Second commit:
(Fix a wrong parameter value for query translation)
The first commit, as you can read in its code comment, assumes a query is always translated for the same session factory and with the same
bool filter
parameter. This was not true forfilter
due to a latent bug causing this parameter to have a wrong value for some filter query case. But this bug was not having any impact because this parameter is currently unused.The second commit just fix that, for having a cleaner code. It currently changes nothing to the feature behavior, so we could also just drop that commit.