public void TestAverageMeasure()
        {
            var sampleCutoff     = TimeSpan.FromMilliseconds(5000);
            var sampleResolution = TimeSpan.FromMilliseconds(100);

            using (var averageMeasure = new AverageMeasure(sampleCutoff, sampleResolution))
            {
                // add samples
                var count = 5;
                var value = 10;
                for (var i = 0; i < count; i++)
                {
                    averageMeasure.Tick(value);
                }

                // wait half the cutoff time and verify average
                Thread.Sleep(new TimeSpan(sampleCutoff.Ticks / 2));
                Assert.AreEqual((int)value, (int)averageMeasure.GetAverage());

                // wait for the cutoff time to pass and verify the average dropped to 0
                Thread.Sleep(sampleCutoff);
                Assert.AreEqual(0, (int)averageMeasure.GetAverage());

                // add new samples
                value = 50;
                for (var i = 0; i < count; i++)
                {
                    averageMeasure.Tick(value);
                }

                // verify average
                Assert.AreEqual((int)value, (int)averageMeasure.GetAverage());
            }
        }
        public void TestAverageMeasure()
        {
            var sampleCutoff = TimeSpan.FromMilliseconds(5000);
            var sampleResolution = TimeSpan.FromMilliseconds(100);
            using (var averageMeasure = new AverageMeasure(sampleCutoff, sampleResolution))
            {
                // add samples
                var count = 5;
                var value = 10;
                for (var i = 0; i < count; i++)
                {
                    averageMeasure.Tick(value);
                }

                // wait half the cutoff time and verify average
                Thread.Sleep(new TimeSpan(sampleCutoff.Ticks / 2));
                Assert.AreEqual((int)value, (int)averageMeasure.GetAverage());

                // wait for the cutoff time to pass and verify the average dropped to 0
                Thread.Sleep(sampleCutoff);
                Assert.AreEqual(0, (int)averageMeasure.GetAverage());

                // add new samples
                value = 50;
                for (var i = 0; i < count; i++)
                {
                    averageMeasure.Tick(value);
                }

                // verify average
                Assert.AreEqual((int)value, (int)averageMeasure.GetAverage());
            }
        }
        public override string ToString()
        {
            var statString = new StringBuilder();

            TimeSpan duration;

            lock (durationStopwatch)
                duration = durationStopwatch.Elapsed;

            var durationFormatted = $"{Math.Floor(duration.TotalHours):#,#00}:{duration:mm':'ss}";

            statString.AppendLine($"Chain State Builder Stats");
            statString.AppendLine($"-------------------------");
            statString.AppendLine($"Height:           {Height,15:N0}");
            statString.AppendLine($"Duration:         {durationFormatted,15}");
            statString.AppendLine($"-------------------------");
            statString.AppendLine($"Blocks Rate:      {blockRateMeasure.GetAverage(),15:N0}/s");
            statString.AppendLine($"Tx Rate:          {txRateMeasure.GetAverage(),15:N0}/s");
            statString.AppendLine($"Input Rate:       {inputRateMeasure.GetAverage(),15:N0}/s");
            statString.AppendLine($"-------------------------");
            statString.AppendLine($"Txes per block:   {txesPerBlockMeasure.GetAverage(),15:N0}");
            statString.AppendLine($"Inputs per block: {inputsPerBlockMeasure.GetAverage(),15:N0}");
            statString.AppendLine($"-------------------------");
            statString.AppendLine($"Processed Txes:   {TotalTxCount,15:N0}");
            statString.AppendLine($"Processed Inputs: {TotalInputCount,15:N0}");
            statString.AppendLine($"Utx Size:         {UnspentTxCount,15:N0}");
            statString.AppendLine($"Utxo Size:        {UnspentOutputCount,15:N0}");
            statString.AppendLine($"-------------------------");

            var txesReadDuration      = txesReadDurationMeasure.GetAverage();
            var txesDecodeDuration    = txesDecodeDurationMeasure.GetAverage();
            var lookAheadDuration     = lookAheadDurationMeasure.GetAverage();
            var calculateUtxoDuration = calculateUtxoDurationMeasure.GetAverage();
            var applyUtxoDuration     = applyUtxoDurationMeasure.GetAverage();
            var validateDuration      = validateDurationMeasure.GetAverage();
            var commitUtxoDuration    = commitUtxoDurationMeasure.GetAverage();
            var addBlockDuration      = addBlockDurationMeasure.GetAverage();

            statString.AppendLine(GetPipelineStat("Block Txes Read", txesReadDuration, TimeSpan.Zero));
            statString.AppendLine(GetPipelineStat("Block Txes Decode", txesDecodeDuration, txesReadDuration));
            statString.AppendLine(GetPipelineStat("UTXO Look-ahead", lookAheadDuration, txesDecodeDuration));
            statString.AppendLine(GetPipelineStat("UTXO Calculation", calculateUtxoDuration, lookAheadDuration));
            statString.AppendLine(GetPipelineStat("UTXO Application", applyUtxoDuration, calculateUtxoDuration));
            statString.AppendLine(GetPipelineStat("Block Validation", validateDuration, calculateUtxoDuration));
            statString.AppendLine(GetPipelineStat("UTXO Commit", commitUtxoDuration,
                                                  TimeSpan.FromTicks(Math.Max(applyUtxoDuration.Ticks, validateDuration.Ticks))));
            statString.Append(GetPipelineStat("AddBlock Total", addBlockDuration, null));

            return(statString.ToString());
        }
