public async Task MemoizeAsync_AfterRefreshRevokeKeysArePreservedInReverseIndexCorrectly()
        {
            var revokesSource = new OneTimeSynchronousSourceBlock <string>();
            var cache         = CreateCache(revokesSource);
            var memoizer      = CreateMemoizer(cache);

            var dataSource = Substitute.For <IThingFrobber>();
            var result1    = new Revocable <Thing> {
                Value = new Thing {
                    Id = "result1"
                }, RevokeKeys = new List <string> {
                    "x", "y"
                }
            };
            var result2 = new Revocable <Thing> {
                Value = new Thing {
                    Id = "result2"
                }, RevokeKeys = new List <string> {
                    "x", "y"
                }
            };

            dataSource.ThingifyTaskRevokable("someString").Returns(result1, result2);

            await(Task <Revocable <Thing> >) memoizer.Memoize(dataSource, ThingifyTaskRevokabkle, new object[] { "someString" }, GetCachingSettings(refreshTimeSeconds: 0));//refresh in next call
            cache.RevokeKeysCount.ShouldBe(2);
            cache.CacheKeyCount.ShouldBe(2);

            await(Task <Revocable <Thing> >) memoizer.Memoize(dataSource, ThingifyTaskRevokabkle, new object[] { "someString" }, GetCachingSettings());
            cache.CacheKeyCount.ShouldBe(2);
            cache.RevokeKeysCount.ShouldBe(2);
        }
        public async Task MemoizeAsync_NotCachedCallWithoutRevokeShouldCacheDataSourceValue()
        {
            var revokesSource = new OneTimeSynchronousSourceBlock <string>();
            var cache         = CreateCache(revokesSource);
            var memoizer      = CreateMemoizer(cache);

            var dataSourceDelay   = 1;
            var dataSourceResult1 = new Revocable <Thing> {
                Value = new Thing {
                    Id = "result1"
                }, RevokeKeys = new List <string> {
                    "x"
                }
            };
            var dataSource = new ThingFrobber(dataSourceDelay, new List <Revocable <Thing> > {
                dataSourceResult1
            });

            RecentlyRevokesCache.TryGetRecentlyRevokedTime(Arg.Any <string>(), Arg.Any <DateTime>()).Returns((DateTime?)null);

            var result = await(Task <Revocable <Thing> >) memoizer.Memoize(dataSource, ThingifyTaskRevokabkle, new object[] { "someString" }, GetCachingSettings());

            result.Value.Id.ShouldBe(dataSourceResult1.Value.Id); //call data source and get first value

            result = await(Task <Revocable <Thing> >) memoizer.Memoize(dataSource, ThingifyTaskRevokabkle, new object[] { "someString" }, GetCachingSettings());
            result.Value.Id.ShouldBe(dataSourceResult1.Value.Id); //get cached value
        }
        public async Task MemoizeAsync_ExistingItemReceivedRevokeAndCallAfterRevokeReturnsAnIgnoredResponseAndSettingIsTryFetchNewValueNextTimeOrUseOld_AfterRevokeCacheIsUsedAndCallIsMadeToDataSourceAndIgnored()
        {
            var firstResult   = "first result";
            var secondResult  = "second result";
            var dataSource    = CreateRevokableDataSource(new[] { "revokeKey" }, firstResult, null, secondResult);
            var revokesSource = new OneTimeSynchronousSourceBlock <string>();
            var cache         = CreateCache(revokesSource);
            var memoizer      = CreateMemoizer(cache);

            var result = await(Task <Revocable <Thing> >) memoizer.Memoize(dataSource, ThingifyTaskRevokabkle, new object[] { "someString" }, GetCachingSettings());

            result.Value.Id.ShouldBe(firstResult);
            dataSource.Received(1).ThingifyTaskRevokable("someString"); //New item - fetch from datasource

            //Post revoke message
            revokesSource.PostMessageSynced("revokeKey");

            //We want to test that revoked item is returned (because data source response should be ignored)
            result = await(Task <Revocable <Thing> >) memoizer.Memoize(dataSource, ThingifyTaskRevokabkle, new object[] { "someString" },
                                                                       GetCachingSettings(revokedResponseBehavior: RevokedResponseBehavior.TryFetchNewValueNextTimeOrUseOld,
                                                                                          responseKindsToCache:  ResponseKinds.NonNullResponse,
                                                                                          responseKindsToIgnore: ResponseKinds.NullResponse));
            result.Value.Id.ShouldBe(firstResult);                      //we use old cached value
            dataSource.Received(2).ThingifyTaskRevokable("someString"); //tried to fetch from data source (and ignored)

            //Cached item is still revoked, so we do another call to data source and get a new valid response
            result = await(Task <Revocable <Thing> >) memoizer.Memoize(dataSource, ThingifyTaskRevokabkle, new object[] { "someString" },
                                                                       GetCachingSettings(revokedResponseBehavior: RevokedResponseBehavior.TryFetchNewValueNextTimeOrUseOld));
            result.Value.Id.ShouldBe(secondResult); //get new value
            dataSource.Received(3).ThingifyTaskRevokable("someString");
        }
        public async Task MemoizeAsync_ExistingItemReceivedRevokeAndSettingIsTryFetchNewValueInBackgroundNextTime_AfterRevokeCacheIsUsedAndItemIsFetchedFromDataSourceInTheBackround()
        {
            var firstResult   = "first result";
            var secondResult  = "second result";
            var dataSource    = CreateRevokableDataSource(new[] { "revokeKey" }, firstResult, secondResult);
            var revokesSource = new OneTimeSynchronousSourceBlock <string>();
            var cache         = CreateCache(revokesSource);
            var memoizer      = CreateMemoizer(cache);

            var result = await(Task <Revocable <Thing> >) memoizer.Memoize(dataSource, ThingifyTaskRevokabkle, new object[] { "someString" }, GetCachingSettings());

            result.Value.Id.ShouldBe(firstResult);
            dataSource.Received(1).ThingifyTaskRevokable("someString"); //New item - fetch from datasource

            //Post revoke message
            revokesSource.PostMessageSynced("revokeKey");

            //We want to test that revoked item is used and we fetch a new item from data source in the backround
            result = await(Task <Revocable <Thing> >) memoizer.Memoize(dataSource, ThingifyTaskRevokabkle, new object[] { "someString" },
                                                                       GetCachingSettings(revokedResponseBehavior: RevokedResponseBehavior.TryFetchNewValueInBackgroundNextTime));
            result.Value.Id.ShouldBe(firstResult);                      //Use old cached value
            dataSource.Received(2).ThingifyTaskRevokable("someString"); //A backround call to the data source is made

            //After backround call is done, we should get new result
            result = await(Task <Revocable <Thing> >) memoizer.Memoize(dataSource, ThingifyTaskRevokabkle, new object[] { "someString" },
                                                                       GetCachingSettings(revokedResponseBehavior: RevokedResponseBehavior.TryFetchNewValueInBackgroundNextTime));
            result.Value.Id.ShouldBe(secondResult);
            dataSource.Received(2).ThingifyTaskRevokable("someString"); //No additional data source call
        }
        //Bug #134604
        public async Task MemoizeAsync_ExistingItemWasRefreshedByTtlAndReciviedRevoke_AfterRevokeCacheIsNotUsedAndItemIsFetchedFromDataSource()
        {
            var completionSource = new TaskCompletionSource <Revocable <Thing> >();

            completionSource.SetResult(new Revocable <Thing> {
                Value = new Thing {
                    Id = "first Value"
                }, RevokeKeys = new[] { "revokeKey" }
            });
            var dataSource    = CreateRevokableDataSource(null, completionSource);
            var revokesSource = new OneTimeSynchronousSourceBlock <string>();
            var cache         = CreateCache(revokesSource);
            var memoizer      = CreateMemoizer(cache);

            //To cause refresh in next call
            await(Task <Revocable <Thing> >) memoizer.Memoize(dataSource, ThingifyTaskRevokabkle, new object[] { "someString" }, GetCachingSettings(refreshTimeSeconds: 0));
            dataSource.Received(1).ThingifyTaskRevokable("someString"); //New item - fetch from datasource

            await(Task <Revocable <Thing> >) memoizer.Memoize(dataSource, ThingifyTaskRevokabkle, new object[] { "someString" }, GetCachingSettings());
            dataSource.Received(2).ThingifyTaskRevokable("someString"); //Refresh task - fetch from datasource

            //Post revoke message
            revokesSource.PostMessageSynced("revokeKey");

            //We want to test that item was removed from cache after revoke and that new item is fetched from datasource
            await(Task <Revocable <Thing> >) memoizer.Memoize(dataSource, ThingifyTaskRevokabkle, new object[] { "someString" }, GetCachingSettings());
            dataSource.Received(3).ThingifyTaskRevokable("someString"); //Revoke received and item removed from cache - fetch from datasource
        }
        public async Task MemoizeAsync_RevokeBeforeRetrivalTaskCompletedCaused_NoIssues()
        {
            var completionSource = new TaskCompletionSource <Revocable <Thing> >();
            var dataSource       = CreateRevokableDataSource(null, completionSource);
            var revokesSource    = new OneTimeSynchronousSourceBlock <string>();
            var cache            = CreateCache(revokesSource);
            var memoizer         = CreateMemoizer(cache);

            //Call method to get results
            var resultTask = (Task <Revocable <Thing> >)memoizer.Memoize(dataSource, ThingifyTaskRevokabkle, new object[] { "someString" }, GetPolicy());

            //Post revoke message while results had not arrived
            revokesSource.PostMessageSynced("revokeKey");

            //Wait before sending results
            await Task.Delay(100);

            completionSource.SetResult(new Revocable <Thing> {
                Value = new Thing {
                    Id = 5
                }, RevokeKeys = new[] { "revokeKey" }
            });

            //Results should arive now
            var actual = await resultTask;

            dataSource.Received(1).ThingifyTaskRevokable("someString");
            actual.Value.Id.ShouldBe(5);

            cache.CacheKeyCount.ShouldBe(1);
        }
