-
Notifications
You must be signed in to change notification settings - Fork 4
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
Composition over inheritance in probes #454
Conversation
c396496
to
4a4c94b
Compare
Co-authored-by: azawlocki <artur.zawlocki@golem.network>
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 job, our class diagram looks much nicer with these changes!
I've left some remarks and questions, I think those in goth/runner/probe/agent.py
need to be addressed before merging.
goth/runner/probe/__init__.py
Outdated
agents: Set[AgentComponent] | ||
"""Set of agent components that will be started as part of this probe.""" |
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 it were a list, not a set, we could guarantee that the agents are started in the order they appear on the list. Maybe this would be a useful feature? Just thinking aloud.
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 agree, this could be useful. Please see my comment on AgentComponent.__hash__
. We could replace this with an OrderedSet
(though that would require pulling in an additional dependency, i.e. https://pypi.org/project/ordered-set/) or an OrderedDict
.
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.
After your suggestion on AgentComponent.__hash__
I'm going to change this to a list.
goth/runner/__init__.py
Outdated
for probe in self.get_probes(probe_type=AgentMixin): | ||
awaitables.append(probe.start_agent()) | ||
for probe in self.probes: | ||
awaitables.extend([a.start() for a in probe.agents]) |
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.
Maybe it would be cleaner to have a start_agents()
method in Probe
:
awaitables = [probe.start_agents() for probe in self.probes]
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.
Added start_agents
method in 5a4a7b8
goth/runner/probe/agent.py
Outdated
log_config.base_dir = probe.container.log_config.base_dir | ||
|
||
self.log_monitor = LogEventMonitor(self.name, log_config) | ||
self._last_checked_line = -1 |
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.
We don't need this line anymore.
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.
Removed in b365b29
goth/runner/probe/agent.py
Outdated
self.log_monitor = LogEventMonitor(self.name, log_config) | ||
self._last_checked_line = -1 | ||
|
||
async def wait_for_log(self, pattern: str, timeout: float = 1000) -> LogEvent: |
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.
Use None
as default 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.
Updated in b365b29
goth/runner/probe/agent.py
Outdated
return entry | ||
|
||
def __hash__(self): | ||
return hash(self.name) |
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 seems dangerous, why not use default __hash__()
here which is based on object identity for custom classes
(https://docs.python.org/3/glossary.html#term-hashable)?
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, together with changing Probe.agents
from a list to a set, was done to prevent adding the same agent type (not necessarily the same class instance) twice to a single Probe
object (I assumed the same agent type would yield the same name).
We could replace this set + custom hash approach with a dict (that is: make Probe.agents
a dict in which the keys are names assigned to agents). My problem with this approach is that it makes iterating over agents unnecessarily complex. Also, I think it still makes sense to keep the name
property on agents, since it's used in AgentComponent
itself for creating the agent's log file.
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 forbid two agent components with the same type in a probe? I can imagine a test scenario with two requestor agents running in one node. Sure, they would have different names but the same type.
Moreover, to guarantee no two agent components have the same type I think it would be more natural to define __hash__()
based on the type, not only the name.
Finally, nothing prevents one from adding two agent components with the same type/name, one of them will be simply silently ignored (probably the second one, but I'm not sure). Maybe instead of overriding __hash__()
for AgentComponent
, Probe
should have a method like this:
def add_agent(agent: AgentComponent):
if any(a.name == agent.name for a in self.agents):
raise SomeError(f"The probe already has an agent component named `{agent.name}`")
....
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 for the suggestion, I like this idea. I'll add an add_agent
method to Probe
.
test/goth/runner/probe/test_agent.py
Outdated
|
||
|
||
def test_agent_duplicate_names(): | ||
"""Test if two agent objects with the same name will be treated as equal.""" |
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 need this property?
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.
Please see my comment on AgentComponent.__hash__
.
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!
cc98cc3
to
a9d0d89
Compare
Resolves: #433
Overview of changes!
Probe components
This PR introduces the concept of probe components (
ProbeComponent
), replacing certain functionalities previously realised through mixins (composition over inheritance). With these changes theProbe
class now includes two types of components:agents
, which are subclasses ofAgentComponent
. These replaceAgentMixin
and allow for having multiple agents running as part of a single probe. In the current implementationProviderProbe
uses this logic for itsProviderAgentComponent
.api
, which is of typeRestApiComponent
. Since this is now part of the baseProbe
class, both provider and requestor probes can now use the yagna REST APIs.Flattening probe hierarchy
With these changes there are now only two concrete probe classes:
ProviderProbe
andRequestorProbe
(so no moreRequestorProbeWithApiSteps
andProviderProbeWithLogSteps
). All high-level test step functions are now placed in domain-specific mixins, some of which are shared between the probe classes (seegoth/runner/probe/mixin.py
for more details).