Example #1
0
        public async Task InitializeCacheAsync()
        {
            // Depending on cache invalidation period and on asset pairs amount, there may be a case when
            // the invalidation timer fires before the cache loading has stopped. This will be a signal
            // to skip timer-based invalidation.
            // Below we combine two approaches:
            // - we're exporting a fast signal for any timer-based routine that we have already been initializing the cache;
            // - and we additionally block all the cache-related operations for other parts of code.
            // The first is needed to avoid queueing of timer events. If we simply use blocks, we will finally face a problem
            // when cache initialization may become infinitly repeated. The second is important for avoidining a multi-threaded
            // write operations to the cache: if we've got a candle update set, we need to await for cache fill completion and
            // then proceed.
            lock (_initializationStateLocker)
            {
                if (InitializationState == CacheInitializationState.InProgress)
                {
                    return;
                }

                InitializationState = CacheInitializationState.InProgress;
            }

            await _cacheSem.WaitAsync();

            try
            {
                _log.Info(nameof(InitializeCacheAsync), "Caching candles history...");

                SlotType activeSlot = await _candlesCacheService.GetActiveSlotAsync(_marketType);

                //initialize cache to inactive slot
                SlotType initSlot = activeSlot == SlotType.Slot0
                    ? SlotType.Slot1
                    : SlotType.Slot0;

                var assetPairs = await _assetPairsManager.GetAllAsync();

                var now = _clock.UtcNow;

                foreach (var pairs in assetPairs.Where(a => _candlesHistoryRepository.CanStoreAssetPair(a.Id)).Batch(6))
                {
                    var tasks = pairs.Select(p => CacheAssetPairCandlesAsync(p, now, initSlot));
                    await Task.WhenAll(tasks);
                }

                await _candlesCacheService.InjectCacheValidityToken();                // Initial validation token set.

                await _candlesCacheService.SetActiveSlotAsync(_marketType, initSlot); //switch slots

                _log.Info(nameof(InitializeCacheAsync), "All candles history is cached");
            }
            finally
            {
                InitializationState = CacheInitializationState.Idle;
                _cacheSem.Release();
            }
        }
Example #2
0
        public async Task CacheAsync(IReadOnlyList <ICandle> candles)
        {
            // To avoid possible race condition with cache initialization triggered by timer:
            await _cacheSem.WaitAsync();

            try
            {
                _healthService.TraceStartCacheCandles();

                // Transaction is needed here, despite of this method is non concurrent-safe,
                // without transaction at the moment candle can be missed or doubled
                // depending on the order of the remove/add calls

                var      transaction = _database.CreateTransaction();
                var      tasks       = new List <Task>();
                SlotType activeSlot  = await GetActiveSlotAsync(_market);

                foreach (var candle in candles)
                {
                    var key             = GetKey(_market, candle.AssetPairId, candle.PriceType, candle.TimeInterval, activeSlot);
                    var serializedValue = SerializeCandle(candle);

                    // Removes old candle

                    var currentCandleKey = candle.Timestamp.ToString(TimestampFormat);
                    var nextCandleKey    = candle.Timestamp.AddIntervalTicks(1, candle.TimeInterval)
                                           .ToString(TimestampFormat);

                    tasks.Add(transaction.SortedSetRemoveRangeByValueAsync(key, currentCandleKey, nextCandleKey,
                                                                           Exclude.Stop));

                    // Adds new candle

                    tasks.Add(transaction.SortedSetAddAsync(key, serializedValue, 0));
                }

                if (!await transaction.ExecuteAsync())
                {
                    throw new InvalidOperationException("Redis transaction is rolled back");
                }

                // Operations in the transaction can't be awaited before transaction is executed, so
                // saves tasks and waits they here, just to calm down the Resharper

                await Task.WhenAll(tasks);

                _healthService.TraceStopCacheCandles(candles.Count);
            }
            finally
            {
                _cacheSem.Release();
            }
        }