Пример #7
0
        public async Task MemoizeAsync_RevokeBeforeRetrivalTaskCompletedCaused_NoIssues()
        {
            var completionSource = new TaskCompletionSource <Revocable <Thing> >();
            var dataSource       = CreateRevokableDataSource(null, completionSource);
            var revokesSource    = new OneTimeSynchronousSourceBlock <string>();
            var cache            = CreateCache(revokesSource);
            var memoizer         = CreateMemoizer(cache);

            //Call method to get results
            var resultTask = (Task <Revocable <Thing> >)memoizer.Memoize(dataSource, ThingifyTaskRevokabkle, new object[] { "someString" }, GetPolicy());

            //Post revoke message while results had not arrived
            revokesSource.PostMessageSynced("revokeKey");

            //Should have a single discarded revoke in meter
            GetMetricsData("Revoke").AssertEquals(new MetricsDataEquatable
            {
                MetersSettings = new MetricsCheckSetting {
                    CheckValues = true
                },
                Meters = new List <MetricDataEquatable> {
                    new MetricDataEquatable {
                        Name = "Discarded", Unit = Unit.Events, Value = 1
                    },
                }
            });

            //Wait before sending results
            await Task.Delay(100);

            completionSource.SetResult(new Revocable <Thing> {
                Value = new Thing {
                    Id = 5
                }, RevokeKeys = new[] { "revokeKey" }
            });

            //Results should arive now
            var actual = await resultTask;

            dataSource.Received(1).ThingifyTaskRevokable("someString");
            actual.Value.Id.ShouldBe(5);

            cache.CacheKeyCount.ShouldBe(1);
        }
        public async Task MemoizeAsync_NotCachedCallWithIntefiringRevokeShouldMarkCacheValueAsStaleAndTriggerACallToDataSource()
        {
            var revokesSource = new OneTimeSynchronousSourceBlock <string>();
            var cache         = CreateCache(revokesSource);
            var memoizer      = CreateMemoizer(cache);

            var dataSourceDelay   = 1;
            var dataSourceResult1 = new Revocable <Thing> {
                Value = new Thing {
                    Id = "result1"
                }, RevokeKeys = new List <string> {
                    "x"
                }
            };
            var dataSourceResult2 = new Revocable <Thing> {
                Value = new Thing {
                    Id = "result2"
                }, RevokeKeys = new List <string> {
                    "x"
                }
            };
            var dataSource = new ThingFrobber(dataSourceDelay, new List <Revocable <Thing> > {
                dataSourceResult1, dataSourceResult2
            });

            //first call to data source will receive a revoke!!!
            RecentlyRevokesCache.TryGetRecentlyRevokedTime(Arg.Any <string>(), Arg.Any <DateTime>()).Returns(TimeFake.UtcNow, (DateTime?)null);

            var result = await(Task <Revocable <Thing> >) memoizer.Memoize(dataSource, ThingifyTaskRevokabkle, new object[] { "someString" }, GetCachingSettings());

            result.Value.Id.ShouldBe(dataSourceResult1.Value.Id); //call data source and get first value

            //revoke received while in first call to data source

            result = await(Task <Revocable <Thing> >) memoizer.Memoize(dataSource, ThingifyTaskRevokabkle, new object[] { "someString" }, GetCachingSettings());
            result.Value.Id.ShouldBe(dataSourceResult2.Value.Id); //call trigger a call to data source because value is stale and a new data source value is returned

            result = await(Task <Revocable <Thing> >) memoizer.Memoize(dataSource, ThingifyTaskRevokabkle, new object[] { "someString" }, GetCachingSettings());
            result.Value.Id.ShouldBe(dataSourceResult2.Value.Id); //cached value is returned
        }
        public async Task MemoizeAsync_RefreshWithoutRevokeShouldCacheNewValue()
        {
            var revokesSource = new OneTimeSynchronousSourceBlock <string>();
            var cache         = CreateCache(revokesSource);
            var memoizer      = CreateMemoizer(cache);

            var dataSourceDelay   = 1;
            var dataSourceResult1 = new Revocable <Thing> {
                Value = new Thing {
                    Id = "result1"
                }, RevokeKeys = new List <string> {
                    "x"
                }
            };
            var dataSourceResult2 = new Revocable <Thing> {
                Value = new Thing {
                    Id = "result2"
                }, RevokeKeys = new List <string> {
                    "x"
                }
            };
            var dataSource = new ThingFrobber(dataSourceDelay, new List <Revocable <Thing> > {
                dataSourceResult1, dataSourceResult2
            });

            RecentlyRevokesCache.TryGetRecentlyRevokedTime(Arg.Any <string>(), Arg.Any <DateTime>()).Returns((DateTime?)null);

            var refreshBehavior = RefreshBehavior.TryFetchNewValueOrUseOld;
            //refresh in next call
            var result = await(Task <Revocable <Thing> >) memoizer.Memoize(dataSource, ThingifyTaskRevokabkle, new object[] { "someString" }, GetCachingSettings(refreshTimeSeconds: 0, refreshBehavior: refreshBehavior));

            result.Value.Id.ShouldBe(dataSourceResult1.Value.Id); //call data source and get first value

            result = await(Task <Revocable <Thing> >) memoizer.Memoize(dataSource, ThingifyTaskRevokabkle, new object[] { "someString" }, GetCachingSettings(refreshBehavior: refreshBehavior));
            result.Value.Id.ShouldBe(dataSourceResult2.Value.Id); //refresh triggered, get second value from data source and cache

            result = await(Task <Revocable <Thing> >) memoizer.Memoize(dataSource, ThingifyTaskRevokabkle, new object[] { "someString" }, GetCachingSettings(refreshBehavior: refreshBehavior));
            result.Value.Id.ShouldBe(dataSourceResult2.Value.Id); //return refreshed cached value
        }
        public async Task MemoizeAsync_ExistingItemReceivedRevokeAndSettingIsKeepUsingRevokedResponse_AfterRevokeCacheIsUsedAndNoCallIsMadeToDataSource()
        {
            var firstResult   = "first result";
            var dataSource    = CreateRevokableDataSource(new [] { "revokeKey" }, firstResult);
            var revokesSource = new OneTimeSynchronousSourceBlock <string>();
            var cache         = CreateCache(revokesSource);
            var memoizer      = CreateMemoizer(cache);

            var result = await(Task <Revocable <Thing> >) memoizer.Memoize(dataSource, ThingifyTaskRevokabkle, new object[] { "someString" }, GetCachingSettings());

            result.Value.Id.ShouldBe(firstResult);
            dataSource.Received(1).ThingifyTaskRevokable("someString"); //New item - fetch from datasource

            //Post revoke message
            revokesSource.PostMessageSynced("revokeKey");

            //We want to test that revoked item is still in cache and returned
            result = await(Task <Revocable <Thing> >) memoizer.Memoize(dataSource, ThingifyTaskRevokabkle, new object[] { "someString" },
                                                                       GetCachingSettings(revokedResponseBehavior: RevokedResponseBehavior.KeepUsingRevokedResponse));
            result.Value.Id.ShouldBe(firstResult);
            dataSource.Received(1).ThingifyTaskRevokable("someString"); //Revoke received and item was not removed from cache - do not fetch from datasource
        }
        public async Task MemoizeAsync_RevokableObjectShouldBeCachedAndRevoked()
        {
            var dataSource = CreateRevokableDataSource(new[] { "revokeKey" }, 5, 6);

            var revokesSource = new OneTimeSynchronousSourceBlock <string>();

            var cache    = CreateCache(revokesSource);
            var memoizer = CreateMemoizer(cache);

            var actual = await(Task <Revocable <Thing> >) memoizer.Memoize(dataSource, ThingifyTaskRevokabkle, new object[] { "someString" }, GetPolicy());

            dataSource.Received(1).ThingifyTaskRevokable("someString");
            actual.Value.Id.ShouldBe(5);

            //Read value from cache should be still 5
            actual = await(Task <Revocable <Thing> >) memoizer.Memoize(dataSource, ThingifyTaskRevokabkle, new object[] { "someString" }, GetPolicy());
            dataSource.Received(1).ThingifyTaskRevokable("someString");
            actual.Value.Id.ShouldBe(5);
            //A single cache key should be stored in index
            cache.CacheKeyCount.ShouldBe(1);

            //Post revoke message, no cache keys should be stored
            revokesSource.PostMessageSynced("revokeKey");
            cache.CacheKeyCount.ShouldBe(0);

            //Value should change to 6
            actual = await(Task <Revocable <Thing> >) memoizer.Memoize(dataSource, ThingifyTaskRevokabkle, new object[] { "someString" }, GetPolicy());
            dataSource.Received(2).ThingifyTaskRevokable("someString");
            actual.Value.Id.ShouldBe(6);
            cache.CacheKeyCount.ShouldBe(1);

            //Post revoke message to not existing key value still should be 6
            revokesSource.PostMessageSynced("NotExistin-RevokeKey");

            actual = await(Task <Revocable <Thing> >) memoizer.Memoize(dataSource, ThingifyTaskRevokabkle, new object[] { "someString" }, GetPolicy());
            dataSource.Received(2).ThingifyTaskRevokable("someString");
            actual.Value.Id.ShouldBe(6);
            cache.CacheKeyCount.ShouldBe(1);
        }
        public async Task MemoizeAsync_ExistingItemReceivedRevokeAndSettingIsTryFetchNewValueNextTimeOrUseOld_AfterRevokeCacheIsNotUsedAndItemIsFetchedFromDataSource()
        {
            var firstResult   = "first result";
            var secondResult  = "second result";
            var dataSource    = CreateRevokableDataSource(new[] { "revokeKey" }, firstResult, secondResult);
            var revokesSource = new OneTimeSynchronousSourceBlock <string>();
            var cache         = CreateCache(revokesSource);
            var memoizer      = CreateMemoizer(cache);

            var result = await(Task <Revocable <Thing> >) memoizer.Memoize(dataSource, ThingifyTaskRevokabkle, new object[] { "someString" }, GetCachingSettings());

            result.Value.Id.ShouldBe(firstResult);
            dataSource.Received(1).ThingifyTaskRevokable("someString"); //New item - fetch from datasource

            //Post revoke message
            revokesSource.PostMessageSynced("revokeKey");

            //We want to test that revoked item is ignored and we fetch a new item from data source
            result = await(Task <Revocable <Thing> >) memoizer.Memoize(dataSource, ThingifyTaskRevokabkle, new object[] { "someString" },
                                                                       GetCachingSettings(revokedResponseBehavior: RevokedResponseBehavior.TryFetchNewValueNextTimeOrUseOld));
            result.Value.Id.ShouldBe(secondResult);
            dataSource.Received(2).ThingifyTaskRevokable("someString");
        }
