コード例 #1
0
        /// <inheritdoc />
        public override Task SaveChangesAsync(IEnumerable <UnspentOutputs> unspentOutputs, IEnumerable <TxOut[]> originalOutputs, uint256 oldBlockHash, uint256 nextBlockHash)
        {
            this.logger.LogTrace("({0}.Count():{1},{2}.Count():{3},{4}:'{5}',{6}:'{7}')", nameof(unspentOutputs), unspentOutputs?.Count(), nameof(originalOutputs), originalOutputs?.Count(), nameof(oldBlockHash), oldBlockHash, nameof(nextBlockHash), nextBlockHash);

            RewindData rewindData       = originalOutputs != null ? new RewindData(oldBlockHash) : null;
            int        insertedEntities = 0;

            List <UnspentOutputs>         all = unspentOutputs.ToList();
            Dictionary <uint256, TxOut[]> unspentToOriginal = new Dictionary <uint256, TxOut[]>(all.Count);

            using (new StopwatchDisposable(o => this.PerformanceCounter.AddInsertTime(o)))
            {
                if (originalOutputs != null)
                {
                    IEnumerator <TxOut[]> originalEnumerator = originalOutputs.GetEnumerator();
                    foreach (UnspentOutputs output in all)
                    {
                        originalEnumerator.MoveNext();
                        unspentToOriginal.Add(output.TransactionId, originalEnumerator.Current);
                    }
                }
            }

            Task task = Task.Run(() =>
            {
                this.logger.LogTrace("()");

                using (DBreeze.Transactions.Transaction transaction = this.dbreeze.GetTransaction())
                {
                    transaction.ValuesLazyLoadingIsOn = false;
                    transaction.SynchronizeTables("BlockHash", "Coins", "Rewind");
                    transaction.Technical_SetTable_OverwriteIsNotAllowed("Coins");

                    using (new StopwatchDisposable(o => this.PerformanceCounter.AddInsertTime(o)))
                    {
                        uint256 current = this.GetCurrentHash(transaction);
                        if (current != oldBlockHash)
                        {
                            this.logger.LogTrace("(-)[BLOCKHASH_MISMATCH]");
                            throw new InvalidOperationException("Invalid oldBlockHash");
                        }

                        this.SetBlockHash(transaction, nextBlockHash);

                        all.Sort(UnspentOutputsComparer.Instance);
                        foreach (UnspentOutputs coin in all)
                        {
                            this.logger.LogTrace("Outputs of transaction ID '{0}' are {1} and will be {2} to the database.", coin.TransactionId, coin.IsPrunable ? "PRUNABLE" : "NOT PRUNABLE", coin.IsPrunable ? "removed" : "inserted");
                            if (coin.IsPrunable)
                            {
                                transaction.RemoveKey("Coins", coin.TransactionId.ToBytes(false));
                            }
                            else
                            {
                                transaction.Insert("Coins", coin.TransactionId.ToBytes(false), coin.ToCoins());
                            }

                            if (originalOutputs != null)
                            {
                                TxOut[] original = null;
                                unspentToOriginal.TryGetValue(coin.TransactionId, out original);
                                if (original == null)
                                {
                                    // This one haven't existed before, if we rewind, delete it.
                                    rewindData.TransactionsToRemove.Add(coin.TransactionId);
                                }
                                else
                                {
                                    // We'll need to restore the original outputs.
                                    UnspentOutputs clone = coin.Clone();
                                    clone._Outputs       = original.ToArray();
                                    rewindData.OutputsToRestore.Add(clone);
                                }
                            }
                        }

                        if (rewindData != null)
                        {
                            int nextRewindIndex = this.GetRewindIndex(transaction) + 1;
                            this.logger.LogTrace("Rewind state #{0} created.", nextRewindIndex);
                            transaction.Insert("Rewind", nextRewindIndex, rewindData);
                        }

                        insertedEntities += all.Count;
                        transaction.Commit();
                    }
                }

                this.PerformanceCounter.AddInsertedEntities(insertedEntities);
                this.logger.LogTrace("(-)");
            });

            this.logger.LogTrace("(-)");
            return(task);
        }
コード例 #2
0
        /// <inheritdoc />
        public Task SaveChangesAsync(IList <UnspentOutputs> unspentOutputs, IEnumerable <TxOut[]> originalOutputs, uint256 oldBlockHash, uint256 nextBlockHash, int height, List <RewindData> rewindDataList = null)
        {
            Task task = Task.Run(() =>
            {
                int insertedEntities = 0;

                using (DBreeze.Transactions.Transaction transaction = this.CreateTransaction())
                {
                    transaction.ValuesLazyLoadingIsOn = false;
                    transaction.SynchronizeTables("BlockHash", "Coins", "Rewind");

                    // Speed can degrade when keys are in random order and, especially, if these keys have high entropy.
                    // This settings helps with speed, see dBreeze documentations about details.
                    // We should double check if this settings help in our scenario, or sorting keys and operations is enough.
                    // Refers to issue #2483. https://github.com/stratisproject/StratisBitcoinFullNode/issues/2483
                    transaction.Technical_SetTable_OverwriteIsNotAllowed("Coins");

                    using (new StopwatchDisposable(o => this.performanceCounter.AddInsertTime(o)))
                    {
                        uint256 current = this.GetTipHash(transaction);
                        if (current != oldBlockHash)
                        {
                            this.logger.LogTrace("(-)[BLOCKHASH_MISMATCH]");
                            throw new InvalidOperationException("Invalid oldBlockHash");
                        }

                        this.SetBlockHash(transaction, nextBlockHash);

                        // Here we'll add items to be inserted in a second pass.
                        List <UnspentOutputs> toInsert = new List <UnspentOutputs>();

                        foreach (var coin in unspentOutputs.OrderBy(utxo => utxo.TransactionId, new UInt256Comparer()))
                        {
                            if (coin.IsPrunable)
                            {
                                this.logger.LogTrace("Outputs of transaction ID '{0}' are prunable and will be removed from the database.", coin.TransactionId);
                                transaction.RemoveKey("Coins", coin.TransactionId.ToBytes(false));
                            }
                            else
                            {
                                // Add the item to another list that will be used in the second pass.
                                // This is for performance reasons: dBreeze is optimized to run the same kind of operations, sorted.
                                toInsert.Add(coin);
                            }
                        }

                        for (int i = 0; i < toInsert.Count; i++)
                        {
                            var coin = toInsert[i];
                            this.logger.LogTrace("Outputs of transaction ID '{0}' are NOT PRUNABLE and will be inserted into the database. {1}/{2}.", coin.TransactionId, i, toInsert.Count);

                            transaction.Insert("Coins", coin.TransactionId.ToBytes(false), coin.ToCoins());
                        }

                        if (rewindDataList != null)
                        {
                            int nextRewindIndex = this.GetRewindIndex(transaction) + 1;
                            foreach (RewindData rewindData in rewindDataList)
                            {
                                this.logger.LogTrace("Rewind state #{0} created.", nextRewindIndex);

                                transaction.Insert("Rewind", nextRewindIndex, rewindData);
                                nextRewindIndex++;
                            }
                        }

                        insertedEntities += unspentOutputs.Count;
                        transaction.Commit();
                    }
                }

                this.performanceCounter.AddInsertedEntities(insertedEntities);
            });

            return(task);
        }