-
Notifications
You must be signed in to change notification settings - Fork 922
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
CHASM: tree node serialization/deserialization methods #7409
base: main
Are you sure you want to change the base?
Conversation
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.
Haven't review the serialize logic yet but I feel it's better to split it into a separate PR given how complex it is (we need to update tree structure as well). Let's focusing on getting the decoding part fully implemented?
79920a2
to
f727af1
Compare
Sorry, I did rebase and most of the comments are "Outdated" now. Which is actually true because I changed a lot. I addressed all comments though. |
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.
Would you mind give an overview of what are the main helper methods introduced by this PR and how they should be used by the closeTransaction() implementation?
// IsDirty returns true if any node rooted at Node n has been modified. | ||
// The result will be reset to false after a call to CloseTransaction(). | ||
func (n *Node) IsDirty() bool { | ||
panic("not implemented") | ||
return n.dirty |
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.
this needs to recursively check all child nodes. The dirty
flag is only for the current node, otherwise we can't compute mutations at the end of a transaction.
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.
Makes sense. Not part of this PR though. I am adding your comment as TODO there.
continue | ||
} | ||
|
||
// chasm.Field field must be a pointer, i.e. *chasm.Field[T]. |
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 don't think it has to be a pointer. As long as the registered component that's using chasm.Field is a pointer we should be good I think.
Is there a case you have in mind that doesn't work when Field is used without a pointer?
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 just don't want to support both cases and existing Field
constructors return *Field
. I agree that Field
look more natural in component definition than *Field
. I am flipping it to use Field
and forbid *Field
.
// TODO: error or just go around? | ||
// return serviceerror.NewInternal("unsupported field type in component " + fieldT.String()) |
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.
let's be strict in the beginning I'd say. It's always easier to relax a check than enforcing one.
} | ||
|
||
n.serializedValue.GetComponentAttributes().Data = blob | ||
n.serializedValue.GetComponentAttributes().Type = nodeValueT.Elem().String() // type name w/o * |
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.
This should be the registered component name, no go type name.
|
||
n.serializedValue.GetComponentAttributes().Data = blob | ||
n.serializedValue.GetComponentAttributes().Type = nodeValueT.Elem().String() // type name w/o * | ||
if n.serializedValue.GetInitialVersionedTransition() == nil { |
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.
initial versioned transition should only be set when the creating a new chasm node. It looks like you can do this as part of setValue()
, though I am not 100% sure when that method will be called.
Then LastUpdateVersionedTransition is updated every time the node is mutated. Guess you can do it here.
if err := childNode.deleteChildren(removedPaths, nil, append(currentPath, childName)); err != nil { | ||
return err | ||
} | ||
path, err := n.pathEncoder.Encode(n, append(currentPath, childName)) |
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.
path, err := n.pathEncoder.Encode(n, append(currentPath, childName)) | |
path, err := n.pathEncoder.Encode(childNode, append(currentPath, childName)) |
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.
🤦♂️
panic("not implemented") | ||
} | ||
} | ||
|
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.
What about newly added fields? when will we add them to the child map?
Is that logic reflectSubcomponents
or setValues()? I guess I am a bit confused where/when those two methods will be invoked.
|
||
type ( | ||
// TestComponent is a sample CHASM component used in tests. | ||
// It would be nice to move it another package, but this creates a circular dependency. |
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.
Could you move the tests that need these and these test components into a separate package (like chasm_test
)?
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 tried to do it. Unfortunately in this case I will need to export almost everything which I don't want to do. I hope later, I will figure out how to write better tests w/o accessing many unexported fields.
But I am 100% with you on this (registry_test.go
is in chasm_test
), and I will do it later.
return f.Name | ||
} | ||
|
||
func (n *Node) reflectSubcomponents() 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.
- (minor) How about,
updateNodeTree
orupdateSubcomponents
? - can we add a doc on what this method does? Maybe something like, "creates the internal node tree structure based on the root node's value?"
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.
Renamed to setChildNodes
(similar to setValue
). Added doc.
// - when a child is removed, all its children are removed too. | ||
// | ||
// Returns slice of paths to removed nodes. | ||
func (n *Node) syncChildren() ([]string, 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.
When is this called in a node's/component's lifecycle? Is it just within CloseTransaction
to get a list of removed paths, after an application deletes some of its nodes?
for i := 0; i < nodeValueT.Elem().NumField(); i++ { | ||
fieldV := nodeValueV.Elem().Field(i) | ||
fieldT := fieldV.Type() |
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.
Have you considered creating a Walk
method for walking a node's tree, to share this logic between reflectSubcomponents
/{de}serializeComponentNode
/syncChildrenInternal
?
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 haven't but I guess I should. I am trying to make it simple and dumb for now. Adding TODO for now.
What changed?
Added methods to serialized/deserialized CHASM components to its proto representation.
Added many TODOs to address later.
Why?
Part of CHASM worksream.
How did you test it?
Added unit tests.
Potential risks
No risks. Feature is still at the very early stage.
Documentation
I wish.
Is hotfix candidate?
No.