Пример #13
0
        public async Task MemoizeAsync_RevokableObjectShouldBeCachedAndRevoked()
        {
            var dataSource = CreateRevokableDataSource(new[] { "revokeKey" }, 5, 6);

            var revokesSource = new OneTimeSynchronousSourceBlock <string>();

            var cache    = CreateCache(revokesSource);
            var memoizer = CreateMemoizer(cache);

            var actual = await CallWithMemoize(memoizer, dataSource);

            dataSource.Received(1).ThingifyTaskRevokable("someString");
            actual.Value.Id.ShouldBe(5);

            //Read value from cache should be still 5
            actual = await CallWithMemoize(memoizer, dataSource);

            dataSource.Received(1).ThingifyTaskRevokable("someString");
            actual.Value.Id.ShouldBe(5);
            //A single cache key should be stored in index
            cache.CacheKeyCount.ShouldBe(1);

            //No metric for Revoke
            GetMetricsData("Revoke").AssertEquals(new MetricsDataEquatable {
                Meters = new List <MetricDataEquatable> ()
            });

            //Post revoke message, no cache keys should be stored
            revokesSource.PostMessageSynced("revokeKey");
            cache.CacheKeyCount.ShouldBe(0);

            //Should have a single revoke in meter
            GetMetricsData("Revoke").AssertEquals(new MetricsDataEquatable
            {
                MetersSettings = new MetricsCheckSetting {
                    CheckValues = true
                },
                Meters = new List <MetricDataEquatable> {
                    new MetricDataEquatable {
                        Name = "Succeeded", Unit = Unit.Events, Value = 1
                    },
                }
            });

            //Should have a single item removed in meter
            GetMetricsData("Items").AssertEquals(new MetricsDataEquatable
            {
                MetersSettings = new MetricsCheckSetting {
                    CheckValues = true
                },
                Meters = new List <MetricDataEquatable> {
                    new MetricDataEquatable {
                        Name = CacheEntryRemovedReason.Removed.ToString(), Unit = Unit.Items, Value = 1
                    },
                }
            });


            //Value should change to 6
            actual = await CallWithMemoize(memoizer, dataSource);

            dataSource.Received(2).ThingifyTaskRevokable("someString");
            actual.Value.Id.ShouldBe(6);
            cache.CacheKeyCount.ShouldBe(1);

            //Post revoke message to not existing key value still should be 6
            revokesSource.PostMessageSynced("NotExistin-RevokeKey");


            actual = await CallWithMemoize(memoizer, dataSource);

            dataSource.Received(2).ThingifyTaskRevokable("someString");
            actual.Value.Id.ShouldBe(6);
            cache.CacheKeyCount.ShouldBe(1);
        }