Beispiel #4
0
        protected override async Task WorkAction()
        {
            // check if pruning is turned off
            var mode = this.Mode;

            if (mode == PruningMode.None)
            {
                return;
            }

            // navigate from the current pruned chain towards the current processed chain
            foreach (var pathElement in this.prunedChain.ToImmutable().NavigateTowards(() => this.coreDaemon.CurrentChain))
            {
                // cooperative loop
                if (!this.IsStarted)
                {
                    break;
                }

                // get candidate block to be pruned
                var direction     = pathElement.Item1;
                var chainedHeader = pathElement.Item2;

                // get the current processed chain
                var processedChain = this.coreDaemon.CurrentChain;

                // determine maximum safe pruning height, based on a buffer distance from the processed chain height
                var blocksPerDay = 144;
                //TODO there should also be a buffer between when blocks are pruned, and when the pruning information
                //TODO to perform that operation is removed (tx index, spent txes)
                var pruneBuffer = blocksPerDay * 7;
                var maxHeight   = processedChain.Height - pruneBuffer;
                //TODO
                maxHeight = Math.Min(maxHeight, this.PrunableHeight);

                // check if this block is safe to prune
                if (chainedHeader.Height > maxHeight)
                {
                    break;
                }

                if (direction > 0)
                {
                    // prune the block, except genesis block
                    if (chainedHeader.Height > 0)
                    {
                        await this.PruneBlock(mode, processedChain, chainedHeader);
                    }

                    // track pruned block
                    this.prunedChain.AddBlock(chainedHeader);
                }
                else if (direction < 0)
                {
                    // pruning should not roll back, the buffer is in place to prevent this on orphans
                    throw new InvalidOperationException();
                }
                else
                {
                    throw new InvalidOperationException();
                }

                // pause chain state processing when pruning lags too far behind
                var isLagging = maxHeight - chainedHeader.Height > 100;
                if (isLagging)
                {
                    Throttler.IfElapsed(TimeSpan.FromSeconds(5), () =>
                    {
                        logger.Info("Pruning is lagging.");
                        lagLogged = true;
                    });

                    //TODO better way to block chain state worker when pruning is behind
                    if (this.chainStateWorker?.IsStarted ?? false)
                    {
                        this.chainStateWorker.Stop(TimeSpan.Zero);
                    }
                }
                else
                {
                    //TODO better way to block chain state worker when pruning is behind
                    if (!(this.chainStateWorker?.IsStarted ?? true))
                    {
                        this.chainStateWorker.NotifyAndStart();
                        if (lagLogged)
                        {
                            logger.Info("Pruning is caught up.");
                            lagLogged = false;
                        }
                    }
                }

                // log pruning stats periodically
                Throttler.IfElapsed(TimeSpan.FromSeconds(15), () =>
                {
                    logger.Info(string.Join(Environment.NewLine,
                                            $"Pruned from block {lastLogHeight:N0} to {chainedHeader.Height:N0}:",
                                            $"- avg tx rate:    {txRateMeasure.GetAverage(),8:N0}/s",
                                            $"- per block stats:",
                                            $"- tx count:       {txCountMeasure.GetAverage(),8:N0}",
                                            $"- prune blocks:   {pruneBlockTxesDurationMeasure.GetAverage().TotalMilliseconds,12:N3}ms",
                                            $"- prune index:    {pruneTxIndexDurationMeasure.GetAverage().TotalMilliseconds,12:N3}ms",
                                            $"- prune spent:    {pruneSpentTxesDurationMeasure.GetAverage().TotalMilliseconds,12:N3}ms",
                                            $"- TOTAL:          {totalDurationMeasure.GetAverage().TotalMilliseconds,12:N3}ms"
                                            ));

                    lastLogHeight = chainedHeader.Height + 1;
                });
            }

            // ensure chain state processing is resumed
            if (!(this.chainStateWorker?.IsStarted ?? true))
            {
                this.chainStateWorker.NotifyAndStart();
            }
        }