Discussion:
[Twisted-Python] Is there a recommended way for a Service to request Application stop?
Daniel Sutcliffe
2016-10-10 16:41:59 UTC
Permalink
I have a hierarchy of Services some of which is MultiService and other
parts are my own implementations of IServiceCollection - in some
situations the a child Service may want to 'suggest' that the
Application's job is done (error, or simply task completed) and I'm
looking for some sort of standardized way to pass this info upstream.
The idea being that I may using my implemented Services in a variety
of Applications.

In this type of situation, is it the general intention a child Service
would use the Application directly, such that potential StopService()s
could bubble down? Or is there a normal pattern here to have messages
bubble up through the Services hierarchy? I'm not seeing anything like
this in the examples I've found or looking through the sources, but
I'm probably missing something.

Cheers
/dan
--
Daniel Sutcliffe <***@gmail.com>
Glyph Lefkowitz
2016-10-10 21:51:24 UTC
Permalink
Post by Daniel Sutcliffe
I have a hierarchy of Services some of which is MultiService and other
parts are my own implementations of IServiceCollection - in some
situations the a child Service may want to 'suggest' that the
Application's job is done (error, or simply task completed) and I'm
looking for some sort of standardized way to pass this info upstream.
The idea being that I may using my implemented Services in a variety
of Applications.
In this type of situation, is it the general intention a child Service
would use the Application directly, such that potential StopService()s
could bubble down? Or is there a normal pattern here to have messages
bubble up through the Services hierarchy? I'm not seeing anything like
this in the examples I've found or looking through the sources, but
I'm probably missing something.
Services are just things that can be started and stopped. Application is just a top-level object that associates a thing-to-start with a few bits of global process-level state, like logging and pidfile settings.

Therefore, the Service hierarchy abstraction is a poor fit for some code that needs to do some work and then exit; it's designed for long-running tools which can be started and stopped on demand. For example, what happens if two Service objects think that the Application's job is "done"?

If you want to exit a process, calling `stop` on the reactor is generally the right way to go.

But: talking about this in such vague, abstract terms is unlikely to be helpful. What, concretely, are you actually trying to do with the "Services in a variety of Applications"?

-glyph
Daniel Sutcliffe
2016-10-10 23:38:12 UTC
Permalink
Thanks for your feedback Glyph, responses embedded below.
Post by Daniel Sutcliffe
I have a hierarchy of Services some of which is MultiService and other
parts are my own implementations of IServiceCollection - in some
situations the child Service may want to 'suggest' that the
Application's job is done (error, or simply task completed) and I'm
looking for some sort of standardized way to pass this info upstream.
The idea being that I may using my implemented Services in a variety
of Applications.
In this type of situation, is it the general intention a child Service
would use the Application directly, such that potential StopService()s
could bubble down? Or is there a normal pattern here to have messages
bubble up through the Services hierarchy? I'm not seeing anything like
this in the examples I've found or looking through the sources, but
I'm probably missing something.
On Mon, Oct 10, 2016 at 5:51 PM, Glyph Lefkowitz
Post by Daniel Sutcliffe
Services are just things that can be started and stopped. Application is
just a top-level object that associates a thing-to-start with a few bits of
global process-level state, like logging and pidfile settings.
Therefore, the Service hierarchy abstraction is a poor fit for some code
that needs to do some work and then exit; it's designed for long-running
tools which can be started and stopped on demand. For example, what happens
if two Service objects think that the Application's job is "done"?
This is perhap the type of thing I'm trying to code for; my goal is
that the Services themselves do not have the final say in when the
Application (or parent Service) is done. The event a Service feels is
a problem may cause its parent to stop it, or it could simply call a
method on it to remediate - depending on situation.
Post by Daniel Sutcliffe
If you want to exit a process, calling `stop` on the reactor is generally
the right way to go.
ie. specifically I would not want a Service to stop the reactor, as
what it feels is a problem might only be a minor inconvenience for the
Application as a whole; but in another Application, or Application
state, it might be 'game over'.
Post by Daniel Sutcliffe
But: talking about this in such vague, abstract terms is unlikely to be
helpful. What, concretely, are you actually trying to do with the "Services
in a variety of Applications"?
I'll try to give one fairly concrete example of where I'd like to use
this kind of pattern.
The Services are long running polling ModBus clients whose configs are
read from a DB by the parent, the child Service has no knowledge of
where its config came from. Occasionally the child Services config may
become totally unworkable (for a variety of reasons) and so they want
to tell their parent the situation to give it a chance to reconfig,
call child.stopService(), or...The parent will have many such
Services, appropriate action may include a child reconfig, or if all
children showing issues telling its parent the situation to ask for
'advice'.

It seems to me in Twisted's Applications the job of calling
stopService() (or similar) should always be the responsibility of the
parent Service, but as a child how should I give my parent a clue I
need attention?

