Re: [Twisted-Python] What to do when a service fails to start, also, deferred and startService

Hi again Glyph
"glyph" == glyph glyph@divmod.com writes:
glyph> On 28 Nov, 03:38 pm, terry@jon.es wrote:
Thanks for the detailed reply.
glyph> No problem, sorry it took so long to get back to this; I definitely glyph> left the conversation halfway through.
And this followup is even slower, sorry. Here's a summary of the conversation I'd like to continue:
So in my case I want to indicate to twistd that the service that my class creates a makeService method to create, but which I do not set in motion, has failed to start and that twistd should exit, or do something other than cheerfully tell me that there's been an Unhandled Error.
Does that make more sense? Sorry, I should have said I was using twistd.
glyph> Yes. And I think that this is a good use-case for making IService a glyph> bit deeper than it is.
glyph> My previous messages were basically saying "you can't do what you glyph> want with IService". i.e. you can't write your own code to do glyph> something clever and somehow have dependencies and notifications to glyph> users of "twistd" fall out of that. But that shouldn't be taken as glyph> an indication that twistd itself shouldn't be improved.
glyph> It's not that you've failed to understand something - you have glyph> correctly identified that there is nothing to understand :). You glyph> need to go find that thing ("whatever it is" ;-)) in twistd that's glyph> invoking the very first call to IService.startService (and glyph> privilegedStartService as well) and propose a concrete way to make glyph> it smarter.
OK, here's a concrete proposal. Let's forget about twistd for the time being and just address the central things I'm trying to do:
1. I want a service to be able to initialize itself using code that receives deferreds. Right now, startService is not expected to return anything. I can go ahead and call a deferred-returning function in startService but I wont know that it's done unless I set a flag somewhere in a callback, and the rest of the methods in my service have to check that flag to see if the initialization is complete. In case it's not clear, I'm imagining the case of a MultiService, one Service of which performs the start and stop for the overall service.
2. Because the other Services in the MultiService depend on the successful execution of the initialization, I'd rather they didn't get started at all if the initialization fails.
3. It would be nice to get my hands on a deferred that had errbacked as a result of an unsuccessful start or stop.
You say: "making IService a bit deeper than it is" and then "you can't do what you want with IService".
And Esteve Fernandez has already pointed me to this:
"duuuuuude. don't. inherit. multiservice."
But it turns out you can do exactly what I want with a very simple subclass (or rewriting of) MultiService. BTW, this code is not tested - I want to float the idea before doing more. Here's the simple version:
class GuardedMultiService(service.MultiService): implements(service.IServiceCollection)
def __init__(self, initService): service.MultiService.__init__(self) self.initService = initService
def startService(self): d = defer.maybeDeferred(self.initService.startService) d.addCallback(lambda _: service.MultiService.startService)
def stopService(self): d = service.MultiService.stopService() d.addCallback(lambda _: self.initService.stopService) return d
This, I believe, solves 1 and 2 above. It has the nice property that all the regular services add to this class will not be started until after the possibly deferred-returning initService.startService is done. So there's no need for any checking later to see that you really are initialized.
And to solve 3, you can add (all defaulting to None) args to __init__ to specify two errback functions and their arguments, and use addErrback to attach these to the deferreds in startService and stopService. If we also make initService have a default of None, the result will be backward compatible with the existing MultiService, and could just replace it. The new version simply gives a way to deal with 1, 2 and 3, if you need it.
BTW, in your original reply you wrote
glyph> This might seem a bit inconsistent, since stopService uses the glyph> return of a Deferred. However, this is for a very specific reason, glyph> not a generalized error-handling case: you may need to prevent the glyph> *rest* of the system (specifically, the reactor) from completely glyph> shutting down
My #2 above is like the flip side of this: if the initialization of a service fails, you may need to prevent the *rest* of the system from coming up, and (if you can't fix things in the errback) you might want to stop the reactor. I say this specifically with regard to services launched by twistd.
Terry

On Sunday 22 February 2009 04:41:12 Terry Jones wrote:
And Esteve Fernandez has already pointed me to this:
"duuuuuude. don't. inherit. multiservice."
But I didn't say that, I was actually quoting this:
http://www.twistedmatrix.com/pipermail/twisted-python/2005-May/010400.html
Cheers.
participants (2)
-
Esteve Fernandez
-
Terry Jones