-
Notifications
You must be signed in to change notification settings - Fork 934
Fix Criteria caching filtered collections #460
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
Fix Criteria caching filtered collections #460
Conversation
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
2d5245a
to
7d53824
Compare
src/NHibernate.Test/NHSpecificTest/NH3848/CriteriaTestFixture.cs
Outdated
Show resolved
Hide resolved
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.
This sounds good in principle. But binary breaking changes have to be avoided, and there is a bunch of formatting issues.
|
||
if (subcriteria.HasRestrictions && subcriteria.JoinType == JoinType.LeftOuterJoin) | ||
{ | ||
var collectionName = $"{rootCriteria.EntityOrClassName}.{subcriteria.Path}"; |
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.
This place looks hacky to me:
- We already have place where we collect all query collection persisters (see
CreateCriteriaCollectionPersisters
). I think it's a good place to collect this skip cache collection flags. And maybe collect them similarly tocriteriaCollectionPersisters
inuncacheableCollectionPersisters
- We should not rely on Left join anymore as now we support fetching collections with inner join too (with
SelectMode.Fetch
).
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 seems to me, ShouldFetchCollectionPersister()
is exactly what we need to determine whether collection will be fetched or not.
From other side, we can map HasRestrictions
flags from subcriteries to assotiations, to determine if persister should be cached or not.
That is why I have moved initialization of uncacheableCollectionPersisters
to CriteriaJoinWalker
.
Is it ok for you?
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.
By "hacky" I meant we shouldn't use "magic strings" like $"{rootCriteria.EntityOrClassName}.{subcriteria.Path}"
. Will it work for multilevel fetch like obj.Child.Collection
(it seems we need a test for this case, maybe it will work but it's not obvious to me)?
So instead I think it's better to collect directly all collection persisters ICollectionPersister
with restrictions. That's why I suggested to look in to CreateCriteriaCollectionPersisters
as that's the place where actual collection persisters are enumerated (though I'm not saying it's the only and best place to do the job)
To me this task looks like the following:
- In translator (or walker) we need to find which collection persisters have restrictions;
- And for loader we need to map "restricted" collection perstisters with persisters in
collectionPerstisters
and prepareUncachedCollectionPersisters
So to me it looks like we don't need to bother "to determine whether collection will be fetched or not" - as long as we can obtain ICollectionPersister
for given criteria. And I don't see why OuterJoinableAssociation
needs to be modified - it seems all information can be obtained from translator/walker.
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.
Ok, if I understood well, we shouldn't add collection into cache if:
- There are restrictions and
subcriteria.JoinType == JoinType.LeftOuterJoin
- There are restrictions and
subcriteria.JoinType == JoinType.InnerJoin
andSelectMode == SelectMode.Fetch
, right?
And it wasn't clear for me how to get selection mode for subcriteria, but it seems that I can get it in this way - rootCriteria.GetSelectMode(subcriteria.Path)
, it isn't?
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.
Could you have a look at my last commit, please? Is it right direction?
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.
Yep. But as I already said I think that logic that checks Join
types and SelectMode
is unnecessary here. No need to duplicate code - those checks will be executed by the routine that prepares collection persisters to fetch.
I've refactored and made a few clean ups to it (see my latest commit). Let me know if you disagree or see any issues.
This comment has been minimized.
This comment has been minimized.
…be stored in second level cache
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
4254866
to
c57a703
Compare
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
@@ -46,6 +50,10 @@ public partial class CriteriaLoader : OuterJoinLoader | |||
|
|||
InitFromWalker(walker); | |||
|
|||
if (translator.UncacheableCriteriaCollectionPersisters.Any()) |
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 quite agree with this check because this obfuscates things by making stuff dependant on a index. Although it looks as an optimization it produces a bigger array.
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.
Actually I didn't mean it as optimization but merely to make it processed the same way as others properties (see CollectionAliases
, CollectionOwners
, CollectionPersisters
,...). And that's collection fetches - it seems big arrays should not be an issue.
@bahusoid I've done with my changes |
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.
lgtm
@hazzik I disagree that index based checks obfuscate things - it's pretty standard checks in Loader. Though have no objections with your version.
private readonly ISet<ICollectionPersister> criteriaCollectionPersisters = new HashSet<ICollectionPersister>(); | ||
private readonly IDictionary<ICriteria, string> criteriaSQLAliasMap = new Dictionary<ICriteria, string>(); | ||
private readonly IDictionary<string, ICriteria> aliasCriteriaMap = new Dictionary<string, ICriteria>(); | ||
private readonly IDictionary<string, ICriteria> associationPathCriteriaMap = new LinkedHashMap<string, ICriteria>(); | ||
private readonly Dictionary<string, CriteriaImpl.Subcriteria> associationPathCriteriaMap = new Dictionary<string, CriteriaImpl.Subcriteria>(); |
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.
LinkedHashMap
guarantees the dictionary elements will be iterated in the same order they were added. This is not the case of Dictionary
, which, with current implementations, will have a different iteration order as soon as some elements were removed.
So this change is not neutral. But it looks like preserving the order has no purpose here, so this is ok. (Similarly associationPathJoinTypesMap
does not need to be a LinkedHashMap
.)
When a cacheable criteria, or query-over, eager-loads a collection with restrictions, causing the collection to be partially loaded, the collection is nonetheless put in the second level cache. This causes lazy-loads of the collection done by other sessions to retrieve an invalid state for the collection.
This is a partial fix of #1341, for QueryOver and Criteria.