/// <inheritdoc/> internal override async Task <StepResult> ExecuteAsync(ChainedBlock nextChainedBlock, CancellationToken token, bool disposeMode) { this.logger.LogTrace("({0}:'{1}',{2}:{3})", nameof(nextChainedBlock), nextChainedBlock, nameof(disposeMode), disposeMode); if (disposeMode) { this.logger.LogTrace("(-)[DISPOSE]:{0}", StepResult.Stop); return(StepResult.Stop); } var context = new BlockStoreInnerStepContext(token, this.BlockStoreLoop, nextChainedBlock, this.loggerFactory, this.dateTimeProvider); while (!token.IsCancellationRequested) { foreach (BlockStoreInnerStep innerStep in context.InnerSteps.ToList()) { InnerStepResult innerStepResult = await innerStep.ExecuteAsync(context); if (innerStepResult == InnerStepResult.Stop) { this.logger.LogTrace("(-)[INNER]:{0}", StepResult.Next); return(StepResult.Next); } } } this.logger.LogTrace("(-):{0}", StepResult.Next); return(StepResult.Next); }
/// <inheritdoc/> public override async Task <InnerStepResult> ExecuteAsync(BlockStoreInnerStepContext context) { var batchSize = BlockStoreInnerStepContext.DownloadStackThreshold - context.DownloadStack.Count; var batchList = new List <ChainedBlock>(batchSize); while (batchList.Count < batchSize) { if (await this.ShouldStopFindingBlocksAsync(context)) { context.StopFindingBlocks(); break; } batchList.Add(context.NextChainedBlock); context.DownloadStack.Enqueue(context.NextChainedBlock); context.GetNextBlock(); } if (batchList.Any()) { this.logger.LogTrace("{0} blocks requested to be downloaded by the puller.", batchList.Count); context.BlockStoreLoop.BlockPuller.AskForMultipleBlocks(batchList.ToArray()); } return(InnerStepResult.Next); }
/// <inheritdoc/> public override async Task <InnerStepResult> ExecuteAsync(BlockStoreInnerStepContext context) { if (!context.DownloadStack.Any()) { this.logger.LogTrace("(-)[EMPTY_STACK1]:{0}", InnerStepResult.Stop); return(InnerStepResult.Stop); } while (context.BlocksPushedCount <= BlockStoreInnerStepContext.DownloadStackPushThreshold) { DownloadedBlock downloadedBlock; ChainedBlock nextBlock = context.DownloadStack.Peek(); if (context.BlockStoreLoop.BlockPuller.TryGetBlock(nextBlock, out downloadedBlock)) { this.logger.LogTrace("Puller provided block '{0}', length {1}.", nextBlock, downloadedBlock.Length); ChainedBlock lastBlockToPush = this.AddDownloadedBlockToStore(context, downloadedBlock); if (this.ShouldBlocksBePushedToRepository(context)) { await this.PushBlocksToRepositoryAsync(context, lastBlockToPush); if (!context.DownloadStack.Any()) { this.logger.LogTrace("(-)[EMPTY_STACK2]:{0}", InnerStepResult.Stop); return(InnerStepResult.Stop); } } } else { if (context.StallCount > context.StallCountThreshold) { // Increase limit by 10 % to allow adjustments for low speed connections. // Eventually, the limit be high enough to allow normal operation. context.StallCountThreshold += context.StallCountThreshold / 10; this.logger.LogTrace("Stall count threshold increased to {0}.", context.StallCountThreshold); this.logger.LogTrace("(-)[STALLING]:{0}", InnerStepResult.Stop); return(InnerStepResult.Stop); } this.logger.LogTrace("Block '{0}' not available, stall count is {1}, waiting {2} ms...", nextBlock, context.StallCount, BlockStoreInnerStepContext.StallDelayMs); await Task.Delay(BlockStoreInnerStepContext.StallDelayMs, context.CancellationToken); context.StallCount++; } } context.BlocksPushedCount = 0; this.logger.LogTrace("(-):{0}", InnerStepResult.Next); return(InnerStepResult.Next); }
/// <summary> Adds the downloaded block to the store and resets the stall count.</summary> private ChainedBlock AddDownloadedBlockToStore(BlockStoreInnerStepContext context, DownloadedBlock downloadedBlock) { this.logger.LogTrace("({0}.{1}:{2})", nameof(downloadedBlock), nameof(downloadedBlock.Length), downloadedBlock.Length); ChainedBlock chainedBlockToStore = context.DownloadStack.Dequeue(); context.Store.Add(new BlockPair(downloadedBlock.Block, chainedBlockToStore)); context.InsertBlockSize += downloadedBlock.Length; context.StallCount = 0; this.logger.LogTrace("(-):'{0}'", chainedBlockToStore); return(chainedBlockToStore); }
/// <summary> /// Push (persist) the downloaded blocks to the block repository /// </summary> /// <param name="lastDownloadedBlock">Last block in the list to store, also used to set the store tip.</param> private async Task PushBlocksToRepositoryAsync(BlockStoreInnerStepContext context, ChainedBlock lastDownloadedBlock) { this.logger.LogTrace("()"); List <Block> blocksToStore = context.Store.Select(bp => bp.Block).ToList(); await context.BlockStoreLoop.BlockRepository.PutAsync(lastDownloadedBlock.HashBlock, blocksToStore); context.BlocksPushedCount += blocksToStore.Count; this.logger.LogTrace("{0} blocks pushed to the repository, {1} blocks pushed in total.", blocksToStore.Count, context.BlocksPushedCount); context.BlockStoreLoop.SetStoreTip(lastDownloadedBlock); context.InsertBlockSize = 0; context.LastDownloadStackFlushTime = context.DateTimeProvider.GetUtcNow(); context.Store.Clear(); this.logger.LogTrace("(-)"); }
/// <summary> Determines whether or not its time for <see cref="BlockStoreInnerStepReadBlocks"/> /// to push (persist) the downloaded blocks to the repository.</summary> private bool ShouldBlocksBePushedToRepository(BlockStoreInnerStepContext context) { this.logger.LogTrace("()"); DateTime now = context.DateTimeProvider.GetUtcNow(); uint lastFlushDiff = (uint)(now - context.LastDownloadStackFlushTime).TotalMilliseconds; bool pushBufferSizeReached = context.InsertBlockSize > BlockStoreLoop.MaxInsertBlockSize; bool downloadStackEmpty = !context.DownloadStack.Any(); bool pushTimeReached = lastFlushDiff > BlockStoreInnerStepContext.MaxDownloadStackFlushTimeMs; this.logger.LogTrace("Insert block size is {0} bytes{1}, download stack contains {2} blocks, last flush time was {3} ms ago{4}.", context.InsertBlockSize, pushBufferSizeReached ? " (threshold reached)" : "", context.DownloadStack.Count, lastFlushDiff, pushTimeReached ? " (threshold reached)" : ""); bool res = pushBufferSizeReached || downloadStackEmpty || pushTimeReached; this.logger.LogTrace("(-):{0}", res); return(res); }
private async Task <bool> ShouldStopFindingBlocksAsync(BlockStoreInnerStepContext context) { this.logger.LogTrace("()"); if (context.NextChainedBlock == null) { this.logger.LogTrace("(-)[NULL_NEXT]:true"); return(true); } if ((context.InputChainedBlock != null) && (context.NextChainedBlock.Header.HashPrevBlock != context.InputChainedBlock.HashBlock)) { this.logger.LogTrace("(-)[NEXT_NEQ_INPUT]:true"); return(true); } if (context.NextChainedBlock.Height > context.BlockStoreLoop.ChainState.ConsensusTip?.Height) { this.logger.LogTrace("(-)[NEXT_HEIGHT_GT_CONSENSUS_TIP]:true"); return(true); } if (context.BlockStoreLoop.PendingStorage.ContainsKey(context.NextChainedBlock.HashBlock)) { this.logger.LogTrace("Chained block '{0}' already exists in the pending storage.", context.NextChainedBlock); this.logger.LogTrace("(-)[NEXT_ALREADY_EXISTS_PENDING_STORE]:true"); return(true); } if (await context.BlockStoreLoop.BlockRepository.ExistAsync(context.NextChainedBlock.HashBlock)) { this.logger.LogTrace("Chained block '{0}' already exists in the repository.", context.NextChainedBlock); this.logger.LogTrace("(-)[NEXT_ALREADY_EXISTS_REPOSITORY]:true"); return(true); } this.logger.LogTrace("(-):false"); return(false); }
public abstract Task <InnerStepResult> ExecuteAsync(BlockStoreInnerStepContext context);