Skip to content

Support CacheMode in QueryBatch #1796

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
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 111 additions & 0 deletions src/NHibernate.Test/Async/Futures/QueryBatchFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,116 @@ public async Task UsingHqlToFutureWithCacheAndTransformerDoesntThrowAsync()
}
}

[Test]
public async Task ReadOnlyWorksWithFutureAsync()
{
using (var s = OpenSession())
using (var t = s.BeginTransaction())
{
var futureSimples =
s
.CreateQuery("from EntitySimpleChild")
.SetReadOnly(true)
.Future<EntitySimpleChild>();
var futureSubselect =
s
.CreateQuery("from EntitySubselectChild")
.Future<EntitySubselectChild>();

var simples = (await (futureSimples.GetEnumerableAsync())).ToList();
Assert.That(simples, Has.Count.GreaterThan(0));
foreach (var entity in simples)
{
Assert.That(s.IsReadOnly(entity), Is.True, entity.Name);
}

var subselect = (await (futureSubselect.GetEnumerableAsync())).ToList();
Assert.That(subselect, Has.Count.GreaterThan(0));
foreach (var entity in subselect)
{
Assert.That(s.IsReadOnly(entity), Is.False, entity.Name);
}

await (t.CommitAsync());
}
}

[Test]
public async Task CacheModeWorksWithFutureAsync()
{
Sfi.Statistics.IsStatisticsEnabled = true;

using (var s = OpenSession())
using (var t = s.BeginTransaction())
{
s
.CreateQuery("from EntitySimpleChild")
.SetCacheable(true)
.SetCacheMode(CacheMode.Get)
.Future<EntitySimpleChild>();
s
.CreateQuery("from EntityComplex")
.SetCacheable(true)
.SetCacheMode(CacheMode.Put)
.Future<EntityComplex>();
await (s
.CreateQuery("from EntitySubselectChild")
.SetCacheable(true)
.Future<EntitySubselectChild>()
.GetEnumerableAsync());
Assert.That(Sfi.Statistics.QueryCachePutCount, Is.EqualTo(2), "Future put");

await (t.CommitAsync());
}

using (var s = OpenSession())
using (var t = s.BeginTransaction())
{
Sfi.Statistics.Clear();
await (s
.CreateQuery("from EntitySimpleChild")
.SetCacheable(true)
.ListAsync<EntitySimpleChild>());
Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(0), "EntitySimpleChild query hit");

Sfi.Statistics.Clear();
await (s
.CreateQuery("from EntityComplex")
.SetCacheable(true)
.ListAsync<EntityComplex>());
Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(1), "EntityComplex query hit");
Copy link
Member

@bahusoid bahusoid Jul 14, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you mean "unexpected query cache miss"? "query hit" is a bit unclear error message considering that in previous check above it's also used but for different condition.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I generally use the label for identifying which assert has failed. That is the assert on query hit. I do not think this label has to describe what is the failure. That would be already interpreting it, and sometimes it could be off the actual cause.


Sfi.Statistics.Clear();
await (s
.CreateQuery("from EntitySubselectChild")
.SetCacheable(true)
.ListAsync<EntitySubselectChild>());
Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(1), "EntitySubselectChild query hit");

await (t.CommitAsync());
}

using (var s = OpenSession())
using (var t = s.BeginTransaction())
{
Sfi.Statistics.Clear();
s
.CreateQuery("from EntitySimpleChild")
.SetCacheable(true)
.SetCacheMode(CacheMode.Get)
.Future<EntitySimpleChild>();
await (s
.CreateQuery("from EntitySubselectChild")
.SetCacheable(true)
.Future<EntitySubselectChild>()
.GetEnumerableAsync());
Assert.That(Sfi.Statistics.QueryCachePutCount, Is.EqualTo(0), "Second future put");
Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(2), "Second future hit");

await (t.CommitAsync());
}
}

#region Test Setup

protected override HbmMapping GetMappings()
Expand Down Expand Up @@ -471,6 +581,7 @@ protected override void OnTearDown()
session.Flush();
transaction.Commit();
}
Sfi.Statistics.IsStatisticsEnabled = false;
}

