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); }
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"); }
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); }