From 3f366fbc17354cf701e325a15d4094f5dbed6158 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Mon, 18 Sep 2017 13:16:35 -0500 Subject: [PATCH] chain: Remove memory block node pruning. This removes the memory block node (header) pruning. It should be noted that this does not apply to stake node pruning. This is being done for two primary reasons: - The goal is to ultimately have all block nodes in memory in the same way the upstream code has done since it provides significant optimization and code simplification opportunities - Upcoming code that deals with calculating sequence locks on inputs far in the past requires the ability to quickly calculate the median time for arbitrarily old nodes and consequently pruning the memory block nodes could lead to significant performance implications under those conditions --- blockchain/accept.go | 9 ++-- blockchain/chain.go | 101 ------------------------------------------- blockchain/prune.go | 7 ++- 3 files changed, 6 insertions(+), 111 deletions(-) diff --git a/blockchain/accept.go b/blockchain/accept.go index fbe23a22..2e769169 100644 --- a/blockchain/accept.go +++ b/blockchain/accept.go @@ -256,13 +256,10 @@ func (b *BlockChain) maybeAcceptBlock(block *dcrutil.Block, flags BehaviorFlags) return false, err } - // Prune stake nodes and block nodes which are no longer needed before - // creating a new node. + // Prune stake nodes which are no longer needed before creating a new + // node. if !dryRun { - err := b.pruner.pruneChainIfNeeded() - if err != nil { - return false, err - } + b.pruner.pruneChainIfNeeded() } // Create a new block node for the block and add it to the in-memory diff --git a/blockchain/chain.go b/blockchain/chain.go index 26e9ba7c..73672b80 100644 --- a/blockchain/chain.go +++ b/blockchain/chain.go @@ -890,94 +890,6 @@ func (b *BlockChain) GetTopBlock() (*dcrutil.Block, error) { return block, err } -// removeBlockNode removes the passed block node from the memory chain by -// unlinking all of its children and removing it from the the node and -// dependency indices. -// -// This function MUST be called with the chain state lock held (for writes). -func (b *BlockChain) removeBlockNode(node *blockNode) error { - if node.parent != nil { - return AssertError(fmt.Sprintf("removeBlockNode must be "+ - "called with a node at the front of the chain - node %v", - node.hash)) - } - - // Remove the node from the node index. - delete(b.index, node.hash) - - // Unlink all of the node's children. - for _, child := range node.children { - child.parent = nil - } - node.children = nil - - // Remove the reference from the dependency index. - prevHash := &node.header.PrevBlock - if children, ok := b.depNodes[*prevHash]; ok { - // Find the node amongst the children of the - // dependencies for the parent hash and remove it. - b.depNodes[*prevHash] = removeChildNode(children, node) - - // Remove the map entry altogether if there are no - // longer any nodes which depend on the parent hash. - if len(b.depNodes[*prevHash]) == 0 { - delete(b.depNodes, *prevHash) - } - } - - return nil -} - -// pruneBlockNodes removes references to old block nodes which are no longer -// needed so they may be garbage collected. In order to validate block rules -// and choose the best chain, only a portion of the nodes which form the block -// chain are needed in memory. This function walks the chain backwards from the -// current best chain to find any nodes before the first needed block node. -// -// This function MUST be called with the chain state lock held (for writes). -func (b *BlockChain) pruneBlockNodes() error { - // Walk the chain backwards to find what should be the new root node. - // Intentionally use node.parent instead of getPrevNodeFromNode since - // the latter loads the node and the goal is to find nodes still in - // memory that can be pruned. - newRootNode := b.bestNode - for i := int64(0); i < minMemoryNodes-1 && newRootNode != nil; i++ { - newRootNode = newRootNode.parent - } - - // Nothing to do if there are not enough nodes. - if newRootNode == nil || newRootNode.parent == nil { - return nil - } - - // Push the nodes to delete on a list in reverse order since it's easier - // to prune them going forwards than it is backwards. This will - // typically end up being a single node since pruning is currently done - // just before each new node is created. However, that might be tuned - // later to only prune at intervals, so the code needs to account for - // the possibility of multiple nodes. - deleteNodes := list.New() - for node := newRootNode.parent; node != nil; node = node.parent { - deleteNodes.PushFront(node) - } - - // Loop through each node to prune, unlink its children, remove it from - // the dependency index, and remove it from the node index. - for e := deleteNodes.Front(); e != nil; e = e.Next() { - node := e.Value.(*blockNode) - // Do not attempt to prune if the node should already have been pruned, - // for example if you're adding an old side chain block. - if node.height > b.bestNode.height-minMemoryNodes { - err := b.removeBlockNode(node) - if err != nil { - return err - } - } - } - - return nil -} - // pruneStakeNodes removes references to old stake nodes which should no // longer be held in memory so as to keep the maximum memory usage down. // It proceeds from the bestNode back to the determined minimum height node, @@ -1024,19 +936,6 @@ func (b *BlockChain) pruneStakeNodes() { } } -// pruneNodes tranverses the blockchain and prunes nodes and stake data from -// memory so that the memory can be recovered by the garbage collector. This -// allows the caller of the blockchain to manually handle GC related to the -// blockchain. -// -// This function is NOT safe for concurrent access and must be called with -// the chain lock held for writes. -func (b *BlockChain) pruneNodes() error { - b.pruneStakeNodes() - - return b.pruneBlockNodes() -} - // BestPrevHash returns the hash of the previous block of the block at HEAD. // // This function is safe for concurrent access. diff --git a/blockchain/prune.go b/blockchain/prune.go index f861efd4..91a9d580 100644 --- a/blockchain/prune.go +++ b/blockchain/prune.go @@ -27,14 +27,13 @@ func newChainPruner(chain *BlockChain) *chainPruner { // If the blockchain hasn't been pruned in this time, it initiates a new pruning. // // pruneChainIfNeeded must be called with the chainLock held for writes. -func (c *chainPruner) pruneChainIfNeeded() error { +func (c *chainPruner) pruneChainIfNeeded() { now := time.Now() duration := now.Sub(c.lastNodeInsertTime) if duration < time.Minute*pruningIntervalInMinutes { - return nil + return } c.lastNodeInsertTime = now - - return c.chain.pruneNodes() + c.chain.pruneStakeNodes() }