-
Notifications
You must be signed in to change notification settings - Fork 269
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
feat: refactor the node key as version + local nonce(seq id)
#676
Conversation
keyformat/key_format.go
Outdated
@@ -105,6 +106,13 @@ func (kf *KeyFormat) Key(args ...interface{}) []byte { | |||
return kf.KeyBytes(segments...) | |||
} | |||
|
|||
func (kf *KeyFormat) NodeKey(nodeKey int64) []byte { |
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.
I can't find usages of this function anywhere, am I missing something?
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.
good catch
This maybe out of scope for this PR but I think we should change the key format for leaf nodes to be |
Shouldn't hardware prefetching mostly eliminate the tree traversal as a time overhead? |
Sounds like a very clever idea, but this may not serve what you want because you don't know what exact version the key is modified, unless also maintain some other indexes. |
But we always know the version since when we load iavl.Store, we always specify the version |
Tree nodes are scattered at different versions, prefetching don't help much I think. |
That is the version you want to query in, not the version the key is last modified, the version contained in the node is the version the node is created in, aka. when the key is modified or inserted. |
@yihuang is right, we have different versions, how is key-iteration query possible? since the version prefix is different |
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.
Logically things look OK to me. Maybe it's a big nit, but I wonder if this diff and implementation would be cleaner and more readable without the NodeKey
struct and the key just represented as []byte (same as it is now as hash).
To that I end I did a little refactoring PoC to see what it looks like. Let me know what you think, since you've spent time on this problem here than me.
https://github.com/cosmos/iavl/compare/592/refactor-nonce-new...kocubinski/592?expand=1
} | ||
} | ||
|
||
// MakeNode constructs an *Node from an encoded byte slice. | ||
// | ||
// The new node doesn't have its hash saved or set. The caller must set it | ||
// afterwards. | ||
func MakeNode(buf []byte) (*Node, error) { | ||
// Read node header (height, size, version, key). | ||
func MakeNode(nodeKey *NodeKey, buf []byte) (*Node, error) { |
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.
Do we really want/need an API breaking change here?
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.
I think this API is not exposed
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.
It is exported, see: https://go.dev/tour/basics/3
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.
absolutely, it is exported. I believe there is no usecase of this api outside.
if node.subtreeHeight == 0 { | ||
if bytes.Equal(node.key, key) { | ||
return node, nil | ||
} | ||
return node, errors.New("key does not exist") | ||
} | ||
|
||
nodeVersion := version |
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.
Why don't we just use the field version
on the Node
? There is no valid case where a node does not have a db key right?
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.
it is related to new design, we have new added nodes while uncommitting stage, and these nodes will have no nodekey and version, in that case, we should indicate the current version for those nodes.
docs/node/node.md
Outdated
|
||
The version of a node is the first version of the IAVL tree that the node gets added in. Future versions of the IAVL may point to this node if they also contain the node, however the node's version itself does not change. | ||
|
||
Size is the number of leaves under a given node. With a full subtree, `node.size = 2^(node.height)`. | ||
|
||
### Marshaling | ||
|
||
Every node is persisted by encoding the key, version, height, size and hash. If the node is a leaf node, then the value is persisted as well. If the node is not a leaf node, then the leftHash and rightHash are persisted as well. | ||
Every node is persisted by encoding the key, height, and size. If the node is a leaf node, then the value is persisted as well. If the node is not a leaf node, then the hash, leftNodeKey and rightNodeKey are persisted as well. |
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.
I think there should be some explanation of why hash is now written for inner nodes since this is a key departure from the previous design.
} | ||
cause = encoding.EncodeBytes(w, node.leftHash) | ||
cause = encoding.EncodeVarint(w, int64(node.leftNodeKey.nonce)) |
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.
If we're going to cast this to int64
anyway on write why not type it as int64?
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.
it only cares about how much is the number, not about the integer variable type in EncodeVarint
.
the result is same for 2(int64) and 2(int32), we will reduce the memory usage with int32 nonce
Here is the rocksDB benchmarking result, there is not much improvement in RocksDB
|
🎉 |
Since cosmos#676, nodes are indexed by an integer nonce instead of its hash
No description provided.