/// <summary> /// Finds and downloads blocks to store in the BlockRepository. /// <para> /// This method executes a chain of steps in order: /// <list> /// <item>1. Reorganise the repository</item> /// <item>2. Check if the block exists in store, if it does move on to the next block</item> /// <item>3. Process the blocks in pending storage</item> /// <item>4. Find and download blocks</item> /// </list> /// </para> /// <para> /// Steps return a <see cref="BlockStoreLoopStepResult"/> which either signals the While loop /// to break or continue execution. /// </para> /// </summary> /// <param name="cancellationToken">CancellationToken to check</param> /// <param name="disposeMode">This will <c>true</c> if the Flush() was called</param> /// <remarks> /// TODO: add support to BlockStoreLoop to unset LazyLoadingOn when not in IBD /// When in IBD we may need many reads for the block key without fetching the block /// So the repo starts with LazyLoadingOn = true, however when not anymore in IBD /// a read is normally done when a peer is asking for the entire block (not just the key) /// then if LazyLoadingOn = false the read will be faster on the entire block /// </remarks> private async Task DownloadAndStoreBlocks(CancellationToken cancellationToken, bool disposeMode = false) { while (!cancellationToken.IsCancellationRequested) { if (this.StoreTip.Height >= this.ChainState.HighestValidatedPoW?.Height) { break; } ChainedBlock nextChainedBlock = this.Chain.GetBlock(this.StoreTip.Height + 1); if (nextChainedBlock == null) { break; } if (this.blockStoreStats.CanLog) { this.blockStoreStats.Log(); } BlockStoreLoopStepResult result = await this.stepChain.Execute(nextChainedBlock, disposeMode, cancellationToken); if (result.ShouldBreak) { break; } if (result.ShouldContinue) { continue; } } }
/// <summary> /// Will cause the caller to Continue execution of the loop i.e. go onto the next item in the iteration. /// </summary> internal static BlockStoreLoopStepResult Continue() { var result = new BlockStoreLoopStepResult(); result.ShouldContinue = true; return(result); }
/// <summary> /// Will cause the caller to break out of the iteration. /// </summary> internal static BlockStoreLoopStepResult Break() { var result = new BlockStoreLoopStepResult(); result.ShouldBreak = true; return(result); }
/// <summary> /// Executes the chain of BlockStoreLoop steps. /// <para> /// Each step will return a BlockStoreLoopStepResult which will either: /// <list> /// <item>1: Break out of the ForEach</item> /// <item>2: Continue execution of the ForEach</item> /// </list> /// </para> /// </summary> /// <param name="nextChainedBlock">Next chained block to process</param> /// <param name="disposeMode">This will <c>true</c> if Flush() was called on the BlockStore</param> /// <param name="cancellationToken">Cancellation token to check</param> /// <returns>BlockStoreLoopStepResult</returns> internal async Task <BlockStoreLoopStepResult> Execute(ChainedBlock nextChainedBlock, bool disposeMode, CancellationToken cancellationToken) { var result = BlockStoreLoopStepResult.Next(); foreach (var step in this.steps) { var stepResult = await step.ExecuteAsync(nextChainedBlock, cancellationToken, disposeMode); if (stepResult.ShouldBreak || stepResult.ShouldContinue) { result = stepResult; break; } } return(result); }
/// <summary> /// Will cause the caller to execute the next line of code. /// </summary> internal static BlockStoreLoopStepResult Next() { var result = new BlockStoreLoopStepResult(); return(result); }