-
Notifications
You must be signed in to change notification settings - Fork 91
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(service): Add notion of State
#4188
base: master
Are you sure you want to change the base?
Conversation
State
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's unfortunately quite boilerplate-heavy. Would be nicer with associated type defaults but they're unstable :/
But on the whole this makes a lot of sense to me. Some services already effectively use this pattern, this just codifies it.
/// A state interface for [services](`Service`). | ||
pub trait State {} |
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.
Is this trait intended to be expanded later? Currently I don't think this accomplishes anything.
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.
Nope, it was meant to be used as some sort of code documentation which clarifies when a type can be used as a state but it serves no purpose besides that. I don't know if it's common in Rust to do 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.
In that case I would just document the associated type directly.
Yes, unfortunately without that feature it's a bit boilerplate heavy. I considered requiring the |
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 me currently this seems like it adds more to the 'framework' which needs to be supported by each individual service, but does not add functionality to the framework which makes creating and managing services easier.
So it has a bit the opposite effect, it puts more burden on the implementation side, which is what we would ideally like to reduce. That's where the benefits come from (1 framework, but many services).
I fully agree. In the service rewrite I attempted, I implemented an abstraction similar to a framework. Unfortunately, it didn’t align well with our specific service usage so I discarded it. Rather than adding new functionality, this approach focuses on standardizing common service use cases through abstractions, nothing more. If you have any proposals to do this better, more than happy to start a discussion. |
I would only go through with this change if we can have sensible defaults, i.e.
Passing the shutdown handle into |
I can make all of such changes but the |
I don't see the upside of this at the moment. More abstractions should also take over logic/complexity of the individual services. For example, instead of passing down a shutdown handle (which could also just be another argument to the constructor), could the framework manage graceful shutdowns including dependencies. |
I explored this. The problem, which I might need help with is that we need to have all the code in a top-level select in the loop which polls from multiple futures and you can't fuse selects (from what I saw). Another mechanism would be to handle the shutdown by spawning a separate task for each service which is somehow bound to the joinhandle of the main task and kills it (if it's possible). |
There is always the option of not doing something if the alternative does not achieve what was hoped. |
Indeed, this is the reason why we are discussing it. |
This PR introduces the idea of
PublicState
to our services, adding the notion of state to them. Such a state is constructed before thespawn_handler
is called and returned with theAddr
of theService
. This abstraction aims to unify how services expose state (both readable and writable, depending on the implementation).In addition to the state, this PR adds:
shutdown
parameter in thespawn_handler
to make it more ergonomic to subscribe to shutdown signals in a service loop.channel()
method to create thetx
andrx
channels for service communication directly on the service itself.start_with_receiver()
method to handle the case in which a service has to be started with an injectedrx
channel due to circular dependencies between services.This PR is just the first small step towards improving our
Service
framework.Closes: #4180