-
Notifications
You must be signed in to change notification settings - Fork 278
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[consensus] Enable Urkel Tree compaction #669
Conversation
Maybe include #675 |
6f85926
to
604f41a
Compare
I'm marking this as ready for review now by solving the last problem: What is the best method for deployment?There are now two ways to do this:
These options give node runners and integrated apps like Bob Wallet the flexibility to compact the tree either automatically or manually, either way with the node operator knowing when it will happen. I think this is a better method then setting some disk size threshold, etc which will run the compactor at unexpected times. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Didn't read through all the tests extremely carefully but they do seem like good starting coverage and documentation overall.
Might be nice to add a little more log reporting about how well it's working when applied. Might help node operators with health monitoring or to detect efficiency regressions etc.
This looks like a great improvement, and I don't see any issues with the diff as written (take that with a grain of salt due to my unfamiliarity with the overall codebase and code conventions). Another tangential question came to mind while reviewing: Are tree updates computed incrementally per block or is it an all-at-once tree update process once every interval? Is that something to consider further optimizations on in either direction? |
Changes to the Urkel Tree are stored in memory between tree intervals and written to disk every 36 blocks. This is an optimization and described in the white paper. If you shut down your node between tree intervals the data on disk will actually be behind. That's why we call syncTree() on startup to refill the tree data in memory from the block data on disk. |
Gotcha, so there's already not much else to be done there unless you wanted to have hyper-fine configuration of what goes into memory vs disk at what times. |
Worth pointing out that 0ddd228 makes this PR NOT backwards compatible. After running on my own local hsd node and compacting the tree, I then switched back to master branch to work on something else and got - this.put(layout.s.encode(), this.tree.rootHash()); so we would still write the treeRoot to the DB even though we no longer use it. I think this would alleviate some cases but we should still consider this a major breaking change. |
TODO:
|
@nodech I removed the RPC |
GeneralThe first thing I was thinking about is, whether we are still Full Node after So I would say, initially support this for prunes only and if someone I think, it's better to support Atomicity and
|
This will call But I fixed that with an |
@nodech thank you so much for your brilliant review and great catch regarding I also added a test in commit 80baf34 to cover the "failed connect()" case as you describe. You were right, the code totally failed that test until |
Mainnet integration testing is going well, working with prune and full nodes and combinations of the new options. Urkel reliably drops from 15GB to ~1GB. Even synced a fresh mainnet chain and restarted every 1000 blocks to keep tree size low throughout entire IBD. Only issue so far is the migration in SPV mode |
Fantastic work on this. Benchmarks are very promising. HSD is getting really lean for node operators! |
4fe79bc
to
3e570fc
Compare
ACK 6e3a113 Show Signature
pinheadmz's public key is on keybase |
Add reconstruct tree RPC call. Make TreeState safe.
chain: clean up chain.open
Minor nits and updates.
ACK 74cc006 Show Signature
pinheadmz's public key is on keybase |
Closes #660
Adds new rpc method
compacttree
which deletes from disk an enormous amount of historical data in the Urkel Tree that is only necessary in the case that a chain reorganization occurs deeper than ~288 blocks. Note that this condition also applies to blockchain-pruning nodes and in the event of such a deep reorg, a pruned node will fail into an unrecoverable state. Pruning is available as a configuration setting or can be executed while running with rpcpruneblockchain
.What does compacting the Urkel Tree mean?
Since the tree is append-only, when an existing name is updated (anything from a REVEAL to a REGISTER to an UPDATE) the old data still remains on disk. A new tree node is written and some file pointers are shuffled around to determine the new root hash of the tree (which is committed to in a block header).
If a chain reorg crosses back over a tree interval (36 blocks on mainnet) then an old tree root is pulled from the unzipped chain and used to revert the entire Urkel Tree to its old state, which is possible since all the historical data and pointers are still there on disk.
To compact the Urkel Tree means to take a given tree root and write a fresh Urkel Tree (in flat-file format on disk) containing ONLY the data connected directly to the root, then rename that directory to
~/.hsd/tree
, wiping out all the historical data that is no longer part of the current state.Strategy in this PR
Since compacting means the tree can no longer be reverted, it means that a chain reorg only one block deep that crosses a tree interval will permanently annihilate the full node. Therefore, what we must do is:
End result
Whether or not a full node is pruning the blockchain, executing this RPC will apply the same restriction at the moment it is completed: a 288-block reorg is the end of the game. Of course UNLIKE a pruning node, this compacting process is NOT on-going, so over time the Urkel Tree will bloat back up again (for better or for worse) until the RPC is called again.
Mainnet testing
I created a second branch of this PR with WIP rpc dumpzone applied. I have hsd synced to height 97555 already and started it in an isolated mode:
hsd --no-dns --no-wallet --only=127.0.0.1
Then executed
hsd-rpc dumpzone hns-97555-full.zone
to get a snapshot of the Urkel Tree state.Then executed
hsd-rpc compacttree
to run the pruning process.The size of the directory
~/.hsd/tree
dropped from 6.8 GB down to ~500 MB 🥳 and the process took about 2.5 minutes total 🎉Finally, I ran
hsd-rpc dumpzone hns-97555-compacted.zone
and compared the two output zone files which matched exactly 🚀TODO: