public void SelectorsToRedisValues() { var selectors = Enumerable.Range(0, 3).Select(_ => Selector.Random()).ToList(); var redisValues = _serializer.ToRedisValues(selectors); var expected = selectors.Select(selector => selector.ContentHash.Serialize() + RedisSerializer.RedisValueSeparator + HexUtilities.BytesToHex(selector.Output)).ToArray(); redisValues.Should().Equal(expected); }
public async Task TestGetOrAddSelectorsExisting() { var selectors = new[] { Selector.Random(), Selector.Random(), }; var weakFp = Fingerprint.Random(); var redisValues = new Dictionary <RedisKey, RedisValue[]> { { _redisSerializer.ToRedisKey(weakFp).Prepend(RedisNameSpace), _redisSerializer.ToRedisValues(selectors) }, }; using (var mockDb = new MockRedisDatabase( SystemClock.Instance, setData: redisValues)) { await RunTest( mockDb, async (context, metadataCache, redisDb) => { var selectorResult = await metadataCache.GetOrAddSelectorsAsync( context, weakFp, fp => { throw new InvalidOperationException( "GetFunc not expected to be called since data is already present in the cache"); }).ShouldBeSuccess(); // Check result Assert.Equal(2, selectorResult.Value.Length); foreach (Selector result in selectorResult.Value) { Assert.True(selectors.Contains(result)); } }); } }
private async Task <Result <Selector[]> > GetOrAddSelectorsAsync( Context context, Fingerprint weakFingerprint, Func <Fingerprint, Task <Result <Selector[]> > > getFunc, CancellationToken cancellationToken) { try { var cacheKey = _redisSerializer.ToRedisKey(weakFingerprint); RedisValue[] cacheSelectors; Stopwatch stopwatch = Stopwatch.StartNew(); try { _cacheTracer.GetDistributedSelectorsStart(context); cacheSelectors = await GetSetMembersWithExpiryBumpAsync(context, cacheKey); } finally { _cacheTracer.GetDistributedSelectorsStop(context, stopwatch.Elapsed); } if (cacheSelectors.Length == 0) { _cacheTracer.RecordSelectorsFetchedFromBackingStore(context, weakFingerprint); Result <Selector[]> selectorResults = await getFunc(weakFingerprint).ConfigureAwait(false); if (!selectorResults || selectorResults.Value.Length == 0) { // Redis throws an error if set add is called without any values. Also, undefined keys are treated as empty sets. // So currently skip trying to cache GetSelectors returning empty. // If needed, we need to create a sentinel value to denote an known empty set v/s undefined. return(selectorResults); } { var selectors = selectorResults.Value; var cacheValues = _redisSerializer.ToRedisValues(selectors); stopwatch = Stopwatch.StartNew(); try { _cacheTracer.AddSelectorsStart(context); var addedCount = await AddSetMembersWithExpiryBumpAsync(context, cacheKey, cacheValues); if (cacheValues.Length != addedCount) { _tracer.Warning(context, $"Expected to add {cacheValues.Length} members but actually added {addedCount}."); } _tracer.Debug(context, $"Added redis cache entry for {weakFingerprint}: {selectors.Length} selectors."); } finally { _cacheTracer.AddSelectorsStop(context, stopwatch.Elapsed); } } return(selectorResults); } else { Selector[] result = _redisSerializer.AsSelectors(cacheSelectors).ToArray(); _cacheTracer.RecordSelectorsFetchedDistributed(context, weakFingerprint, result.Length); return(result); } } catch (Exception ex) { return(Result.FromException <Selector[]>(ex)); } }