-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Improve readiness check for all services #2648
Conversation
Voight Kampff Integration Test Succeeded (Results) |
e7d1883
to
5261d01
Compare
Voight Kampff Integration Test Succeeded (Results) |
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.
On the surface, this seems like an unnecessarily elaborate status logging mechanism. Are there follow-up PRs that leverage the ProcessStatus class more than it is here? If we are going to implement a status service in the future, is this really necessary now? Would this even be needed if we had a status service?
I also wonder if it would be better to react to status change messages on the bus rather than making periodic checks for a particular status.
mycroft/audio/__main__.py
Outdated
@@ -45,6 +46,8 @@ def main(ready_hook=on_ready, error_hook=on_error, stopping_hook=on_stopping): | |||
check_for_signal("isSpeaking") | |||
bus = MessageBusClient() # Connect to the Mycroft Messagebus | |||
Configuration.set_config_update_handlers(bus) | |||
status = ProcessStatus('audio', bus, on_ready=ready_hook, |
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 am seeing this pattern of parameters passed a lot of places (ready_hook, error_hook, stopping_hook). Could be replaced by a dataclass/namedtuple/dictionary to simplify the parameterization.
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'm not sure that simplifies it, it just adds another layer.
Edit: And by that I mean that for that another object needs to parametrized and then passed to this object for initialization. Basically layering another object but still needing the same number of parameters somewhere. And since each project shall provide their own hook functions this still needs to be done at some point.
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.
Been trying it out a bit. Grouping the callback methods into a named tuple sort of makes sense and the complexity increase is quite low. I'll add that and we'll see what you think.
mycroft/util/process_utils.py
Outdated
def set_alive(self): | ||
"""Basic loading is done.""" | ||
self.state = ProcessState.ALIVE | ||
if self.on_alive: | ||
self.on_alive() | ||
|
||
def set_ready(self): | ||
"""All loading is done.""" | ||
self.state = ProcessState.READY | ||
if self.on_ready: | ||
self.on_ready() |
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 would like some more clarity on what is "alive" vs. "ready". What is difference between "basic loading" and all loading. what is an example of why we care about alive vs. ready?
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 will differ wildly between the different processes. In skills "basic loading" is defined as priority skills are loaded and the process has started it's main sequence but all skills aren't yet loaded.
In the audio service basic loading is when TTS has been loaded and mycroft can start talking, (servicing the priority skills for example), all loading is when the audioservices are loaded.
mycroft/util/process_utils.py
Outdated
self.bus.on('mycroft.{}.is_alive'.format(self.name), self.check_alive) | ||
self.bus.on('mycroft.{}.is_ready'.format(self.name), | ||
self.check_ready) | ||
self.bus.on('mycroft.{}.all_loaded'.format(self.name), | ||
self.check_ready) |
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 is difference between is_ready and all_loaded? Aren't "ready" and "all loaded" the same thing? Also, I don't see "all_loaded" emitted anywhere.
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.
.all_loaded
is a backwards compatibility message type (conforming to the call VK currently makes), in case someone / thing is already using it.
I'll add a comment about that.
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.
Forgot about this, but it's added now.
mycroft/util/process_utils.py
Outdated
@@ -121,3 +122,131 @@ def echo(message): | |||
# Listen for messages and echo them for logging | |||
LOG(name).info("BUS: {}".format(message)) | |||
return echo | |||
|
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.
Run this through black and pylint
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 has been pylinted and blacked (-s -l 79
)
def on_started(): | ||
LOG.info('Skills service is starting up.') | ||
|
||
|
||
def on_alive(): | ||
LOG.info('Skills service is alive.') | ||
|
||
|
||
def on_ready(): | ||
LOG.info('Skill service is ready.') | ||
LOG.info('Skills service is ready.') | ||
|
||
|
||
def on_error(e='Unknown'): | ||
LOG.info('Skill service failed to launch ({})'.format(repr(e))) | ||
LOG.info('Skills service failed to launch ({})'.format(repr(e))) | ||
|
||
|
||
def on_stopping(): | ||
LOG.info('Skill service is shutting down...') | ||
LOG.info('Skills service is shutting down...') |
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.
Couldn't these log messages be put into the ProcessStatus class and generalized so they can be written out for any service?
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.
They are basically there to be defaults for the status hooks arguments of the process's main function.
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 have defaults? Status hooks should be optional. If there is nothing to do when a paticular status is triggered, pass None to the hook and don't do anything. This is more meaningful that passing stubs all the time, IMO.
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 main reason was to have functions with the expected signature available to anyone overriding them so it's easy to see what's expected of the callbacks.
I have no strong opinion of this and can remove them if you'd prefer.
|
||
def _register_handlers(self): | ||
"""Register messagebus handlers for status queries.""" | ||
self.bus.on('mycroft.{}.is_alive'.format(self.name), self.check_alive) |
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 see this emitted anywhere.
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 this is used currently but it makes sure backwards compatibility is ensured with the current implementation of mycroft.skills.is_alive
we can at some point deprecate this is we it doesn't get used
"""Respond to all_loaded status request. | ||
|
||
Arguments: | ||
message: Optional message to respond to, if omitted no message |
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 see message passed anywhere.
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 is currently used by VK and by the mycroft-gui
|
||
|
||
def main(ready_hook=on_ready, error_hook=on_error, stopping_hook=on_stopping, | ||
watchdog=None): | ||
def main(alive_hook=on_alive, started_hook=on_started, ready_hook=on_ready, |
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 are the hooks passed as arguments like this? The hook functions exist in the same module, this seems extraneous.
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 hooks are meant to be overridden by someone implementing a Mycroft wrapping (for example a systemd service). See more info about the hooks in the PR that originally implemented them: #2601
These are just default values.
5261d01
to
3ebfeb7
Compare
Voight Kampff Integration Test Succeeded (Results) |
One use case for the hooks that exists today is systemd integration. (the default functions here are intended to be overridden). The reason for having defaults is basically to show them being used to developers doing this type of integration. |
3ebfeb7
to
48f70aa
Compare
Hello @krisgesling! Thanks for updating this PR. We checked the lines you've touched for PEP 8 issues, and found: There are currently no PEP 8 issues detected in this Pull Request. Cheers! 🍻 Comment last updated at 2021-01-28 16:29:33 UTC |
48f70aa
to
aa9b996
Compare
Voight Kampff Integration Test Failed (Results) |
aa9b996
to
6c60d22
Compare
Voight Kampff Integration Test Succeeded (Results) |
6c60d22
to
1efdaf1
Compare
Voight Kampff Integration Test Succeeded (Results) |
Voight Kampff Integration Test Failed (Results) |
1efdaf1
to
7328c62
Compare
Voight Kampff Integration Test Succeeded (Results) |
1 similar comment
Voight Kampff Integration Test Succeeded (Results) |
ec940f0
to
c98e8a0
Compare
Voight Kampff Integration Test Failed (Results) |
c98e8a0
to
241adfd
Compare
Voight Kampff Integration Test Succeeded (Results) |
Rebased the code and updated it for the new Padatious intent service. Tackled most of the concerns above. |
Hey, Chris and I spent some time today looking at what this might look like being pulled out as a new service independent of the the services that it's monitoring. Started a spec doc that would be great to get your thoughts on: @j1nx - I'm sure you'd have some great input too |
Hey @forslund, are you able to rebase this one? I think it's all good to merge! |
Sure thing, will rebase tonight |
ProcessStatus tracks the process status and allows callbacks on changes and status queries over the messagebus. StatusCallbackMap is used to setup the callbacks ProcessState is an enum tracking the different states.
Previously Mycroft reported mycroft.ready when Padatious had finished it's first training run. This check has been shifted to the enclosure service, and starts checking once the Padatious training is complete. Currently checks on readiness of audio, speech and skills services.
Change all strings referring to the service to use "Skills service"
241adfd
to
5df1524
Compare
Rebased |
Voight Kampff Integration Test Succeeded (Results) |
Description
Add a consistent interface to set and query the state of Mycroft Services improving the ability to check that Mycroft as a whole is ready for use. Full documentation at:
https://mycroft-ai.gitbook.io/docs/mycroft-technologies/mycroft-core#process-status-proposed
This adds a ProcessStatus object to key services providing a common interface for querying it's lifecycle status.
It also shifts the readiness check from the Padatious service to the Enclosure. Though is still triggered by the completion of the first Padatious training run.
Initial issue discussion at: #2639
Previous closed PR's - #2642, #2645
How to test
Run unit tests.
Alternatively break a service preventing it from loading and check that the
mycroft.ready
message is not emitted (also logs "Mycroft is all loaded and ready to roll!" to theenclosure.log
).Contributor license agreement signed?