Skip to content

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

Merged

Conversation

zalewskil
Copy link
Contributor

@zalewskil zalewskil commented Feb 23, 2016

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.

@hazzik hazzik assigned hazzik and unassigned hazzik Jul 25, 2017
@hazzik hazzik changed the title Nh 3848 - Tests and Fix for Criteria and QueryOver NH-3848 - Tests and Fix for Criteria and QueryOver May 25, 2018
@hazzik

This comment has been minimized.

@zalewskil

This comment has been minimized.

@hazzik

This comment has been minimized.

@hazzik hazzik force-pushed the NH-3848_Criteria_And_QueryOver branch from 2d5245a to 7d53824 Compare May 26, 2018 04:53
Copy link
Member

@fredericDelaporte fredericDelaporte left a 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.

@fredericDelaporte fredericDelaporte changed the title NH-3848 - Tests and Fix for Criteria and QueryOver Fix Criteria caching filtered collections Nov 15, 2018
@fredericDelaporte
Copy link
Member

fredericDelaporte commented Nov 15, 2018

As this is a partial fix of #1341, I have re-tagged it as a stand-alone issue and I have changed the description for avoiding GitHub auto-closing #1341.


if (subcriteria.HasRestrictions && subcriteria.JoinType == JoinType.LeftOuterJoin)
{
var collectionName = $"{rootCriteria.EntityOrClassName}.{subcriteria.Path}";
Copy link
Member

@bahusoid bahusoid Nov 16, 2018

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 in uncacheableCollectionPersisters
  • We should not rely on Left join anymore as now we support fetching collections with inner join too (with SelectMode.Fetch).

Copy link

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?

Copy link
Member

@bahusoid bahusoid Nov 20, 2018

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:

  1. In translator (or walker) we need to find which collection persisters have restrictions;
  2. And for loader we need to map "restricted" collection perstisters with persisters in collectionPerstisters and prepare UncachedCollectionPersisters

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.

Copy link

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:

  1. There are restrictions and subcriteria.JoinType == JoinType.LeftOuterJoin
  2. There are restrictions and subcriteria.JoinType == JoinType.InnerJoin and SelectMode == 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?

Copy link

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?

Copy link
Member

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.

@fredericDelaporte

This comment has been minimized.

@fredericDelaporte

This comment has been minimized.

@IwanowM

This comment has been minimized.

@fredericDelaporte fredericDelaporte force-pushed the NH-3848_Criteria_And_QueryOver branch from 4254866 to c57a703 Compare November 16, 2018 12:34
@fredericDelaporte

This comment has been minimized.

@hazzik

This comment has been minimized.

@bahusoid

This comment has been minimized.

@@ -46,6 +50,10 @@ public partial class CriteriaLoader : OuterJoinLoader

InitFromWalker(walker);

if (translator.UncacheableCriteriaCollectionPersisters.Any())
Copy link
Member

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.

Copy link
Member

@bahusoid bahusoid Nov 20, 2018

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.

@hazzik
Copy link
Member

hazzik commented Nov 20, 2018

@bahusoid I've done with my changes

Copy link
Member

@bahusoid bahusoid left a 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>();
Copy link
Member

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.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants