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);
            }
        }
Ejemplo n.º 3
0
        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());
            }
        }