public async Task QueryCacheTestAsync() { // QueryCache batching is used by QueryBatch. if (!Sfi.ConnectionProvider.Driver.SupportsMultipleQueries) { Assert.Ignore($"{Sfi.ConnectionProvider.Driver} does not support multiple queries"); } var queryCache = Sfi.GetQueryCache(null); var field = typeof(StandardQueryCache).GetField( "_cache", BindingFlags.NonPublic | BindingFlags.Instance); Assert.That(field, Is.Not.Null, "Unable to find _cache field"); var cache = (BatchableCache)field.GetValue(queryCache); Assert.That(cache, Is.Not.Null, "_cache is null"); var timestamp = Sfi.UpdateTimestampsCache; var tsField = typeof(UpdateTimestampsCache).GetField( "_updateTimestamps", BindingFlags.NonPublic | BindingFlags.Instance); Assert.That(tsField, Is.Not.Null, "Unable to find _updateTimestamps field"); var tsCache = (BatchableCache)tsField.GetValue(timestamp); Assert.That(tsCache, Is.Not.Null, "_updateTimestamps is null"); await(cache.ClearAsync(CancellationToken.None)); cache.ClearStatistics(); await(tsCache.ClearAsync(CancellationToken.None)); tsCache.ClearStatistics(); using (var s = OpenSession()) { const string query = "from ReadOnly e where e.Name = :name"; const string name1 = "Name1"; const string name2 = "Name2"; const string name3 = "Name3"; const string name4 = "Name4"; const string name5 = "Name5"; var q1 = s .CreateQuery(query) .SetString("name", name1) .SetCacheable(true); var q2 = s .CreateQuery(query) .SetString("name", name2) .SetCacheable(true); var q3 = s .Query <ReadWrite>() .Where(r => r.Name == name3) .WithOptions(o => o.SetCacheable(true)); var q4 = s .QueryOver <ReadWrite>() .Where(r => r.Name == name4) .Cacheable(); var q5 = s .CreateSQLQuery("select * " + query) .AddEntity(typeof(ReadOnly)) .SetString("name", name5) .SetCacheable(true); var queries = s .CreateQueryBatch() .Add <ReadOnly>(q1) .Add <ReadOnly>(q2) .Add(q3) .Add(q4) .Add <ReadOnly>(q5); using (var t = s.BeginTransaction()) { await(queries.ExecuteAsync(CancellationToken.None)); await(t.CommitAsync()); } Assert.That(cache.GetMultipleCalls, Has.Count.EqualTo(1), "cache GetMany first execution"); Assert.That(cache.GetCalls, Has.Count.EqualTo(0), "cache Get first execution"); Assert.That(cache.PutMultipleCalls, Has.Count.EqualTo(1), "cache PutMany first execution"); Assert.That(cache.PutCalls, Has.Count.EqualTo(0), "cache Put first execution"); Assert.That(tsCache.GetMultipleCalls, Has.Count.EqualTo(0), "tsCache GetMany first execution"); Assert.That(tsCache.GetCalls, Has.Count.EqualTo(0), "tsCache Get first execution"); // Run a second time, to test the query cache using (var t = s.BeginTransaction()) { await(queries.ExecuteAsync(CancellationToken.None)); await(t.CommitAsync()); } Assert.That( await(queries.GetResultAsync <ReadOnly>(0, CancellationToken.None)), Has.Count.EqualTo(1).And.One.Property(nameof(ReadOnly.Name)).EqualTo(name1), "q1"); Assert.That( await(queries.GetResultAsync <ReadOnly>(1, CancellationToken.None)), Has.Count.EqualTo(1).And.One.Property(nameof(ReadOnly.Name)).EqualTo(name2), "q2"); Assert.That( await(queries.GetResultAsync <ReadWrite>(2, CancellationToken.None)), Has.Count.EqualTo(1).And.One.Property(nameof(ReadWrite.Name)).EqualTo(name3), "q3"); Assert.That( await(queries.GetResultAsync <ReadWrite>(3, CancellationToken.None)), Has.Count.EqualTo(1).And.One.Property(nameof(ReadWrite.Name)).EqualTo(name4), "q4"); Assert.That( await(queries.GetResultAsync <ReadOnly>(4, CancellationToken.None)), Has.Count.EqualTo(1).And.One.Property(nameof(ReadOnly.Name)).EqualTo(name5), "q5"); Assert.That(cache.GetMultipleCalls, Has.Count.EqualTo(2), "cache GetMany secondExecution"); Assert.That(cache.GetCalls, Has.Count.EqualTo(0), "cache Get secondExecution"); Assert.That(cache.PutMultipleCalls, Has.Count.EqualTo(1), "cache PutMany secondExecution"); Assert.That(cache.PutCalls, Has.Count.EqualTo(0), "cache Put secondExecution"); Assert.That(tsCache.GetMultipleCalls, Has.Count.EqualTo(1), "tsCache GetMany secondExecution"); Assert.That(tsCache.GetCalls, Has.Count.EqualTo(0), "tsCache Get secondExecution"); Assert.That(tsCache.PutMultipleCalls, Has.Count.EqualTo(0), "tsCache PutMany secondExecution"); Assert.That(tsCache.PutCalls, Has.Count.EqualTo(0), "tsCache Put secondExecution"); // Update an entity to invalidate them using (var t = s.BeginTransaction()) { var readwrite1 = await(s.Query <ReadWrite>().SingleAsync(e => e.Name == name3)); readwrite1.Name = "NewName"; await(t.CommitAsync()); } Assert.That(tsCache.GetMultipleCalls, Has.Count.EqualTo(1), "tsCache GetMany after update"); Assert.That(tsCache.GetCalls, Has.Count.EqualTo(0), "tsCache Get after update"); // Pre-invalidate + invalidate => 2 calls Assert.That(tsCache.PutMultipleCalls, Has.Count.EqualTo(2), "tsCache PutMany after update"); Assert.That(tsCache.PutCalls, Has.Count.EqualTo(0), "tsCache Put after update"); // Run a third time, to re-test the query cache using (var t = s.BeginTransaction()) { await(queries.ExecuteAsync(CancellationToken.None)); await(t.CommitAsync()); } Assert.That( await(queries.GetResultAsync <ReadOnly>(0, CancellationToken.None)), Has.Count.EqualTo(1).And.One.Property(nameof(ReadOnly.Name)).EqualTo(name1), "q1 after update"); Assert.That( await(queries.GetResultAsync <ReadOnly>(1, CancellationToken.None)), Has.Count.EqualTo(1).And.One.Property(nameof(ReadOnly.Name)).EqualTo(name2), "q2 after update"); Assert.That( await(queries.GetResultAsync <ReadWrite>(2, CancellationToken.None)), Has.Count.EqualTo(0), "q3 after update"); Assert.That( await(queries.GetResultAsync <ReadWrite>(3, CancellationToken.None)), Has.Count.EqualTo(1).And.One.Property(nameof(ReadWrite.Name)).EqualTo(name4), "q4 after update"); Assert.That( await(queries.GetResultAsync <ReadOnly>(4, CancellationToken.None)), Has.Count.EqualTo(1).And.One.Property(nameof(ReadOnly.Name)).EqualTo(name5), "q5 after update"); Assert.That(cache.GetMultipleCalls, Has.Count.EqualTo(3), "cache GetMany thirdExecution"); Assert.That(cache.GetCalls, Has.Count.EqualTo(0), "cache Get thirdExecution"); // ReadWrite queries should have been re-put, so count should have been incremented Assert.That(cache.PutMultipleCalls, Has.Count.EqualTo(2), "cache PutMany thirdExecution"); Assert.That(cache.PutCalls, Has.Count.EqualTo(0), "cache Put thirdExecution"); // Readonly entities should have been still cached, so their queries timestamp should have been // rechecked and the get count incremented Assert.That(tsCache.GetMultipleCalls, Has.Count.EqualTo(2), "tsCache GetMany thirdExecution"); Assert.That(tsCache.GetCalls, Has.Count.EqualTo(0), "tsCache Get thirdExecution"); Assert.That(tsCache.PutMultipleCalls, Has.Count.EqualTo(2), "tsCache PutMany thirdExecution"); Assert.That(tsCache.PutCalls, Has.Count.EqualTo(0), "tsCache Put thirdExecution"); } }
public void RetrievedQueryCacheMatchesGloballyStoredOne() { var region = "RetrievedQueryCacheMatchesGloballyStoredOne"; LockedCache.Semaphore = new SemaphoreSlim(0); LockedCache.CreationCount = 0; try { var failures = new ConcurrentBag <Exception>(); var thread1 = new Thread( () => { try { Sfi.GetQueryCache(region); } catch (Exception e) { failures.Add(e); } }); var thread2 = new Thread( () => { try { Sfi.GetQueryCache(region); } catch (Exception e) { failures.Add(e); } }); thread1.Start(); thread2.Start(); // Give some time to threads for reaching the wait, having all of them ready to do most of their job concurrently. Thread.Sleep(100); // Let only one finish its job, it should be the one being stored in query cache dictionary. LockedCache.Semaphore.Release(1); // Give some time to released thread to finish its job. Thread.Sleep(100); // Release other thread LockedCache.Semaphore.Release(10); thread1.Join(); thread2.Join(); Assert.That(failures, Is.Empty, $"{failures.Count} thread(s) failed."); } finally { LockedCache.Semaphore.Dispose(); LockedCache.Semaphore = null; } var queryCache = Sfi.GetQueryCache(region).Cache; var globalCache = Sfi.GetSecondLevelCacheRegion(region); Assert.That(globalCache, Is.SameAs(queryCache)); Assert.That(LockedCache.CreationCount, Is.EqualTo(1)); }