-
Notifications
You must be signed in to change notification settings - Fork 934
Combine Future queries for ICriteria and IQuery #1718
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
Conversation
I'm OK. |
I am also ok on the principle, provided this is not overly complex to achieve. (I have not looked yet at how you do that.) |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
e93cd5d
to
1f59d46
Compare
@hazzik , @fredericDelaporte Could you please review my changes in current state. I got rid of auto-flushing in I've enabled batcher for stateless session. Seems to be working fine. Should I obsolete existing future batcher with all related classes? |
@bahusoid I just wanted to suggest to make additions as a separate commits so it would be easier to see what's changed. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Actually I don't think we need it. We expose old future as: nhibernate-core/src/NHibernate/Engine/ISessionImplementor.cs Lines 329 to 331 in 2345bb4
Where both nhibernate-core/src/NHibernate/Impl/FutureBatch.cs Lines 26 to 29 in 2345bb4
And that's why old future is disabled for stateless session. So we shouldn't bother to fallback to old code - old code can work only with |
Ok. I will rewrite history in few commits. All changes are specific for this feature (and multi criteria fix is trivial after required for this change refactoring).
As already said in previous comment - it's supported only by new batcher. |
@@ -116,15 +116,5 @@ public abstract partial class AbstractQueryImpl2 : AbstractQueryImpl | |||
After(); | |||
} | |||
} | |||
|
|||
protected internal override async Task<IEnumerable<ITranslator>> GetTranslatorsAsync(ISessionImplementor sessionImplementor, QueryParameters queryParameters, CancellationToken cancellationToken) | |||
{ |
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.
Same as previous comment.
@@ -105,21 +105,5 @@ public override Task<IEnumerable<T>> EnumerableAsync<T>(CancellationToken cancel | |||
After(); | |||
} | |||
} | |||
|
|||
protected internal override Task<IEnumerable<ITranslator>> GetTranslatorsAsync(ISessionImplementor sessionImplementor, QueryParameters queryParameters, CancellationToken cancellationToken) |
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.
Same as previous comment.
@@ -35,6 +35,11 @@ internal static IDisposable BeginProcess(this ISessionImplementor session) | |||
// breaking change in case in custom session implementation is used. | |||
new SessionIdLoggingContext(session.SessionId); | |||
} | |||
|
|||
internal static IMultiAnyQueryBatch GetFutureMultiBatch(this ISessionImplementor session) |
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.
If obsoleting the previous batcher, better name the new one even more generically. Something like GetFutureBatch
here.
@@ -903,12 +903,20 @@ public IQuery SetResultTransformer(IResultTransformer transformer) | |||
|
|||
public IFutureEnumerable<T> Future<T>() | |||
{ | |||
if (FutureSettings.IsUnifiedFuture) |
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.
As said in a standalone comment, this should be instead some checking on the session capabilities (a type test indeed), likely done in the constructor of the query class.
/// <summary> | ||
/// Base class for both ICriteria and IQuery queries | ||
/// </summary> | ||
public abstract partial class MultiAnyQueryBase<TResult> : IMultiAnyQuery, IMultiAnyQuery<TResult> |
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.
IMultiAnyQuery
already comes with IMultiAnyQuery<TResult>
, reduce redundancy. An same remark than previously for naming.
|
||
namespace NHibernate | ||
{ | ||
public partial class MultiAnyQuery<TResult> : MultiAnyQueryBase<TResult>, IMultiAnyQuery<TResult> |
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.
Same remarks as previously on naming and interfaces.
var count = _queryInfos.Count; | ||
NewArray(count, out _hydratedObjects); | ||
NewArray(count, out _subselectResultKeys); | ||
NewArray(count, out _loaderResults); |
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 just writing regular code here?
_hydratedObjects = new List<object>[count];
_subselectResultKeys = new List<EntityKey[]>[count];
_loaderResults = new IList[count];
Adding a general purpose NewArray
in that class just for this need here looks as a code smell to me. Either remove it or put that as an internal helper on a class like ArrayHelper
.
return rowCount; | ||
}; | ||
|
||
_loaderResults[index] = tmpResults; |
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.
Putting this after the yield leaves the state a bit inconsistent between each MoveNext
when enumerating. Better move that before the yield.
/// <summary> | ||
/// Universal query batcher | ||
/// </summary> | ||
public partial class MultiAnyQueryBatch : IMultiAnyQueryBatch |
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.
Again, same remark for naming than on the interface.
I've split commit with some squashed clean up in tests. Regarding naming for
|
public interface INhQueryProviderSupportMultiBatch | ||
{ | ||
IQuery GetPreparedQuery(Expression exrpession, out NhLinqExpression expression); | ||
IMultiAnyQueryBatch GetFutureMultiBatch(); |
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'm thinking to expose ISessionImplementor Session
instead. And get batch from session. Any objections?
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 do not really see a benefit in exposing the whole session from the Linq provider. I would rather keep just exposing the future batch. Or even cease exposing it and add methods for adding new Linq query to it, a bit like the old interface was doing.
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.
The benefit is to make further modifications simpler. If session was available and old future was using it - we wouldn't need to introduce new interface. It was only internal modifications in extension methods. Is Session really internal implementation detail for provider? I guess it can be abstracted one more layer down - but it looks to me as very rare breaking change (and such modification will be breaking in many more places).
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, go for it.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
I was going to look in to it when new batcher is ready. But I suspect - yes. Are you OK with using new batcher cache approach in MultiCriteria\MultiQuery? |
Better have a single way of tackling the same subject everywhere, but let see that in another PR. |
@@ -2419,14 +2425,15 @@ public static IFutureEnumerable<TSource> ToFuture<TSource>(this IQueryable<TSour | |||
/// <exception cref="T:System.NotSupportedException"><paramref name="source" /> <see cref="IQueryable.Provider"/> is not a <see cref="INhQueryProvider"/>.</exception> | |||
public static IFutureValue<TSource> ToFutureValue<TSource>(this IQueryable<TSource> source) | |||
{ | |||
if (FutureSettings.IsUnifiedFuture) | |||
if(source.Provider is INhQueryProviderSupportMultiBatch batchProvider) |
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.
Missing space after if.
@@ -2404,8 +2404,14 @@ public static Task<List<TSource>> ToListAsync<TSource>(this IQueryable<TSource> | |||
/// <exception cref="T:System.NotSupportedException"><paramref name="source" /> <see cref="IQueryable.Provider"/> is not a <see cref="INhQueryProvider"/>.</exception> | |||
public static IFutureEnumerable<TSource> ToFuture<TSource>(this IQueryable<TSource> source) | |||
{ | |||
if(source.Provider is INhQueryProviderSupportMultiBatch batchProvider) |
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.
Missing space after if.
public interface INhQueryProviderSupportMultiBatch | ||
{ | ||
IQuery GetPreparedQuery(Expression exrpession, out NhLinqExpression expression); | ||
IMultiAnyQueryBatch GetFutureMultiBatch(); |
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 do not really see a benefit in exposing the whole session from the Linq provider. I would rather keep just exposing the future batch. Or even cease exposing it and add methods for adding new Linq query to it, a bit like the old interface was doing.
This comment has been minimized.
This comment has been minimized.
1e920b2
to
1cc973f
Compare
Looking at existing Future issues:
|
a8a01f0
to
4432f97
Compare
Is this done? Or more work required apart from squashing commits? |
From my point of view, yes. But since the last commit is some refactoring I have made, I was expecting a feedback from @bahusoid, before approving. I am already thinking of a follow-up PR which would obsolete the current multi-query/criteria for the query batcher introduced here. I do not think it should be done directly here, because this PR is already quite big, and its main subject was futures. |
@fredericDelaporte I have no objections with your latest changes. And I also think it's ready. |
Fixes nhibernate#752 Fixes nhibernate#1293 Fixes partially nhibernate#840 (multi-criteria/query not fixed) Fixes partially nhibernate#1344 (multi-criteria not fixed)
a45ab82
to
2de3f85
Compare
Rebased and squashed. |
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.
return; | ||
} | ||
|
||
using (Session.BeginProcess()) |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
protected void DoExecute() | ||
{ | ||
var resultSetsCommand = Session.Factory.ConnectionProvider.Driver.GetResultSetsCommand(Session); | ||
CombineQueries(resultSetsCommand); |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
int rowCount = 0; | ||
try | ||
{ | ||
if (resultSetsCommand.HasQueries) |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
//Skip processing for items already loaded from cache | ||
if (_queryInfos[i].CacheKey?.ResultTransformer != null && _loaderResults[i] != null) | ||
{ | ||
_loaderResults[i] = loader.TransformCacheableResults(queryParameters, _queryInfos[i].CacheKey.ResultTransformer, _loaderResults[i]); |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
|
||
if (index == _queryInfos.Count - 1) | ||
{ | ||
InitializeEntitiesAndCollections(reader, hydratedObjects); |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
for (int i = 0; i < _queryInfos.Count; i++) | ||
{ | ||
_queryInfos[i].Loader.InitializeEntitiesAndCollections( | ||
hydratedObjects[i], reader, Session, Session.PersistenceContext.DefaultReadOnly); |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
if (queryInfo.Cache != null) | ||
{ | ||
queryInfo.Loader.PutResultInQueryCache(Session, queryInfo.Parameters, queryInfo.Cache, queryInfo.CacheKey, _loaderResults[i]); | ||
} |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
|
||
using (Session.BeginProcess()) | ||
{ | ||
DoExecute(); |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
Fix some more troubles, see review on nhibernate#1718
} | ||
if (queryParameters.HasAutoDiscoverScalarTypes) | ||
{ | ||
loader.AutoDiscoverTypes(reader, queryParameters, null); |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
* AutoFlush is handled too late, causing stale data to be yielded * Cacheable result transformation is not fully done * Auto discovery of types fails * InitializeEntitiesAndCollections may be wrongly skipped or called * DoExecute should be renamed ExecuteBatched * BeginProcess should span the whole processing Follow up to nhibernate#1718. See its reviews for more details on the above shortcomings.
* AutoFlush is handled too late, causing stale data to be yielded * Cacheable result transformation is not fully done * Auto discovery of types fails * InitializeEntitiesAndCollections may be wrongly skipped or called * DoExecute should be renamed ExecuteBatched * BeginProcess should span the whole processing Follow up to #1718. See its reviews for more details on the above shortcomings.
The intention is to implement batcher that can contain both
ICriteria
andIQuery
based queries. And make Future implementation based on this batcher. See examples in testsNew batcher interface IQueryBatch (implementation QueryBatch ) allows only to Add queries wrapped in to IQueryBatchItem interface to batch and Execute batch.
See implementation for Criteria, IQuery and Linq. Most of functionality is shared in QueryBatchItemBase .
Caching is implemented per query (it's up to IQueryBatchItem implementation to cache or not to cache). So it's possible to cache queries with different settings (cache region etc), combine cached and non-cached queries. And batcher doesn't need to know all internal details about query cache (so it might be different cache for different IQueryBatchItem implementations).
Fixes #752
As "side effects" of this re-implementation of futures: