/// <inheritdoc/>
        internal override async Task <StepResult> ExecuteAsync(ChainedBlock nextChainedBlock, CancellationToken cancellationToken, bool disposeMode)
        {
            this.logger.LogTrace("({0}:'{1}',{2}:{3})", nameof(nextChainedBlock), nextChainedBlock, nameof(disposeMode), disposeMode);

            var context = new ProcessPendingStorageContext(this.logger, this.BlockStoreLoop, nextChainedBlock, cancellationToken);

            // Next block does not exist in pending storage, continue onto the download blocks step.
            if (!this.BlockStoreLoop.PendingStorage.ContainsKey(context.NextChainedBlock.HashBlock))
            {
                this.logger.LogTrace("(-)[NOT_FOUND]:{0}", StepResult.Next);
                return(StepResult.Next);
            }

            if (disposeMode)
            {
                StepResult lres = await this.ProcessWhenDisposingAsync(context);

                this.logger.LogTrace("(-)[DISPOSE]:{0}", lres);
                return(lres);
            }

            if (this.BlockStoreLoop.ChainState.IsInitialBlockDownload)
            {
                StepResult lres = await this.ProcessWhenInIBDAsync(context);

                this.logger.LogTrace("(-)[IBD]:{0}", lres);
                return(lres);
            }

            StepResult res = await this.ProcessWhenNotInIBDAsync(context);

            this.logger.LogTrace("(-):{0}", res);
            return(res);
        }
        /// <summary>
        /// When the node is in IBD wait for <see cref="BlockStoreLoop.PendingStorageBatchThreshold"/> to be true then continuously process all the blocks in <see cref="BlockStoreLoop.PendingStorage"/> until
        /// a stop condition is found, the blocks will be pushed to the repository in batches of size <see cref="BlockStoreLoop.MaxPendingInsertBlockSize"/>.
        /// </summary>
        private async Task <StepResult> ProcessWhenInIBDAsync(ProcessPendingStorageContext context)
        {
            this.logger.LogTrace("({0}:'{1}',{2}.{3}:{4})", nameof(context.NextChainedBlock), context.NextChainedBlock, nameof(this.BlockStoreLoop.PendingStorage), nameof(this.BlockStoreLoop.PendingStorage.Count), this.BlockStoreLoop.PendingStorage.Count);

            if (this.BlockStoreLoop.PendingStorage.Count < BlockStoreLoop.PendingStorageBatchThreshold)
            {
                return(StepResult.Continue);
            }

            while (context.CancellationToken.IsCancellationRequested == false)
            {
                StepResult result = this.PrepareNextBlockFromPendingStorage(context);
                if (result == StepResult.Stop)
                {
                    break;
                }

                if (context.PendingStorageBatchSize > BlockStoreLoop.MaxPendingInsertBlockSize)
                {
                    await this.PushBlocksToRepositoryAsync(context);
                }
            }

            if (context.PendingBlockPairsToStore.Any())
            {
                await this.PushBlocksToRepositoryAsync(context);
            }

            this.logger.LogTrace("(-):{0}", StepResult.Continue);
            return(StepResult.Continue);
        }
        /// <summary>
        /// Store missing blocks and remove them from pending blocks and set the Store's tip to <see cref="ProcessPendingStorageContext.NextChainedBlock"/>
        /// </summary>
        /// <param name="context"><see cref="ProcessPendingStorageContext"/></param>
        private async Task PushBlocksToRepositoryAsync(ProcessPendingStorageContext context)
        {
            this.logger.LogDebug(context.ToString());

            await this.BlockStoreLoop.BlockRepository.PutAsync(context.PendingBlockPairsToStore.First().ChainedBlock.HashBlock, context.PendingBlockPairsToStore.Select(b => b.Block).ToList());

            this.BlockStoreLoop.SetStoreTip(context.PendingBlockPairsToStore.First().ChainedBlock);

            context.PendingBlockPairToStore = null;
            context.PendingBlockPairsToStore.Clear();
            context.PendingStorageBatchSize = 0;
        }
        /// <summary>
        /// Tries to get and remove the next block from pending storage. If it exists
        /// then add it to <see cref="ProcessPendingStorageContext.PendingBlockPairsToStore"/>.
        /// This will also check if the next block can be processed.
        /// </summary>
        /// <param name="context"><see cref="ProcessPendingStorageContext"/></param>
        private StepResult PrepareNextBlockFromPendingStorage(ProcessPendingStorageContext context)
        {
            var blockIsInPendingStorage = this.BlockStoreLoop.PendingStorage.TryRemove(context.NextChainedBlock.HashBlock, out context.PendingBlockPairToStore);

            if (blockIsInPendingStorage)
            {
                context.PendingBlockPairsToStore.Push(context.PendingBlockPairToStore);
                context.PendingStorageBatchSize += context.PendingBlockPairToStore.Block.GetSerializedSize();
            }

            return(context.CanProcessNextBlock() ? StepResult.Next : StepResult.Stop);
        }
Пример #5
0
        /// <inheritdoc/>
        internal override async Task <StepResult> ExecuteAsync(ChainedHeader nextChainedHeader, CancellationToken cancellationToken, bool disposeMode)
        {
            this.logger.LogTrace("({0}:'{1}',{2}:{3})", nameof(nextChainedHeader), nextChainedHeader, nameof(disposeMode), disposeMode);

            var context = new ProcessPendingStorageContext(this.logger, this.BlockStoreLoop, nextChainedHeader, cancellationToken);

            // Next block does not exist in pending storage, continue onto the download blocks step.
            if (!this.BlockStoreLoop.PendingStorage.ContainsKey(context.NextChainedHeader.HashBlock))
            {
                this.logger.LogTrace("(-)[NOT_FOUND]:{0}", StepResult.Next);
                return(StepResult.Next);
            }

            // In case of IBD do not save every single block- persist them in batches.
            if (this.BlockStoreLoop.PendingStorage.Count < BlockStoreLoop.PendingStorageBatchThreshold &&
                !disposeMode && this.BlockStoreLoop.InitialBlockDownloadState.IsInitialBlockDownload())
            {
                return(StepResult.Stop);
            }

            while (!context.CancellationToken.IsCancellationRequested)
            {
                StepResult result = this.PrepareNextBlockFromPendingStorage(context);
                if (result == StepResult.Stop)
                {
                    break;
                }

                if (context.PendingStorageBatchSize > BlockStoreLoop.MaxPendingInsertBlockSize)
                {
                    await this.PushBlocksToRepositoryAsync(context).ConfigureAwait(false);
                }
            }

            if (context.PendingBlockPairsToStore.Any())
            {
                await this.PushBlocksToRepositoryAsync(context).ConfigureAwait(false);
            }

            return(StepResult.Continue);
        }
        /// <summary>
        /// When the node disposes, process all the blocks in <see cref="BlockStoreLoop.PendingStorage"/> until
        /// its empty
        /// </summary>
        private async Task <StepResult> ProcessWhenDisposingAsync(ProcessPendingStorageContext context)
        {
            this.logger.LogTrace("({0}:'{1}')", nameof(context.NextChainedBlock), context.NextChainedBlock);

            while (this.BlockStoreLoop.PendingStorage.Count > 0)
            {
                StepResult result = this.PrepareNextBlockFromPendingStorage(context);
                if (result == StepResult.Stop)
                {
                    break;
                }
            }

            if (context.PendingBlockPairsToStore.Any())
            {
                await this.PushBlocksToRepositoryAsync(context);
            }

            this.logger.LogTrace("(-):{0}", StepResult.Stop);
            return(StepResult.Stop);
        }
        /// <summary>
        /// When the node is NOT in IBD, process and push the blocks in <see cref="BlockStoreLoop.PendingStorage"/> immediately
        /// to the block repository without checking batch size.
        /// </summary>
        private async Task <StepResult> ProcessWhenNotInIBDAsync(ProcessPendingStorageContext context)
        {
            this.logger.LogTrace("({0}:'{1}')", nameof(context.NextChainedBlock), context.NextChainedBlock);

            while (context.CancellationToken.IsCancellationRequested == false)
            {
                StepResult result = this.PrepareNextBlockFromPendingStorage(context);
                if (result == StepResult.Stop)
                {
                    break;
                }
            }

            if (context.PendingBlockPairsToStore.Any())
            {
                await this.PushBlocksToRepositoryAsync(context);
            }

            this.logger.LogTrace("(-):{0}", StepResult.Continue);
            return(StepResult.Continue);
        }