private void WriteTest(string name, Instruction[] instructions, string input, CacheBuilder inputCache, string output, CacheBuilder outputCache, int?nopBreakpointId) { Trace.WriteLine(name); // generate sourcecode var cw = new CodeWriter(new StringWriter()); cw.EmitDebugInfo = DebugEnabled; cw.EmitDebugBreakpoint = DebugEnabled && nopBreakpointId == null; // cw.Begin(); // cache initialized by cache builder foreach (var insn in instructions) { if (insn.Prototype == InstructionPrototypes.Nop && insn.Get(0) == nopBreakpointId) { cw.Write(new Instruction(InstructionPrototypes.Breakpoint)); } cw.Write(insn); } cw.End(); Trace.WriteLine(cw.Writer); this.DumpInfo("Input ", input, inputCache.Build(), inputCache.CachePointer); this.DumpInfo("Expected", output, outputCache.Build(), outputCache.CachePointer); // run code var intptr = new BrainfuckInterpreter(cw.Writer.ToString(), inputCache.Build()); intptr.Input = new StringReader(input); intptr.Output = new StringWriter(); intptr.CachePointer = inputCache.CachePointer; if (nopBreakpointId != null) { intptr.NextBreakpoint(); Assert.IsFalse(intptr.Terminated, "terminated"); } else { intptr.Run(); } this.DumpInfo("Actual ", intptr.Output.ToString(), intptr.Cache, intptr.CachePointer); // asserts var prefix = $"\nTestcase: {name}\n"; outputCache.CacheSize = inputCache.CacheSize; CollectionAssert.AreEqual(outputCache.Build(), intptr.Cache, prefix + "cache"); Assert.AreEqual(outputCache.CachePointer, intptr.CachePointer, prefix + "cache pointer"); Assert.AreEqual(output, intptr.Output.ToString(), prefix + "output"); }
public CachedFunctionWithSingleKey( Func <TParams, CancellationToken, ValueTask <TValue> > originalFunction, Func <TParams, TKey> keySelector, CachedFunctionWithSingleKeyConfiguration <TParams, TKey, TValue> config) { _originalFunction = originalFunction; _keySelector = keySelector; _onSuccessAction = config.OnSuccessAction; _onExceptionAction = config.OnExceptionAction; if (config.DisableCaching) { _cache = NullCache <TKey, TValue> .Instance; } else { if (config.TimeToLive.HasValue) { _timeToLive = config.TimeToLive.Value; } else { _timeToLiveFactory = config.TimeToLiveFactory; } _cache = CacheBuilder.Build(config); _skipCacheGetPredicate = config.SkipCacheGetPredicate; _skipCacheSetPredicate = config.SkipCacheSetPredicate; } _cacheStatsIfSkipped = GetCacheStatsIfSkipped(); _cacheEnabled = _cache.LocalCacheEnabled || _cache.DistributedCacheEnabled; _measurementsEnabled = _onSuccessAction != null || _onExceptionAction != null; }
public async Task Sample2WithFluentApi() { // This func is to fetch the value from the data source. In this case it // is just a random generator, but this could be anything like a web service or database. // The parameter should be the same type as your cache key type, and the return value // should be the same type as your cache's value type. Func <int, Task <string> > loaderFunction = async(key) => { int rnd = new Random().Next(); return(await Task.FromResult(string.Format("value{0}", rnd))); }; var cache = CacheBuilder.Build <int, string>() .WithName(nameof(Sample2WithFluentApi)) .CacheItemExpiryFromSeconds(1) .CacheItemExpiryPercentageRandomization(0) .FlushIntervalFromSeconds(5) .MaximumCacheSizeIndicator(100) .CircuitBreakerTimeoutForAdditionalThreadsPerKeyFromSeconds(10) .DisposeExpiredValuesIfDisposable() .LoaderFunction(loaderFunction) .Create(); // Record the first hit values for 100 keys var firstHitValues = new List <string>(); for (int key = 0; key < 100; key++) { var value = await cache.GetOrLoadAsync(key); firstHitValues.Add(value); } // Assert that 50 subsequent hits return the same values for all 100 keys. for (int i = 0; i < 50; i++) { for (int key = 0; key < 100; key++) { var value = await cache.GetOrLoadAsync(key); Assert.AreEqual(firstHitValues[key], value); } } // Wait a while so that all cache entries expire as per the configured options Thread.Sleep(1000); // Assert that the next hit returns fresh values. i.e. not equal to the first hit values. for (int key = 0; key < 100; key++) { var value = await cache.GetOrLoadAsync(key); Assert.AreNotEqual(firstHitValues[key], value); } }
public CachedFunctionWithOuterKeyAndInnerEnumerableKeys( Func <TParams, ReadOnlyMemory <TInnerKey>, CancellationToken, ValueTask <IEnumerable <KeyValuePair <TInnerKey, TValue> > > > originalFunction, Func <TParams, TOuterKey> keySelector, CachedFunctionWithOuterKeyAndInnerEnumerableKeysConfiguration <TParams, TOuterKey, TInnerKey, TValue> config) { _originalFunction = originalFunction; _keySelector = keySelector; _maxBatchSize = config.MaxBatchSize; _maxBatchSizeFactory = config.MaxBatchSizeFactory; _batchBehaviour = config.BatchBehaviour; _onSuccessAction = config.OnSuccessAction; _onExceptionAction = config.OnExceptionAction; _filterResponsePredicate = config.FilterResponsePredicate; if (config.DisableCaching) { _cache = NullCache <TOuterKey, TInnerKey, TValue> .Instance; } else { if (config.TimeToLive.HasValue) { _timeToLive = config.TimeToLive.Value; } else { _timeToLiveFactory = config.TimeToLiveFactory; } _cache = CacheBuilder.Build(config); _keyComparer = config.KeyComparer ?? EqualityComparer <TInnerKey> .Default; _skipCacheGetOuterPredicate = config.SkipCacheGetOuterPredicate; _skipCacheGetInnerPredicate = config.SkipCacheGetInnerPredicate; _skipCacheSetOuterPredicate = config.SkipCacheSetOuterPredicate; _skipCacheSetInnerPredicate = config.SkipCacheSetInnerPredicate; } if (config.FillMissingKeysConstantValue.IsSet) { _shouldFillMissingKeys = true; _shouldFillMissingKeysWithConstantValue = true; _fillMissingKeysConstantValue = config.FillMissingKeysConstantValue.Value; } else if (!(config.FillMissingKeysValueFactory is null)) { _shouldFillMissingKeys = true; _fillMissingKeysValueFactory = config.FillMissingKeysValueFactory; } _cacheEnabled = _cache.LocalCacheEnabled || _cache.DistributedCacheEnabled; _measurementsEnabled = _onSuccessAction != null || _onExceptionAction != null; }
public void Build_HappyPath_ReturnsCacheObject() { var result = _sut.Build(); result.Should().NotBeNull(); }
public async Task Sample3WithFluentApiAndRandomItemExpiry() { // This func is to fetch the value from the data source. In this case it // is just a random generator, but this could be anything like a web service or database. // The parameter should be the same type as your cache key type, and the return value // should be the same type as your cache's value type. Func <int, Task <string> > loaderFunction = async(key) => { int rnd = new Random().Next(); return(await Task.FromResult(string.Format("value{0}", rnd))); }; int expiryMs = 1000; var cache = CacheBuilder.Build <int, string>() .WithName(nameof(Sample3WithFluentApiAndRandomItemExpiry)) .CacheItemExpiryFromMilliseconds(expiryMs) .CacheItemExpiryPercentageRandomization(100) .FlushIntervalFromSeconds(5) .MaximumCacheSizeIndicator(100) .CircuitBreakerTimeoutForAdditionalThreadsPerKeyFromSeconds(10) .DisposeExpiredValuesIfDisposable() .LoaderFunction(loaderFunction) .Create(); // Record the first hit values for 100 keys var firstHitValues = new List <string>(); for (int key = 0; key < 100; key++) { var value = await cache.GetOrLoadAsync(key); firstHitValues.Add(value); } // Assert that 10 subsequent hits return the same values for all 100 keys. for (int i = 0; i < 10; i++) { for (int key = 0; key < 100; key++) { var value = await cache.GetOrLoadAsync(key); value.Should().Be(firstHitValues[key]); } } // Wait the expiry time so that about half the cache entries randomly expire as per the configured options await Task.Delay(expiryMs); // Assert that the next hit returns fresh values. i.e. not equal to the first hit values. int oldValueCount = 0; int newValueCount = 0; for (int key = 0; key < 100; key++) { var value = await cache.GetOrLoadAsync(key); if (value == firstHitValues[key]) { oldValueCount++; } else { newValueCount++; } } // Statistically, it should be about 50/50 split, but for this test to pass reliably, we considder 30% on either side success. oldValueCount.Should().BeGreaterOrEqualTo(30, "about half should have not yet expired"); newValueCount.Should().BeGreaterOrEqualTo(30, "about half should have expired already"); }