I can see implementing this with my own Interfaces so the child
Services know more about their parent's Interfaces/attributes, or
related objects, and can bubble information up through these, but I'd
prefer my child Services to know as little about their parents as
possible so they can be re-used in other simple Twisted apps.
Basically, I didn't want to go implementing stuff when there were
already tried and tested Twisted patterns for dealing with this kind
of thing.

Generically, can I somehow bubble up events through the Service
hierarchy, or should I communicate with external objects to the
hierarchy that can bubble down actions from higher up?

Any suggestions, or pointers to similar examples will be most
appreciated - I'm not stuck on this quandary, just wanting to write
code which fits best with the Twisted way of doing things.
Cheers
/dan
--
Daniel Sutcliffe <***@gmail.com>
Glyph Lefkowitz
2016-10-11 00:02:54 UTC
Permalink
Post by Daniel Sutcliffe
Generically, can I somehow bubble up events through the Service
hierarchy, or should I communicate with external objects to the
hierarchy that can bubble down actions from higher up?
Following the <https://en.wikipedia.org/wiki/Single_responsibility_principle <https://en.wikipedia.org/wiki/Single_responsibility_principle>>, the service hierarchy's job is just to make sure everything gets started up and shut down together.

It sounds to me like you have a pretty well-defined hierarchy which seems like it fits into the service hierarchy because it's roughly parallel in terms of which objects participate; however, you have very application-specific semantics for this parallel hierarchy. For example, it's pretty unusual to have a super-service reconfigure a subordinate service in order to recover from an error condition, in my experience, unless you're talking about stuff like erlang supervision hierarchies, but that requires runtime support like the code being recovered running in a subprocess that doesn't share state.

It often feels like abstractions are expensive so you should have as few of them as possible; but, in reality, *simple* abstractions are cheap, and what makes abstraction expensive is when you overload them. Make a new, simple abstraction that contains exactly the semantics you just described, and use composition to point at the appropriate point in the MultiService hierarchy. When it's time to "stop" a service, do setServiceParent(None); when it's time to "start" it, do setServiceParent(appropriateServiceParent). This should take care of keeping your services in the appropriate state.

BTW, if you have stateful long-running services that have to self-modify based on changing circumstances, you might want to also check out https://github.com/glyph/automat <https://github.com/glyph/automat> to see if it can help you ensure that everything's in a consistent state.

Good luck!

-glyph
Daniel Sutcliffe
2016-10-11 20:25:01 UTC
Permalink
Post by Daniel Sutcliffe
Generically, can I somehow bubble up events through the Service
hierarchy, or should I communicate with external objects to the
hierarchy that can bubble down actions from higher up?
On Mon, Oct 10, 2016 at 8:02 PM, Glyph Lefkowitz
Post by Daniel Sutcliffe
Following the
<https://en.wikipedia.org/wiki/Single_responsibility_principle>, the service
hierarchy's job is just to make sure everything gets started up and shut
down together.
It sounds to me like you have a pretty well-defined hierarchy which seems
like it fits into the service hierarchy because it's roughly parallel in
terms of which objects participate; however, you have very
application-specific semantics for this parallel hierarchy.
Maybe this was my conceptual issue; the parallels were close enough
that it just felt right to add the functionality to Twisted's Service
Hierarchy - close enough that I was sucked into thoughts of
modbusPollingClient IS-A Service... but keeping in mind the arguments
of composition vs inheritance, I somehow finished up with
modbusClientService HAS-A pollingLogic - whereas what I think you are
recommending here is that that pollingLogic HAS-A modbusClientService
and knows about a MultiService which it makes use of only to let the
Application have overall control over starting and stopping all the
active Services.

Actually I just read this back through and am not sure this is
actually really what you meant Glyph :-/
I do get that it seems I am grabbing the stick by the wrong end, I
guess I'll just go back to reading more example code to see if I can
find something that resonates with me as being close to what I'm
trying to achieve.
Post by Daniel Sutcliffe
[...]
When it's time to "stop" a service, do setServiceParent(None); when it's
time to "start" it, do setServiceParent(appropriateServiceParent). This
should take care of keeping your services in the appropriate state.
I have to admit I had not even thought of using setServiceParent(None)
to bring services down - looking at the source makes this concept
clearer and potentially useful to me though
Post by Daniel Sutcliffe
BTW, if you have stateful long-running services that have to self-modify
based on changing circumstances, you might want to also check out
https://github.com/glyph/automat to see if it can help you ensure that
everything's in a consistent state.
I'm still really just prototyping at this stage, trying to find an
architecture that fits what I believe should be quite a simple
application that is reasonably well suited to Twisted, but once I've
passed this stage if I do find myself needing a FSM that is beyond
totally simple then I'll certainly give this a look. Thanks.
/dan
--
Daniel Sutcliffe <***@gmail.com>
Continue reading on narkive:
Loading...