-
Notifications
You must be signed in to change notification settings - Fork 486
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
Allow other nodes than the component nodes to have dependants #6225
Conversation
I want to make sure I understand the refactoring being done here and how it relates to the new modules work. What is a high-level overview of the list of nodes we're planning to introduce, and what events will they emit that should cause other components to re-evaluate? (For example, components emit an event when they change their exported values, so components which rely on those exports should be updated) |
At this moment this is mostly a rename with future changes to allow config blocks/others to trigger evaluations? |
That's correct. Custom node components and import config nodes will implement this interface. |
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.
The code itself is fine, but I have a concern about the interface design and whether we can simplify it by rolling up extra responsibilities into BlockNode.
// NodeWithDependants must be implemented by BlockNode that can trigger other nodes to be evaluated. | ||
type NodeWithDependants interface { | ||
BlockNode | ||
|
||
// LastUpdateTime returns the time corresponding to the last time where the node changed its exports. | ||
LastUpdateTime() time.Time | ||
|
||
// Exports returns the current set of exports from the managed component. | ||
Exports() component.Exports | ||
|
||
// ID returns the component ID of the managed component from its River block. | ||
ID() ComponentID | ||
} |
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's a few issues with this interface:
-
The name is potentially misleading. The name implies it's about a characteristic of a particular node, but the description is more about whether a BlockNode can emit an event informing its dependents to be updated. In fact, from the perspective of the DAG, any node can have dependents regardless of whether it implements this interface.
-
The set of methods is perhaps too broad; it's not obvious why a node which can emit updates to cause other nodes to be evaluated needs to have component exports, or a component ID.
Because of the points above, I think it's hard to justify introducing this interface as a new concept to keep inside one's mental model of the controller code.
I would recommend that we aim to not introduce this interface, opting instead for the following:
- Move
LastUpdateTime()
to BlockNode instead. This would be a reasonable expectation of a BlockNode; as all BlockNodes support evaluating, it makes sense for them to also report their last update time. (Other methods should not be moved, just LastUpdateTime.) - Instead of changing code to accept
NodeWithDependents
, update it to accept aBlockNode
instead. You will need to add some type switching when a more concrete type is necessary.- This will also have the added benefit of making some of the changed code a bit shorter;
OnNodeWithDependantsUpdate
is a bit harder to read thanOnBlockNodeUpdate
.
- This will also have the added benefit of making some of the changed code a bit shorter;
WDYT?
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, I was not really happy with the name in the first place so I'm happy to change this :)
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 made the changes to see how it looks but after putting more thought into it I'm not 100% happy with it:
- For the component node, LastUpdateTime actually refers to the last time its exports were updated, which is not necessarily tied to when the node was updated or evaluated.
- In the Evaluate dependant I use a type cast to cache the exports because the BlockNode does not have the Export() function but I feel like this should be the default behavior. All BlockNodes can be evaluated which means that they have a concept of "Arguments" (inputs). I think that it actually makes sense that they also have a concept of "Exports" (outputs) and that these exports should be cached if not nil.
But for now the definitions of Arguments and Exports are really tied to components and not nodes so it would require bigger changes.
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.
For the component node, LastUpdateTime actually refers to the last time its exports were updated, which is not necessarily tied to when the node was updated or evaluated.
Ah, I see. Honestly, I don't think a node should have to track its own export time anyway; the controller could be tracking that when OnBlockNodeUpdate
is called. What do you think? Should that be scoped into this PR so we can take it out of the interface?
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.
Sounds good, the change is small and the time is only used by one metric. I think that it is worth doing it in the PR as it prevents introducing redundant code. I will take care of it
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, but there's an open question about whether LastUpdateTime should be removed from the interface entirely and move responsibility of update time tracking to the Loader.
Let me know if you want to make that change here or if we should merge this as-is.
…denciesWaitTime metric
@thampiotr I changed the way the LastUpdateTime is passed for the metric "agent_component_dependencies_wait_seconds". Since this was introduced by you some time ago you might want to have a look |
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 is fine too; though I find it very slightly unfortunate that the loader has to be aware of node queueing.
Another to handle this would be for the loader to maintain a map of node -> evaluated time, and for the loader to wrap the OnBlockNodeUpdate function. That way you don't have to introduce the QueueNode type for information only relevant to the loader and not the queue.
Let me know what you'd like to do (keep it as-is and merge or change it)
I thought about it but then the map would be accessed concurrently so it would need to be protected by a mutex and I was worried that it might slow down evaluation. I found that introducing the QueueNode type made things simpler |
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.
Thanks William! Will merge as soon as the CI goes green.
PR Description
Currently, only component nodes can trigger other nodes for re-evaluation at run time.
This PR introduces an interface to allow other nodes that implement it to have dependants.
For now, only the component nodes implement this interface but the new modules PR will introduce other types of nodes that will have dependants to send for re-evaluation.