protected override void OnSetUp()
Expand Down
111 changes: 111 additions & 0 deletions src/NHibernate.Test/Futures/QueryBatchFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,116 @@ public void UsingHqlToFutureWithCacheAndTransformerDoesntThrow()
}
}

[Test]
public void ReadOnlyWorksWithFuture()
{
using (var s = OpenSession())
using (var t = s.BeginTransaction())
{
var futureSimples =
s
.CreateQuery("from EntitySimpleChild")
.SetReadOnly(true)
.Future<EntitySimpleChild>();
var futureSubselect =
s
.CreateQuery("from EntitySubselectChild")
.Future<EntitySubselectChild>();

var simples = futureSimples.GetEnumerable().ToList();
Assert.That(simples, Has.Count.GreaterThan(0));
foreach (var entity in simples)
{
Assert.That(s.IsReadOnly(entity), Is.True, entity.Name);
}

var subselect = futureSubselect.GetEnumerable().ToList();
Assert.That(subselect, Has.Count.GreaterThan(0));
foreach (var entity in subselect)
{
Assert.That(s.IsReadOnly(entity), Is.False, entity.Name);
}

t.Commit();
}
}

[Test]
public void CacheModeWorksWithFuture()
{
Sfi.Statistics.IsStatisticsEnabled = true;

using (var s = OpenSession())
using (var t = s.BeginTransaction())
{
s
.CreateQuery("from EntitySimpleChild")
.SetCacheable(true)
.SetCacheMode(CacheMode.Get)
.Future<EntitySimpleChild>();
s
.CreateQuery("from EntityComplex")
.SetCacheable(true)
.SetCacheMode(CacheMode.Put)
.Future<EntityComplex>();
s
.CreateQuery("from EntitySubselectChild")
.SetCacheable(true)
.Future<EntitySubselectChild>()
.GetEnumerable();
Assert.That(Sfi.Statistics.QueryCachePutCount, Is.EqualTo(2), "Future put");

t.Commit();
}

using (var s = OpenSession())
using (var t = s.BeginTransaction())
{
Sfi.Statistics.Clear();
s
.CreateQuery("from EntitySimpleChild")
.SetCacheable(true)
.List<EntitySimpleChild>();
Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(0), "EntitySimpleChild query hit");

Sfi.Statistics.Clear();
s
.CreateQuery("from EntityComplex")
.SetCacheable(true)
.List<EntityComplex>();
Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(1), "EntityComplex query hit");

Sfi.Statistics.Clear();
s
.CreateQuery("from EntitySubselectChild")
.SetCacheable(true)
.List<EntitySubselectChild>();
Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(1), "EntitySubselectChild query hit");

t.Commit();
}

using (var s = OpenSession())
using (var t = s.BeginTransaction())
{
Sfi.Statistics.Clear();
s
.CreateQuery("from EntitySimpleChild")
.SetCacheable(true)
.SetCacheMode(CacheMode.Get)
.Future<EntitySimpleChild>();
s
.CreateQuery("from EntitySubselectChild")
.SetCacheable(true)
.Future<EntitySubselectChild>()
.GetEnumerable();
Assert.That(Sfi.Statistics.QueryCachePutCount, Is.EqualTo(0), "Second future put");
Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(2), "Second future hit");

t.Commit();
}
}

#region Test Setup

protected override HbmMapping GetMappings()
Expand Down Expand Up @@ -459,6 +569,7 @@ protected override void OnTearDown()
session.Flush();
transaction.Commit();
}
Sfi.Statistics.IsStatisticsEnabled = false;
}

protected override void OnSetUp()
Expand Down
22 changes: 16 additions & 6 deletions src/NHibernate/Async/Cache/StandardQueryCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -245,13 +245,23 @@ public async Task<IList[]> GetManyAsync(
else
queryParams.ReadOnly = persistenceContext.DefaultReadOnly;

try
// Adjust the session cache mode, as GetResultFromCacheable assemble types which may cause
// entity loads, which may interact with the cache.
using (session.SwitchCacheMode(queryParams.CacheMode))
{
results[i] = await (GetResultFromCacheableAsync(key, returnTypes[i], queryParams.NaturalKeyLookup, session, cacheable, cancellationToken)).ConfigureAwait(false);
}
finally
{
persistenceContext.DefaultReadOnly = defaultReadOnlyOrig;
try
{
results[i] = await (GetResultFromCacheableAsync(
key,
returnTypes[i],
queryParams.NaturalKeyLookup,
session,
cacheable, cancellationToken)).ConfigureAwait(false);
}
finally
{
persistenceContext.DefaultReadOnly = defaultReadOnlyOrig;
}
}
}

