public CandlesCacheInitalizationService( ICandlesCacheSemaphore cacheSem, ILogFactory logFactory, IAssetPairsManager assetPairsManager, IClock clock, ICandlesCacheService candlesCacheService, ICandlesHistoryRepository candlesHistoryRepository, Dictionary <CandleTimeInterval, int> amountOfCandlesToStore, MarketType marketType, DateTime minDate) { _cacheSem = cacheSem ?? throw new ArgumentNullException(nameof(cacheSem)); if (logFactory == null) { throw new ArgumentNullException(nameof(logFactory)); } _log = logFactory.CreateLog(this); _assetPairsManager = assetPairsManager ?? throw new ArgumentNullException(nameof(assetPairsManager)); _clock = clock ?? throw new ArgumentNullException(nameof(clock)); _candlesCacheService = candlesCacheService ?? throw new ArgumentNullException(nameof(candlesCacheService)); _candlesHistoryRepository = candlesHistoryRepository ?? throw new ArgumentNullException(nameof(candlesHistoryRepository)); _amountOfCandlesToStore = amountOfCandlesToStore; _marketType = marketType; _minDate = minDate; InitializationState = CacheInitializationState.Idle; }
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(); } }