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");
            }
        }
Exemple #2
0
        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));
        }