Expand Down
54 changes: 28 additions & 26 deletions src/NHibernate/Async/Event/Default/DefaultDeleteEventListener.cs
Original file line number Diff line number Diff line change
Expand Up @@ -222,40 +222,42 @@ protected virtual async Task DeleteEntityAsync(IEventSource session, object enti
protected virtual async Task CascadeBeforeDeleteAsync(IEventSource session, IEntityPersister persister, object entity, EntityEntry entityEntry, ISet<object> transientEntities, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
ISessionImplementor si = session;
CacheMode cacheMode = si.CacheMode;
si.CacheMode = CacheMode.Get;
session.PersistenceContext.IncrementCascadeLevel();
try
using (session.SwitchCacheMode(CacheMode.Get))
{
// cascade-delete to collections BEFORE the collection owner is deleted
await (new Cascade(CascadingAction.Delete, CascadePoint.AfterInsertBeforeDelete, session).CascadeOnAsync(persister, entity,
transientEntities, cancellationToken)).ConfigureAwait(false);
}
finally
{
session.PersistenceContext.DecrementCascadeLevel();
si.CacheMode = cacheMode;
session.PersistenceContext.IncrementCascadeLevel();
try
{
// cascade-delete to collections BEFORE the collection owner is deleted
await (new Cascade(CascadingAction.Delete, CascadePoint.AfterInsertBeforeDelete, session).CascadeOnAsync(
persister,
entity,
transientEntities, cancellationToken)).ConfigureAwait(false);
}
finally
{
session.PersistenceContext.DecrementCascadeLevel();
}
}
}

protected virtual async Task CascadeAfterDeleteAsync(IEventSource session, IEntityPersister persister, object entity, ISet<object> transientEntities, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
ISessionImplementor si = session;
CacheMode cacheMode = si.CacheMode;
si.CacheMode = CacheMode.Get;
session.PersistenceContext.IncrementCascadeLevel();
try
using (session.SwitchCacheMode(CacheMode.Get))
{
// cascade-delete to many-to-one AFTER the parent was deleted
await (new Cascade(CascadingAction.Delete, CascadePoint.BeforeInsertAfterDelete, session).CascadeOnAsync(persister, entity,
transientEntities, cancellationToken)).ConfigureAwait(false);
}
finally
{
session.PersistenceContext.DecrementCascadeLevel();
si.CacheMode = cacheMode;
session.PersistenceContext.IncrementCascadeLevel();
try
{
// cascade-delete to many-to-one AFTER the parent was deleted
await (new Cascade(CascadingAction.Delete, CascadePoint.BeforeInsertAfterDelete, session).CascadeOnAsync(
persister,
entity,
transientEntities, cancellationToken)).ConfigureAwait(false);
}
finally
{
session.PersistenceContext.DecrementCascadeLevel();
}
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/NHibernate/Async/Loader/Loader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1227,7 +1227,7 @@ private async Task<IList> GetResultFromQueryCacheAsync(
IQueryCache queryCache, QueryKey key, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
if (!CanGetFromCache(session, queryParameters))
if (!queryParameters.CanGetFromCache(session))
return null;

var result = await (queryCache.GetAsync(
Expand Down Expand Up @@ -1256,7 +1256,7 @@ private async Task PutResultInQueryCacheAsync(ISessionImplementor session, Query
IQueryCache queryCache, QueryKey key, IList result, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
if (!session.CacheMode.HasFlag(CacheMode.Put))
if (!queryParameters.CanPutToCache(session))
return;

var put = await (queryCache.PutAsync(
Expand Down
Loading