private async Task IndexAddressesContinuouslyAsync() { var watch = Stopwatch.StartNew(); while (!this.cancellation.IsCancellationRequested) { if (this.dateTimeProvider.GetUtcNow() - this.lastFlushTime > this.flushChangesInterval) { this.logger.LogDebug("Flushing changes."); this.SaveAll(); this.lastFlushTime = this.dateTimeProvider.GetUtcNow(); this.logger.LogDebug("Flush completed."); } if (this.cancellation.IsCancellationRequested) { break; } ChainedHeader nextHeader = this.consensusManager.Tip.GetAncestor(this.IndexerTip.Height + 1); if (nextHeader == null) { this.logger.LogDebug("Next header wasn't found. Waiting."); try { await Task.Delay(DelayTimeMs, this.cancellation.Token).ConfigureAwait(false); } catch (OperationCanceledException) { } continue; } if (nextHeader.Previous.HashBlock != this.IndexerTip.HashBlock) { ChainedHeader lastCommonHeader = nextHeader.FindFork(this.IndexerTip); this.logger.LogDebug("Reorganization detected. Rewinding till '{0}'.", lastCommonHeader); this.RewindAndSave(lastCommonHeader); continue; } // First try to see if it's prefetched. ChainedHeaderBlock prefetchedBlock = this.prefetchingTask == null ? null : await this.prefetchingTask.ConfigureAwait(false); Block blockToProcess; if (prefetchedBlock != null && prefetchedBlock.ChainedHeader == nextHeader) { blockToProcess = prefetchedBlock.Block; } else { blockToProcess = this.consensusManager.GetBlockData(nextHeader.HashBlock).Block; } if (blockToProcess == null) { this.logger.LogDebug("Next block wasn't found. Waiting."); try { await Task.Delay(DelayTimeMs, this.cancellation.Token).ConfigureAwait(false); } catch (OperationCanceledException) { } continue; } // Schedule prefetching of the next block; ChainedHeader headerToPrefetch = this.consensusManager.Tip.GetAncestor(nextHeader.Height + 1); if (headerToPrefetch != null) { this.prefetchingTask = Task.Run(() => this.consensusManager.GetBlockData(headerToPrefetch.HashBlock)); } watch.Restart(); bool success = this.ProcessBlock(blockToProcess, nextHeader); watch.Stop(); this.averageTimePerBlock.AddSample(watch.Elapsed.TotalMilliseconds); if (!success) { this.logger.LogDebug("Failed to process next block. Waiting."); try { await Task.Delay(DelayTimeMs, this.cancellation.Token).ConfigureAwait(false); } catch (OperationCanceledException) { } continue; } this.IndexerTip = nextHeader; } this.SaveAll(); }
private async Task IndexAddressesContinuouslyAsync() { Stopwatch watch = Stopwatch.StartNew(); while (!this.cancellation.IsCancellationRequested) { if (DateTime.Now - this.lastFlushTime > this.flushChangesInterval) { this.logger.LogDebug("Flushing changes."); lock (this.lockObject) { this.addressIndexRepository.SaveAllItems(); this.outpointsRepository.SaveAllItems(); this.tipDataStore.Update(this.tipData); } this.lastFlushTime = DateTime.Now; this.logger.LogDebug("Flush completed."); } if (this.cancellation.IsCancellationRequested) break; ChainedHeader nextHeader = this.consensusManager.Tip.GetAncestor(this.IndexerTip.Height + 1); if (nextHeader == null) { this.logger.LogDebug("Next header wasn't found. Waiting."); try { await Task.Delay(DelayTimeMs, this.cancellation.Token).ConfigureAwait(false); } catch (OperationCanceledException) { } continue; } if (nextHeader.Previous.HashBlock != this.IndexerTip.HashBlock) { ChainedHeader lastCommonHeader = nextHeader.FindFork(this.IndexerTip); this.logger.LogDebug("Reorganization detected. Rewinding till '{0}'.", lastCommonHeader); lock (this.lockObject) { // The cache doesn't really lend itself to handling a reorg very well. // Therefore, we leverage LiteDb's indexing capabilities to tell us // which records are for the affected blocks. // TODO: May also be efficient to run ProcessBlocks with inverted deposit flags instead, depending on size of reorg List<string> affectedAddresses = this.addressIndexRepository.GetAddressesHigherThanHeight(lastCommonHeader.Height); foreach (string address in affectedAddresses) { AddressIndexerData indexData = this.addressIndexRepository.GetOrCreateAddress(address); indexData.BalanceChanges.RemoveAll(x => x.BalanceChangedHeight > lastCommonHeader.Height); } // Rewind all the way back to the fork point. while (this.IndexerTip.HashBlock != lastCommonHeader.HashBlock) { this.outpointsRepository.Rewind(this.IndexerTip.HashBlock); this.IndexerTip = this.IndexerTip.Previous; } this.tipData.TipHashBytes = this.IndexerTip.HashBlock.ToBytes(); this.tipData.Height = this.IndexerTip.Height; } continue; } // First try to see if it's prefetched. ChainedHeaderBlock prefetchedBlock = this.prefetchingTask == null ? null : await this.prefetchingTask.ConfigureAwait(false); Block blockToProcess; if (prefetchedBlock != null && prefetchedBlock.ChainedHeader == nextHeader) blockToProcess = prefetchedBlock.Block; else blockToProcess = this.consensusManager.GetBlockData(nextHeader.HashBlock).Block; if (blockToProcess == null) { this.logger.LogDebug("Next block wasn't found. Waiting."); try { await Task.Delay(DelayTimeMs, this.cancellation.Token).ConfigureAwait(false); } catch (OperationCanceledException) { } continue; } // Schedule prefetching of the next block; ChainedHeader headerToPrefetch = this.consensusManager.Tip.GetAncestor(nextHeader.Height + 1); if (headerToPrefetch != null) this.prefetchingTask = Task.Run(() => this.consensusManager.GetBlockData(headerToPrefetch.HashBlock)); watch.Restart(); bool success = this.ProcessBlock(blockToProcess, nextHeader); watch.Stop(); this.averageTimePerBlock.AddSample(watch.Elapsed.TotalMilliseconds); if (!success) { this.logger.LogDebug("Failed to process next block. Waiting."); try { await Task.Delay(DelayTimeMs, this.cancellation.Token).ConfigureAwait(false); } catch (OperationCanceledException) { } continue; } this.IndexerTip = nextHeader; lock (this.lockObject) { this.tipData.TipHashBytes = this.IndexerTip.HashBlock.ToBytes(); this.tipData.Height = this.IndexerTip.Height; } } lock (this.lockObject) { this.addressIndexRepository.SaveAllItems(); this.outpointsRepository.SaveAllItems(); this.tipDataStore.Update(this.tipData); } }
private async Task IndexAddressesContinuouslyAsync() { DateTime lastFlushTime = DateTime.Now; try { while (!this.cancellation.IsCancellationRequested) { if (DateTime.Now - lastFlushTime > this.flushChangesInterval) { this.logger.LogDebug("Flushing changes."); lock (this.lockObject) { this.dataStore.Update(this.addressesIndex); } lastFlushTime = DateTime.Now; this.logger.LogDebug("Flush completed."); } if (this.cancellation.IsCancellationRequested) { break; } ChainedHeader nextHeader = this.consensusManager.Tip.GetAncestor(this.IndexerTip.Height + 1); if (nextHeader == null) { this.logger.LogDebug("Next header wasn't found. Waiting."); try { await Task.Delay(DelayTimeMs, this.cancellation.Token).ConfigureAwait(false); } catch (OperationCanceledException) { } continue; } if (nextHeader.Previous.HashBlock != this.IndexerTip.HashBlock) { ChainedHeader lastCommonHeader = nextHeader.FindFork(this.IndexerTip); this.logger.LogDebug("Reorg detected. Rewinding till '{0}'.", lastCommonHeader); lock (this.lockObject) { foreach (List <AddressBalanceChange> changes in this.addressesIndex.AddressChanges.Values) { changes.RemoveAll(x => x.BalanceChangedHeight > lastCommonHeader.Height); } } this.IndexerTip = lastCommonHeader; continue; } // Get next header block and process it. Block blockToProcess = this.consensusManager.GetBlockData(nextHeader.HashBlock).Block; if (blockToProcess == null) { this.logger.LogDebug("Next block wasn't found. Waiting."); try { await Task.Delay(DelayTimeMs, this.cancellation.Token).ConfigureAwait(false); } catch (OperationCanceledException) { } continue; } bool success = this.ProcessBlock(blockToProcess, nextHeader); if (!success) { this.logger.LogDebug("Failed to process next block. Waiting."); try { await Task.Delay(DelayTimeMs, this.cancellation.Token).ConfigureAwait(false); } catch (OperationCanceledException) { } continue; } this.IndexerTip = nextHeader; lock (this.lockObject) { this.addressesIndex.TipHashBytes = this.IndexerTip.HashBlock.ToBytes(); } } lock (this.lockObject) { this.dataStore.Update(this.addressesIndex); } } catch (Exception e) { this.logger.LogCritical(e.ToString()); } }